src/heightmap.c
branchcustombridgeheads
changeset 5649 55c8267c933f
parent 5648 1608018c5ff2
child 5650 aefc131bf5ce
equal deleted inserted replaced
5648:1608018c5ff2 5649:55c8267c933f
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "variables.h"
       
     6 #include "functions.h"
       
     7 #include "heightmap.h"
       
     8 #include "clear_map.h"
       
     9 #include "table/strings.h"
       
    10 #include "void_map.h"
       
    11 #include "debug.h"
       
    12 #include "gfx.h"
       
    13 #include "gui.h"
       
    14 #include "saveload.h"
       
    15 #include "bmp.h"
       
    16 
       
    17 /**
       
    18  * Convert RGB colors to Grayscale using 29.9% Red, 58.7% Green, 11.4% Blue
       
    19  *  (average luminosity formula) -- Dalestan
       
    20  * This in fact is the NTSC Color Space -- TrueLight
       
    21  */
       
    22 static inline byte RGBToGrayscale(byte red, byte green, byte blue)
       
    23 {
       
    24 	/* To avoid doubles and stuff, multiple it with a total of 65536 (16bits), then
       
    25 	 *  divide by it to normalize the value to a byte again. */
       
    26 	return ((red * 19595) + (green * 38470) + (blue * 7471)) / 65536;
       
    27 }
       
    28 
       
    29 
       
    30 #ifdef WITH_PNG
       
    31 
       
    32 #include "png.h"
       
    33 
       
    34 /**
       
    35  * The PNG Heightmap loader.
       
    36  */
       
    37 static void ReadHeightmapPNGImageData(byte *map, png_structp png_ptr, png_infop info_ptr)
       
    38 {
       
    39 	uint x, y;
       
    40 	byte gray_palette[256];
       
    41 	png_bytep *row_pointers = NULL;
       
    42 
       
    43 	/* Get palette and convert it to grayscale */
       
    44 	if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
       
    45 		int i;
       
    46 		int palette_size;
       
    47 		png_color *palette;
       
    48 		bool all_gray = true;
       
    49 
       
    50 		png_get_PLTE(png_ptr, info_ptr, &palette, &palette_size);
       
    51 		for (i = 0; i < palette_size && (palette_size != 16 || all_gray); i++) {
       
    52 			all_gray &= palette[i].red == palette[i].green && palette[i].red == palette[i].blue;
       
    53 			gray_palette[i] = RGBToGrayscale(palette[i].red, palette[i].green, palette[i].blue);
       
    54 		}
       
    55 
       
    56 		/**
       
    57 		 * For a non-gray palette of size 16 we assume that
       
    58 		 * the order of the palette determines the height;
       
    59 		 * the first entry is the sea (level 0), the second one
       
    60 		 * level 1, etc.
       
    61 		 */
       
    62 		if (palette_size == 16 && !all_gray) {
       
    63 			for (i = 0; i < palette_size; i++) {
       
    64 				gray_palette[i] = 256 * i / palette_size;
       
    65 			}
       
    66 		}
       
    67 	}
       
    68 
       
    69 	row_pointers = png_get_rows(png_ptr, info_ptr);
       
    70 
       
    71 	/* Read the raw image data and convert in 8-bit grayscale */
       
    72 	for (x = 0; x < info_ptr->width; x++) {
       
    73 		for (y = 0; y < info_ptr->height; y++) {
       
    74 			byte *pixel = &map[y * info_ptr->width + x];
       
    75 			uint x_offset = x * info_ptr->channels;
       
    76 
       
    77 			if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
       
    78 				*pixel = gray_palette[row_pointers[y][x_offset]];
       
    79 			} else if (info_ptr->channels == 3) {
       
    80 				*pixel = RGBToGrayscale(row_pointers[y][x_offset + 0],
       
    81 						row_pointers[y][x_offset + 1], row_pointers[y][x_offset + 2]);
       
    82 			} else {
       
    83 				*pixel = row_pointers[y][x_offset];
       
    84 			}
       
    85 		}
       
    86 	}
       
    87 }
       
    88 
       
    89 /**
       
    90  * Reads the heightmap and/or size of the heightmap from a PNG file.
       
    91  * If map == NULL only the size of the PNG is read, otherwise a map
       
    92  * with grayscale pixels is allocated and assigned to *map.
       
    93  */
       
    94 static bool ReadHeightmapPNG(char *filename, uint *x, uint *y, byte **map)
       
    95 {
       
    96 	FILE *fp;
       
    97 	png_structp png_ptr = NULL;
       
    98 	png_infop info_ptr  = NULL;
       
    99 
       
   100 	fp = fopen(filename, "rb");
       
   101 	if (fp == NULL) {
       
   102 		ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_PNGMAP_ERROR, 0, 0);
       
   103 		return false;
       
   104 	}
       
   105 
       
   106 	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
       
   107 	if (png_ptr == NULL) {
       
   108 		ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
       
   109 		fclose(fp);
       
   110 		return false;
       
   111 	}
       
   112 
       
   113 	info_ptr = png_create_info_struct(png_ptr);
       
   114 	if (info_ptr == NULL || setjmp(png_jmpbuf(png_ptr))) {
       
   115 		ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
       
   116 		fclose(fp);
       
   117 		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
       
   118 		return false;
       
   119 	}
       
   120 
       
   121 	png_init_io(png_ptr, fp);
       
   122 
       
   123 	/* Allocate memory and read image, without alpha or 16-bit samples
       
   124 	 * (result is either 8-bit indexed/grayscale or 24-bit RGB) */
       
   125 	png_set_packing(png_ptr);
       
   126 	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_STRIP_16, NULL);
       
   127 
       
   128 	/* Maps of wrong color-depth are not used.
       
   129 	 * (this should have been taken care of by stripping alpha and 16-bit samples on load) */
       
   130 	if ((info_ptr->channels != 1) && (info_ptr->channels != 3) && (info_ptr->bit_depth != 8)) {
       
   131 		ShowErrorMessage(STR_PNGMAP_ERR_IMAGE_TYPE, STR_PNGMAP_ERROR, 0, 0);
       
   132 		fclose(fp);
       
   133 		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
       
   134 		return false;
       
   135 	}
       
   136 
       
   137 	if (map != NULL) {
       
   138 		*map = malloc(info_ptr->width * info_ptr->height * sizeof(byte));
       
   139 
       
   140 		if (*map == NULL) {
       
   141 			ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_PNGMAP_ERROR, 0, 0);
       
   142 			fclose(fp);
       
   143 			png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
       
   144 			return false;
       
   145 		}
       
   146 
       
   147 		ReadHeightmapPNGImageData(*map, png_ptr, info_ptr);
       
   148 	}
       
   149 
       
   150 	*x = info_ptr->width;
       
   151 	*y = info_ptr->height;
       
   152 
       
   153 	fclose(fp);
       
   154 	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
       
   155 	return true;
       
   156 }
       
   157 
       
   158 #endif /* WITH_PNG */
       
   159 
       
   160 
       
   161 /**
       
   162  * The BMP Heightmap loader.
       
   163  */
       
   164 static void ReadHeightmapBMPImageData(byte *map, BmpInfo *info, BmpData *data)
       
   165 {
       
   166 	uint x, y;
       
   167 	byte gray_palette[256];
       
   168 
       
   169 	if (data->palette != NULL) {
       
   170 		uint i;
       
   171 		bool all_gray = true;
       
   172 
       
   173 		if (info->palette_size != 2) {
       
   174 			for (i = 0; i < info->palette_size && (info->palette_size != 16 || all_gray); i++) {
       
   175 				all_gray &= data->palette[i].r == data->palette[i].g && data->palette[i].r == data->palette[i].b;
       
   176 				gray_palette[i] = RGBToGrayscale(data->palette[i].r, data->palette[i].g, data->palette[i].b);
       
   177 			}
       
   178 
       
   179 			/**
       
   180 			 * For a non-gray palette of size 16 we assume that
       
   181 			 * the order of the palette determines the height;
       
   182 			 * the first entry is the sea (level 0), the second one
       
   183 			 * level 1, etc.
       
   184 			 */
       
   185 			if (info->palette_size == 16 && !all_gray) {
       
   186 				for (i = 0; i < info->palette_size; i++) {
       
   187 					gray_palette[i] = 256 * i / info->palette_size;
       
   188 				}
       
   189 			}
       
   190 		} else {
       
   191 			/**
       
   192 			 * For a palette of size 2 we assume that the order of the palette determines the height;
       
   193 			 * the first entry is the sea (level 0), the second one is the land (level 1)
       
   194 			 */
       
   195 			gray_palette[0] = 0;
       
   196 			gray_palette[1] = 16;
       
   197 		}
       
   198 	}
       
   199 
       
   200 	/* Read the raw image data and convert in 8-bit grayscale */
       
   201 	for (y = 0; y < info->height; y++) {
       
   202 		byte *pixel = &map[y * info->width];
       
   203 		byte *bitmap = &data->bitmap[y * info->width * (info->bpp == 24 ? 3 : 1)];
       
   204 
       
   205 		for (x = 0; x < info->width; x++) {
       
   206 			if (info->bpp != 24) {
       
   207 				*pixel++ = gray_palette[*bitmap++];
       
   208 			} else {
       
   209 				*pixel++ = RGBToGrayscale(*bitmap, *(bitmap + 1), *(bitmap + 2));
       
   210 				bitmap += 3;
       
   211 			}
       
   212 		}
       
   213 	}
       
   214 }
       
   215 
       
   216 /**
       
   217  * Reads the heightmap and/or size of the heightmap from a BMP file.
       
   218  * If map == NULL only the size of the BMP is read, otherwise a map
       
   219  * with grayscale pixels is allocated and assigned to *map.
       
   220  */
       
   221 static bool ReadHeightmapBMP(char *filename, uint *x, uint *y, byte **map)
       
   222 {
       
   223 	FILE *f;
       
   224 	BmpInfo info;
       
   225 	BmpData data;
       
   226 	BmpBuffer buffer;
       
   227 
       
   228 	f = fopen(filename, "rb");
       
   229 	if (f == NULL) {
       
   230 		ShowErrorMessage(STR_PNGMAP_ERR_FILE_NOT_FOUND, STR_BMPMAP_ERROR, 0, 0);
       
   231 		return false;
       
   232 	}
       
   233 
       
   234 	BmpInitializeBuffer(&buffer, f);
       
   235 
       
   236 	if (!BmpReadHeader(&buffer, &info, &data)) {
       
   237 		ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
       
   238 		fclose(f);
       
   239 		BmpDestroyData(&data);
       
   240 		return false;
       
   241 	}
       
   242 
       
   243 	if (map != NULL) {
       
   244 		if (!BmpReadBitmap(&buffer, &info, &data)) {
       
   245 			ShowErrorMessage(STR_BMPMAP_ERR_IMAGE_TYPE, STR_BMPMAP_ERROR, 0, 0);
       
   246 			fclose(f);
       
   247 			BmpDestroyData(&data);
       
   248 			return false;
       
   249 		}
       
   250 
       
   251 		*map = malloc(info.width * info.height * sizeof(byte));
       
   252 		if (*map == NULL) {
       
   253 			ShowErrorMessage(STR_PNGMAP_ERR_MISC, STR_BMPMAP_ERROR, 0, 0);
       
   254 			fclose(f);
       
   255 			BmpDestroyData(&data);
       
   256 			return false;
       
   257 		}
       
   258 
       
   259 		ReadHeightmapBMPImageData(*map, &info, &data);
       
   260 
       
   261 	}
       
   262 
       
   263 	BmpDestroyData(&data);
       
   264 
       
   265 	*x = info.width;
       
   266 	*y = info.height;
       
   267 
       
   268 	fclose(f);
       
   269 	return true;
       
   270 }
       
   271 
       
   272 static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map)
       
   273 {
       
   274 	/* Defines the detail of the aspect ratio (to avoid doubles) */
       
   275 	const uint num_div = 16384;
       
   276 
       
   277 	uint width, height;
       
   278 	uint row, col;
       
   279 	uint row_pad = 0, col_pad = 0;
       
   280 	uint img_scale;
       
   281 	uint img_row, img_col;
       
   282 	TileIndex tile;
       
   283 
       
   284 	/* Get map size and calculate scale and padding values */
       
   285 	switch (_patches.heightmap_rotation) {
       
   286 	case HM_COUNTER_CLOCKWISE:
       
   287 		width   = MapSizeX();
       
   288 		height  = MapSizeY();
       
   289 		break;
       
   290 	case HM_CLOCKWISE:
       
   291 		width   = MapSizeY();
       
   292 		height  = MapSizeX();
       
   293 		break;
       
   294 	default:
       
   295 		NOT_REACHED();
       
   296 		/* Avoids compiler warnings */
       
   297 		return;
       
   298 	}
       
   299 
       
   300 	if ((img_width * num_div) / img_height > ((width * num_div) / height)) {
       
   301 		/* Image is wider than map - center vertically */
       
   302 		img_scale = (width * num_div) / img_width;
       
   303 		row_pad = (height - ((img_height * img_scale) / num_div)) / 2;
       
   304 	} else {
       
   305 		/* Image is taller than map - center horizontally */
       
   306 		img_scale = (height * num_div) / img_height;
       
   307 		col_pad = (width - ((img_width * img_scale) / num_div)) / 2;
       
   308 	}
       
   309 
       
   310 	/* Form the landscape */
       
   311 	for (row = 0; row < height - 1; row++) {
       
   312 		for (col = 0; col < width - 1; col++) {
       
   313 			switch (_patches.heightmap_rotation) {
       
   314 			case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break;
       
   315 			case HM_CLOCKWISE:         tile = TileXY(row, col); break;
       
   316 			default:                   NOT_REACHED(); return;
       
   317 			}
       
   318 
       
   319 			/* Check if current tile is within the 1-pixel map edge or padding regions */
       
   320 			if ((DistanceFromEdge(tile) <= 1) ||
       
   321 					(row < row_pad) || (row >= (img_height + row_pad)) ||
       
   322 					(col < col_pad) || (col >= (img_width  + col_pad))) {
       
   323 				SetTileHeight(tile, 0);
       
   324 			} else {
       
   325 				/* Use nearest neighbor resizing to scale map data.
       
   326 				 *  We rotate the map 45 degrees (counter)clockwise */
       
   327 				img_row = (((row - row_pad) * num_div) / img_scale);
       
   328 				switch (_patches.heightmap_rotation) {
       
   329 				case HM_COUNTER_CLOCKWISE:
       
   330 					img_col = (((width - 1 - col - col_pad) * num_div) / img_scale);
       
   331 					break;
       
   332 				case HM_CLOCKWISE:
       
   333 					img_col = (((col - col_pad) * num_div) / img_scale);
       
   334 					break;
       
   335 				default:
       
   336 					NOT_REACHED();
       
   337 					/* Avoids compiler warnings */
       
   338 					return;
       
   339 				}
       
   340 
       
   341 				assert(img_row < img_height);
       
   342 				assert(img_col < img_width);
       
   343 
       
   344 				/* Color scales from 0 to 255, OpenTTD height scales from 0 to 15 */
       
   345 				SetTileHeight(tile, map[img_row * img_width + img_col] / 16);
       
   346 			}
       
   347 			MakeClear(tile, CLEAR_GRASS, 3);
       
   348 		}
       
   349 	}
       
   350 }
       
   351 
       
   352 /**
       
   353  * This function takes care of the fact that land in OpenTTD can never differ
       
   354  * more than 1 in height
       
   355  */
       
   356 static void FixSlopes(void)
       
   357 {
       
   358 	uint width, height;
       
   359 	uint row, col;
       
   360 	byte current_tile;
       
   361 
       
   362 	/* Adjust height difference to maximum one horizontal/vertical change. */
       
   363 	width   = MapSizeX();
       
   364 	height  = MapSizeY();
       
   365 
       
   366 	/* Top and left edge */
       
   367 	for (row = 1; row < height - 2; row++) {
       
   368 		for (col = 1; col < width - 2; col++) {
       
   369 			/* Find lowest tile; either the top or left one */
       
   370 			current_tile = TileHeight(TileXY(col - 1, row)); // top edge
       
   371 			if (TileHeight(TileXY(col, row - 1)) < current_tile) {
       
   372 				current_tile = TileHeight(TileXY(col, row - 1)); // left edge
       
   373 			}
       
   374 
       
   375 			/* Does the height differ more than one? */
       
   376 			if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
       
   377 				/* Then change the height to be no more than one */
       
   378 				SetTileHeight(TileXY(col, row), current_tile + 1);
       
   379 			}
       
   380 		}
       
   381 	}
       
   382 
       
   383 	/* Bottom and right edge */
       
   384 	for (row = height - 2; row > 0; row--) {
       
   385 		for (col = width - 2; col > 0; col--) {
       
   386 			/* Find lowest tile; either the bottom and right one */
       
   387 			current_tile = TileHeight(TileXY(col + 1, row)); // bottom edge
       
   388 			if (TileHeight(TileXY(col, row + 1)) < current_tile) {
       
   389 				current_tile = TileHeight(TileXY(col, row + 1)); // right edge
       
   390 			}
       
   391 
       
   392 			/* Does the height differ more than one? */
       
   393 			if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
       
   394 				/* Then change the height to be no more than one */
       
   395 				SetTileHeight(TileXY(col, row), current_tile + 1);
       
   396 			}
       
   397 		}
       
   398 	}
       
   399 }
       
   400 
       
   401 /**
       
   402  * Reads the heightmap with the correct file reader
       
   403  */
       
   404 static bool ReadHeightMap(char *filename, uint *x, uint *y, byte **map)
       
   405 {
       
   406 	switch (_file_to_saveload.mode) {
       
   407 #ifdef WITH_PNG
       
   408 		case SL_PNG:
       
   409 			return ReadHeightmapPNG(filename, x, y, map);
       
   410 #endif /* WITH_PNG */
       
   411 		case SL_BMP:
       
   412 			return ReadHeightmapBMP(filename, x, y, map);
       
   413 
       
   414 		default:
       
   415 			NOT_REACHED();
       
   416 			/* Avoids compiler warnings */
       
   417 			return false;
       
   418 	}
       
   419 }
       
   420 
       
   421 bool GetHeightmapDimensions(char *filename, uint *x, uint *y)
       
   422 {
       
   423 	return ReadHeightMap(filename, x, y, NULL);
       
   424 }
       
   425 
       
   426 void LoadHeightmap(char *filename)
       
   427 {
       
   428 	uint x, y;
       
   429 	byte *map = NULL;
       
   430 
       
   431 	if (!ReadHeightMap(filename, &x, &y, &map)) {
       
   432 		free(map);
       
   433 		return;
       
   434 	}
       
   435 
       
   436 	GrayscaleToMapHeights(x, y, map);
       
   437 	free(map);
       
   438 
       
   439 	FixSlopes();
       
   440 	MarkWholeScreenDirty();
       
   441 }
       
   442 
       
   443 void FlatEmptyWorld(byte tile_height)
       
   444 {
       
   445 	uint width, height;
       
   446 	uint row, col;
       
   447 
       
   448 	width  = MapSizeX();
       
   449 	height = MapSizeY();
       
   450 
       
   451 	for (row = 2; row < height - 2; row++) {
       
   452 		for (col = 2; col < width - 2; col++) {
       
   453 			SetTileHeight(TileXY(col, row), tile_height);
       
   454 		}
       
   455 	}
       
   456 
       
   457 	FixSlopes();
       
   458 	MarkWholeScreenDirty();
       
   459 }