00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "variables.h"
00008 #include "heightmap.h"
00009 #include "clear_map.h"
00010 #include "void_map.h"
00011 #include "debug.h"
00012 #include "gui.h"
00013 #include "saveload.h"
00014 #include "bmp.h"
00015 #include "gfx_func.h"
00016 #include "core/alloc_func.hpp"
00017 #include "fios.h"
00018 #include "settings_type.h"
00019
00020 #include "table/strings.h"
00021
00027 static inline byte RGBToGrayscale(byte red, byte green, byte blue)
00028 {
00029
00030
00031 return ((red * 19595) + (green * 38470) + (blue * 7471)) / 65536;
00032 }
00033
00034
00035 #ifdef WITH_PNG
00036
00037 #include <png.h>
00038
00042 static void ReadHeightmapPNGImageData(byte *map, png_structp png_ptr, png_infop info_ptr)
00043 {
00044 uint x, y;
00045 byte gray_palette[256];
00046 png_bytep *row_pointers = NULL;
00047
00048
00049 if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
00050 int i;
00051 int palette_size;
00052 png_color *palette;
00053 bool all_gray = true;
00054
00055 png_get_PLTE(png_ptr, info_ptr, &palette, &palette_size);
00056 for (i = 0; i < palette_size && (palette_size != 16 || all_gray); i++) {
00057 all_gray &= palette[i].red == palette[i].green && palette[i].red == palette[i].blue;
00058 gray_palette[i] = RGBToGrayscale(palette[i].red, palette[i].green, palette[i].blue);
00059 }
00060
00067 if (palette_size == 16 && !all_gray) {
00068 for (i = 0; i < palette_size; i++) {
00069 gray_palette[i] = 256 * i / palette_size;
00070 }
00071 }
00072 }
00073
00074 row_pointers = png_get_rows(png_ptr, info_ptr);
00075
00076
00077 for (x = 0; x < info_ptr->width; x++) {
00078 for (y = 0; y < info_ptr->height; y++) {
00079 byte *pixel = &map[y * info_ptr->width + x];
00080 uint x_offset = x * info_ptr->channels;
00081
00082 if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
00083 *pixel = gray_palette[row_pointers[y][x_offset]];
00084 } else if (info_ptr->channels == 3) {
00085 *pixel = RGBToGrayscale(row_pointers[y][x_offset + 0],
00086 row_pointers[y][x_offset + 1], row_pointers[y][x_offset + 2]);
00087 } else {
00088 *pixel = row_pointers[y][x_offset];
00089 }
00090 }
00091 }
00092 }
00093
00099 static bool ReadHeightmapPNG(char *filename, uint *x, uint *y, byte **map)
00100 {
00101 FILE *fp;
00102 png_structp png_ptr = NULL;
00103 png_infop info_ptr = NULL;
00104
00105 fp = fopen(filename, "rb");
00106 if (fp == NULL) {
00107 ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_PNGMAP_ERROR, 0, 0);
00108 return false;
00109 }
00110
00111 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00112 if (png_ptr == NULL) {
00113 ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
00114 fclose(fp);
00115 return false;
00116 }
00117
00118 info_ptr = png_create_info_struct(png_ptr);
00119 if (info_ptr == NULL || setjmp(png_jmpbuf(png_ptr))) {
00120 ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
00121 fclose(fp);
00122 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00123 return false;
00124 }
00125
00126 png_init_io(png_ptr, fp);
00127
00128
00129
00130 png_set_packing(png_ptr);
00131 png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_STRIP_16, NULL);
00132
00133
00134
00135 if ((info_ptr->channels != 1) && (info_ptr->channels != 3) && (info_ptr->bit_depth != 8)) {
00136 ShowErrorMessage(STR_PNGMAP_ERR_IMAGE_TYPE, STR_PNGMAP_ERROR, 0, 0);
00137 fclose(fp);
00138 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00139 return false;
00140 }
00141
00142 if (map != NULL) {
00143 *map = MallocT<byte>(info_ptr->width * info_ptr->height);
00144
00145 if (*map == NULL) {
00146 ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
00147 fclose(fp);
00148 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00149 return false;
00150 }
00151
00152 ReadHeightmapPNGImageData(*map, png_ptr, info_ptr);
00153 }
00154
00155 *x = info_ptr->width;
00156 *y = info_ptr->height;
00157
00158 fclose(fp);
00159 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00160 return true;
00161 }
00162
00163 #endif
00164
00165
00169 static void ReadHeightmapBMPImageData(byte *map, BmpInfo *info, BmpData *data)
00170 {
00171 uint x, y;
00172 byte gray_palette[256];
00173
00174 if (data->palette != NULL) {
00175 uint i;
00176 bool all_gray = true;
00177
00178 if (info->palette_size != 2) {
00179 for (i = 0; i < info->palette_size && (info->palette_size != 16 || all_gray); i++) {
00180 all_gray &= data->palette[i].r == data->palette[i].g && data->palette[i].r == data->palette[i].b;
00181 gray_palette[i] = RGBToGrayscale(data->palette[i].r, data->palette[i].g, data->palette[i].b);
00182 }
00183
00190 if (info->palette_size == 16 && !all_gray) {
00191 for (i = 0; i < info->palette_size; i++) {
00192 gray_palette[i] = 256 * i / info->palette_size;
00193 }
00194 }
00195 } else {
00200 gray_palette[0] = 0;
00201 gray_palette[1] = 16;
00202 }
00203 }
00204
00205
00206 for (y = 0; y < info->height; y++) {
00207 byte *pixel = &map[y * info->width];
00208 byte *bitmap = &data->bitmap[y * info->width * (info->bpp == 24 ? 3 : 1)];
00209
00210 for (x = 0; x < info->width; x++) {
00211 if (info->bpp != 24) {
00212 *pixel++ = gray_palette[*bitmap++];
00213 } else {
00214 *pixel++ = RGBToGrayscale(*bitmap, *(bitmap + 1), *(bitmap + 2));
00215 bitmap += 3;
00216 }
00217 }
00218 }
00219 }
00220
00226 static bool ReadHeightmapBMP(char *filename, uint *x, uint *y, byte **map)
00227 {
00228 FILE *f;
00229 BmpInfo info;
00230 BmpData data;
00231 BmpBuffer buffer;
00232
00233
00234 memset(&data, 0, sizeof(data));
00235
00236 f = fopen(filename, "rb");
00237 if (f == NULL) {
00238 ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_BMPMAP_ERROR, 0, 0);
00239 return false;
00240 }
00241
00242 BmpInitializeBuffer(&buffer, f);
00243
00244 if (!BmpReadHeader(&buffer, &info, &data)) {
00245 ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
00246 fclose(f);
00247 BmpDestroyData(&data);
00248 return false;
00249 }
00250
00251 if (map != NULL) {
00252 if (!BmpReadBitmap(&buffer, &info, &data)) {
00253 ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
00254 fclose(f);
00255 BmpDestroyData(&data);
00256 return false;
00257 }
00258
00259 *map = MallocT<byte>(info.width * info.height);
00260 if (*map == NULL) {
00261 ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_BMPMAP_ERROR, 0, 0);
00262 fclose(f);
00263 BmpDestroyData(&data);
00264 return false;
00265 }
00266
00267 ReadHeightmapBMPImageData(*map, &info, &data);
00268
00269 }
00270
00271 BmpDestroyData(&data);
00272
00273 *x = info.width;
00274 *y = info.height;
00275
00276 fclose(f);
00277 return true;
00278 }
00279
00287 static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map)
00288 {
00289
00290 const uint num_div = 16384;
00291
00292 uint width, height;
00293 uint row, col;
00294 uint row_pad = 0, col_pad = 0;
00295 uint img_scale;
00296 uint img_row, img_col;
00297 TileIndex tile;
00298
00299
00300 switch (_patches.heightmap_rotation) {
00301 default: NOT_REACHED();
00302 case HM_COUNTER_CLOCKWISE:
00303 width = MapSizeX();
00304 height = MapSizeY();
00305 break;
00306 case HM_CLOCKWISE:
00307 width = MapSizeY();
00308 height = MapSizeX();
00309 break;
00310 }
00311
00312 if ((img_width * num_div) / img_height > ((width * num_div) / height)) {
00313
00314 img_scale = (width * num_div) / img_width;
00315 row_pad = (1 + height - ((img_height * img_scale) / num_div)) / 2;
00316 } else {
00317
00318 img_scale = (height * num_div) / img_height;
00319 col_pad = (1 + width - ((img_width * img_scale) / num_div)) / 2;
00320 }
00321
00322
00323 for (row = 0; row < height - 1; row++) {
00324 for (col = 0; col < width - 1; col++) {
00325 switch (_patches.heightmap_rotation) {
00326 default: NOT_REACHED();
00327 case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break;
00328 case HM_CLOCKWISE: tile = TileXY(row, col); break;
00329 }
00330
00331
00332 if ((DistanceFromEdge(tile) <= 1) ||
00333 (row < row_pad) || (row >= (height - row_pad - 1)) ||
00334 (col < col_pad) || (col >= (width - col_pad - 1))) {
00335 SetTileHeight(tile, 0);
00336 } else {
00337
00338
00339 img_row = (((row - row_pad) * num_div) / img_scale);
00340 switch (_patches.heightmap_rotation) {
00341 default: NOT_REACHED();
00342 case HM_COUNTER_CLOCKWISE:
00343 img_col = (((width - 1 - col - col_pad) * num_div) / img_scale);
00344 break;
00345 case HM_CLOCKWISE:
00346 img_col = (((col - col_pad) * num_div) / img_scale);
00347 break;
00348 }
00349
00350 assert(img_row < img_height);
00351 assert(img_col < img_width);
00352
00353
00354 SetTileHeight(tile, map[img_row * img_width + img_col] / 16);
00355 }
00356 MakeClear(tile, CLEAR_GRASS, 3);
00357 }
00358 }
00359 }
00360
00365 static void FixSlopes()
00366 {
00367 uint width, height;
00368 uint row, col;
00369 byte current_tile;
00370
00371
00372 width = MapSizeX();
00373 height = MapSizeY();
00374
00375
00376 for (row = 1; row < height - 2; row++) {
00377 for (col = 1; col < width - 2; col++) {
00378
00379 current_tile = TileHeight(TileXY(col - 1, row));
00380 if (TileHeight(TileXY(col, row - 1)) < current_tile) {
00381 current_tile = TileHeight(TileXY(col, row - 1));
00382 }
00383
00384
00385 if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
00386
00387 SetTileHeight(TileXY(col, row), current_tile + 1);
00388 }
00389 }
00390 }
00391
00392
00393 for (row = height - 2; row > 0; row--) {
00394 for (col = width - 2; col > 0; col--) {
00395
00396 current_tile = TileHeight(TileXY(col + 1, row));
00397 if (TileHeight(TileXY(col, row + 1)) < current_tile) {
00398 current_tile = TileHeight(TileXY(col, row + 1));
00399 }
00400
00401
00402 if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
00403
00404 SetTileHeight(TileXY(col, row), current_tile + 1);
00405 }
00406 }
00407 }
00408 }
00409
00413 static bool ReadHeightMap(char *filename, uint *x, uint *y, byte **map)
00414 {
00415 switch (_file_to_saveload.mode) {
00416 default: NOT_REACHED();
00417 #ifdef WITH_PNG
00418 case SL_PNG:
00419 return ReadHeightmapPNG(filename, x, y, map);
00420 #endif
00421 case SL_BMP:
00422 return ReadHeightmapBMP(filename, x, y, map);
00423 }
00424 }
00425
00426 bool GetHeightmapDimensions(char *filename, uint *x, uint *y)
00427 {
00428 return ReadHeightMap(filename, x, y, NULL);
00429 }
00430
00431 void LoadHeightmap(char *filename)
00432 {
00433 uint x, y;
00434 byte *map = NULL;
00435
00436 if (!ReadHeightMap(filename, &x, &y, &map)) {
00437 free(map);
00438 return;
00439 }
00440
00441 GrayscaleToMapHeights(x, y, map);
00442 free(map);
00443
00444 FixSlopes();
00445 MarkWholeScreenDirty();
00446 }
00447
00448 void FlatEmptyWorld(byte tile_height)
00449 {
00450 uint width, height;
00451 uint row, col;
00452
00453 width = MapSizeX();
00454 height = MapSizeY();
00455
00456 for (row = 2; row < height - 2; row++) {
00457 for (col = 2; col < width - 2; col++) {
00458 SetTileHeight(TileXY(col, row), tile_height);
00459 }
00460 }
00461
00462 FixSlopes();
00463 MarkWholeScreenDirty();
00464 }