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