00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "heightmap.h"
00014 #include "clear_map.h"
00015 #include "void_map.h"
00016 #include "gui.h"
00017 #include "saveload/saveload.h"
00018 #include "bmp.h"
00019 #include "gfx_func.h"
00020 #include "fios.h"
00021 #include "fileio_func.h"
00022
00023 #include "table/strings.h"
00024
00029 static inline byte RGBToGrayscale(byte red, byte green, byte blue)
00030 {
00031
00032
00033 return ((red * 19595) + (green * 38470) + (blue * 7471)) / 65536;
00034 }
00035
00036
00037 #ifdef WITH_PNG
00038
00039 #include <png.h>
00040
00044 static void ReadHeightmapPNGImageData(byte *map, png_structp png_ptr, png_infop info_ptr)
00045 {
00046 uint x, y;
00047 byte gray_palette[256];
00048 png_bytep *row_pointers = NULL;
00049
00050
00051 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) {
00052 int i;
00053 int palette_size;
00054 png_color *palette;
00055 bool all_gray = true;
00056
00057 png_get_PLTE(png_ptr, info_ptr, &palette, &palette_size);
00058 for (i = 0; i < palette_size && (palette_size != 16 || all_gray); i++) {
00059 all_gray &= palette[i].red == palette[i].green && palette[i].red == palette[i].blue;
00060 gray_palette[i] = RGBToGrayscale(palette[i].red, palette[i].green, palette[i].blue);
00061 }
00062
00069 if (palette_size == 16 && !all_gray) {
00070 for (i = 0; i < palette_size; i++) {
00071 gray_palette[i] = 256 * i / palette_size;
00072 }
00073 }
00074 }
00075
00076 row_pointers = png_get_rows(png_ptr, info_ptr);
00077
00078
00079 for (x = 0; x < png_get_image_width(png_ptr, info_ptr); x++) {
00080 for (y = 0; y < png_get_image_height(png_ptr, info_ptr); y++) {
00081 byte *pixel = &map[y * png_get_image_width(png_ptr, info_ptr) + x];
00082 uint x_offset = x * png_get_channels(png_ptr, info_ptr);
00083
00084 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) {
00085 *pixel = gray_palette[row_pointers[y][x_offset]];
00086 } else if (png_get_channels(png_ptr, info_ptr) == 3) {
00087 *pixel = RGBToGrayscale(row_pointers[y][x_offset + 0],
00088 row_pointers[y][x_offset + 1], row_pointers[y][x_offset + 2]);
00089 } else {
00090 *pixel = row_pointers[y][x_offset];
00091 }
00092 }
00093 }
00094 }
00095
00101 static bool ReadHeightmapPNG(char *filename, uint *x, uint *y, byte **map)
00102 {
00103 FILE *fp;
00104 png_structp png_ptr = NULL;
00105 png_infop info_ptr = NULL;
00106
00107 fp = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR);
00108 if (fp == NULL) {
00109 ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_FILE_NOT_FOUND, WL_ERROR);
00110 return false;
00111 }
00112
00113 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00114 if (png_ptr == NULL) {
00115 ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_MISC, WL_ERROR);
00116 fclose(fp);
00117 return false;
00118 }
00119
00120 info_ptr = png_create_info_struct(png_ptr);
00121 if (info_ptr == NULL || setjmp(png_jmpbuf(png_ptr))) {
00122 ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_MISC, WL_ERROR);
00123 fclose(fp);
00124 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00125 return false;
00126 }
00127
00128 png_init_io(png_ptr, fp);
00129
00130
00131
00132 png_set_packing(png_ptr);
00133 png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_STRIP_16, NULL);
00134
00135
00136
00137 if ((png_get_channels(png_ptr, info_ptr) != 1) && (png_get_channels(png_ptr, info_ptr) != 3) && (png_get_bit_depth(png_ptr, info_ptr) != 8)) {
00138 ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_IMAGE_TYPE, WL_ERROR);
00139 fclose(fp);
00140 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00141 return false;
00142 }
00143
00144 uint width = png_get_image_width(png_ptr, info_ptr);
00145 uint height = png_get_image_height(png_ptr, info_ptr);
00146
00147
00148 if ((uint64)width * height >= (size_t)-1) {
00149 ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_HEIGHTMAP_TOO_LARGE, WL_ERROR);
00150 fclose(fp);
00151 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00152 return false;
00153 }
00154
00155 if (map != NULL) {
00156 *map = MallocT<byte>(width * height);
00157 ReadHeightmapPNGImageData(*map, png_ptr, info_ptr);
00158 }
00159
00160 *x = width;
00161 *y = height;
00162
00163 fclose(fp);
00164 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00165 return true;
00166 }
00167
00168 #endif
00169
00170
00174 static void ReadHeightmapBMPImageData(byte *map, BmpInfo *info, BmpData *data)
00175 {
00176 uint x, y;
00177 byte gray_palette[256];
00178
00179 if (data->palette != NULL) {
00180 uint i;
00181 bool all_gray = true;
00182
00183 if (info->palette_size != 2) {
00184 for (i = 0; i < info->palette_size && (info->palette_size != 16 || all_gray); i++) {
00185 all_gray &= data->palette[i].r == data->palette[i].g && data->palette[i].r == data->palette[i].b;
00186 gray_palette[i] = RGBToGrayscale(data->palette[i].r, data->palette[i].g, data->palette[i].b);
00187 }
00188
00195 if (info->palette_size == 16 && !all_gray) {
00196 for (i = 0; i < info->palette_size; i++) {
00197 gray_palette[i] = 256 * i / info->palette_size;
00198 }
00199 }
00200 } else {
00205 gray_palette[0] = 0;
00206 gray_palette[1] = 16;
00207 }
00208 }
00209
00210
00211 for (y = 0; y < info->height; y++) {
00212 byte *pixel = &map[y * info->width];
00213 byte *bitmap = &data->bitmap[y * info->width * (info->bpp == 24 ? 3 : 1)];
00214
00215 for (x = 0; x < info->width; x++) {
00216 if (info->bpp != 24) {
00217 *pixel++ = gray_palette[*bitmap++];
00218 } else {
00219 *pixel++ = RGBToGrayscale(*bitmap, *(bitmap + 1), *(bitmap + 2));
00220 bitmap += 3;
00221 }
00222 }
00223 }
00224 }
00225
00231 static bool ReadHeightmapBMP(char *filename, uint *x, uint *y, byte **map)
00232 {
00233 FILE *f;
00234 BmpInfo info;
00235 BmpData data;
00236 BmpBuffer buffer;
00237
00238
00239 memset(&data, 0, sizeof(data));
00240
00241 f = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR);
00242 if (f == NULL) {
00243 ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_PNGMAP_FILE_NOT_FOUND, WL_ERROR);
00244 return false;
00245 }
00246
00247 BmpInitializeBuffer(&buffer, f);
00248
00249 if (!BmpReadHeader(&buffer, &info, &data)) {
00250 ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_BMPMAP_IMAGE_TYPE, WL_ERROR);
00251 fclose(f);
00252 BmpDestroyData(&data);
00253 return false;
00254 }
00255
00256
00257 if ((uint64)info.width * info.height >= (size_t)-1 / (info.bpp == 24 ? 3 : 1)) {
00258 ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_HEIGHTMAP_TOO_LARGE, WL_ERROR);
00259 fclose(f);
00260 BmpDestroyData(&data);
00261 return false;
00262 }
00263
00264 if (map != NULL) {
00265 if (!BmpReadBitmap(&buffer, &info, &data)) {
00266 ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_BMPMAP_IMAGE_TYPE, WL_ERROR);
00267 fclose(f);
00268 BmpDestroyData(&data);
00269 return false;
00270 }
00271
00272 *map = MallocT<byte>(info.width * info.height);
00273 ReadHeightmapBMPImageData(*map, &info, &data);
00274 }
00275
00276 BmpDestroyData(&data);
00277
00278 *x = info.width;
00279 *y = info.height;
00280
00281 fclose(f);
00282 return true;
00283 }
00284
00292 static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map)
00293 {
00294
00295 const uint num_div = 16384;
00296
00297 uint width, height;
00298 uint row, col;
00299 uint row_pad = 0, col_pad = 0;
00300 uint img_scale;
00301 uint img_row, img_col;
00302 TileIndex tile;
00303
00304
00305 switch (_settings_game.game_creation.heightmap_rotation) {
00306 default: NOT_REACHED();
00307 case HM_COUNTER_CLOCKWISE:
00308 width = MapSizeX();
00309 height = MapSizeY();
00310 break;
00311 case HM_CLOCKWISE:
00312 width = MapSizeY();
00313 height = MapSizeX();
00314 break;
00315 }
00316
00317 if ((img_width * num_div) / img_height > ((width * num_div) / height)) {
00318
00319 img_scale = (width * num_div) / img_width;
00320 row_pad = (1 + height - ((img_height * img_scale) / num_div)) / 2;
00321 } else {
00322
00323 img_scale = (height * num_div) / img_height;
00324 col_pad = (1 + width - ((img_width * img_scale) / num_div)) / 2;
00325 }
00326
00327 if (_settings_game.construction.freeform_edges) {
00328 for (uint x = 0; x < MapSizeX(); x++) MakeVoid(TileXY(x, 0));
00329 for (uint y = 0; y < MapSizeY(); y++) MakeVoid(TileXY(0, y));
00330 }
00331
00332
00333 for (row = 0; row < height; row++) {
00334 for (col = 0; col < width; col++) {
00335 switch (_settings_game.game_creation.heightmap_rotation) {
00336 default: NOT_REACHED();
00337 case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break;
00338 case HM_CLOCKWISE: tile = TileXY(row, col); break;
00339 }
00340
00341
00342 if ((!_settings_game.construction.freeform_edges && DistanceFromEdge(tile) <= 1) ||
00343 (row < row_pad) || (row >= (height - row_pad - (_settings_game.construction.freeform_edges ? 0 : 1))) ||
00344 (col < col_pad) || (col >= (width - col_pad - (_settings_game.construction.freeform_edges ? 0 : 1)))) {
00345 SetTileHeight(tile, 0);
00346 } else {
00347
00348
00349 img_row = (((row - row_pad) * num_div) / img_scale);
00350 switch (_settings_game.game_creation.heightmap_rotation) {
00351 default: NOT_REACHED();
00352 case HM_COUNTER_CLOCKWISE:
00353 img_col = (((width - 1 - col - col_pad) * num_div) / img_scale);
00354 break;
00355 case HM_CLOCKWISE:
00356 img_col = (((col - col_pad) * num_div) / img_scale);
00357 break;
00358 }
00359
00360 assert(img_row < img_height);
00361 assert(img_col < img_width);
00362
00363
00364 SetTileHeight(tile, map[img_row * img_width + img_col] / 16);
00365 }
00366
00367 if (TileX(tile) != MapMaxX() && TileY(tile) != MapMaxY() &&
00368 (!_settings_game.construction.freeform_edges || (TileX(tile) != 0 && TileY(tile) != 0))) {
00369 MakeClear(tile, CLEAR_GRASS, 3);
00370 }
00371 }
00372 }
00373 }
00374
00379 void FixSlopes()
00380 {
00381 uint width, height;
00382 int row, col;
00383 byte current_tile;
00384
00385
00386 width = MapSizeX();
00387 height = MapSizeY();
00388
00389
00390 for (row = 0; (uint)row < height; row++) {
00391 for (col = 0; (uint)col < width; col++) {
00392 current_tile = MAX_TILE_HEIGHT;
00393 if (col != 0) {
00394
00395 current_tile = TileHeight(TileXY(col - 1, row));
00396 }
00397 if (row != 0) {
00398 if (TileHeight(TileXY(col, row - 1)) < current_tile) {
00399 current_tile = TileHeight(TileXY(col, row - 1));
00400 }
00401 }
00402
00403
00404 if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
00405
00406 SetTileHeight(TileXY(col, row), current_tile + 1);
00407 }
00408 }
00409 }
00410
00411
00412 for (row = height - 1; row >= 0; row--) {
00413 for (col = width - 1; col >= 0; col--) {
00414 current_tile = MAX_TILE_HEIGHT;
00415 if ((uint)col != width - 1) {
00416
00417 current_tile = TileHeight(TileXY(col + 1, row));
00418 }
00419
00420 if ((uint)row != height - 1) {
00421 if (TileHeight(TileXY(col, row + 1)) < current_tile) {
00422 current_tile = TileHeight(TileXY(col, row + 1));
00423 }
00424 }
00425
00426
00427 if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
00428
00429 SetTileHeight(TileXY(col, row), current_tile + 1);
00430 }
00431 }
00432 }
00433 }
00434
00438 static bool ReadHeightMap(char *filename, uint *x, uint *y, byte **map)
00439 {
00440 switch (_file_to_saveload.mode) {
00441 default: NOT_REACHED();
00442 #ifdef WITH_PNG
00443 case SL_PNG:
00444 return ReadHeightmapPNG(filename, x, y, map);
00445 #endif
00446 case SL_BMP:
00447 return ReadHeightmapBMP(filename, x, y, map);
00448 }
00449 }
00450
00458 bool GetHeightmapDimensions(char *filename, uint *x, uint *y)
00459 {
00460 return ReadHeightMap(filename, x, y, NULL);
00461 }
00462
00469 void LoadHeightmap(char *filename)
00470 {
00471 uint x, y;
00472 byte *map = NULL;
00473
00474 if (!ReadHeightMap(filename, &x, &y, &map)) {
00475 free(map);
00476 return;
00477 }
00478
00479 GrayscaleToMapHeights(x, y, map);
00480 free(map);
00481
00482 FixSlopes();
00483 MarkWholeScreenDirty();
00484 }
00485
00490 void FlatEmptyWorld(byte tile_height)
00491 {
00492 int edge_distance = _settings_game.construction.freeform_edges ? 0 : 2;
00493 for (uint row = edge_distance; row < MapSizeY() - edge_distance; row++) {
00494 for (uint col = edge_distance; col < MapSizeX() - edge_distance; col++) {
00495 SetTileHeight(TileXY(col, row), tile_height);
00496 }
00497 }
00498
00499 FixSlopes();
00500 MarkWholeScreenDirty();
00501 }