Commits

Anonymous committed adf364a

Add RLE compression

Comments (0)

Files changed (2)

 #define BMP_BI_JPEG 4
 #define BMP_BI_PNG 5
 
+#define BMP_RLE_COMMAND 0
+#define BMP_RLE_ENDOFLINE 0
+#define BMP_RLE_ENDOFBITMAP 1
+#define BMP_RLE_DELTA 2
+
 /* BMP header. */
 typedef struct
 {
 
 	Todo:
 
-	RLE4, RLE8 and Bitfield encoding
+	Bitfield encoding
 	Add full support for Windows v4 and Windows v5 header formats
 	Add write support
 
 static int bmp_read_1bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
 static int bmp_read_4bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
 static int bmp_read_8bit(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info, bmp_hdr_t *header);
+static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info);
 
-/* Byte helper functions */
+/* Byte helper functions, since added to GD 2.1 */
 static int gdBMPGetInt(signed int *result, gdIOCtx * ctx);
 static int gdBMPGetWord(signed short int *result, gdIOCtx * ctx);
 
 				BMP_DEBUG(printf("Bitfield compression isn't supported for 24-bit\n"));
 				return 1;
 			}
-			BMP_DEBUG(printf("No bitfield support atm\n"));
+			BMP_DEBUG(printf("Currently no bitfield support\n"));
 			return 1;
 		break;
 
 	int ypos = 0, xpos = 0, row = 0, index = 0;
 	int padding = 0, current_byte = 0;
 
-	if (info->enctype != BMP_BI_RGB /*&& info->enctype != BMP_BI_RLE4*/)
+	if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE4)
 	{
 		return 1;
 	}
 		padding = 4 - padding;
 	}
 
-	for (ypos = 0; ypos < info->height; ++ypos)
+	switch (info->enctype)
 	{
-		if (info->topdown)
+		case BMP_BI_RGB:
+		for (ypos = 0; ypos < info->height; ++ypos)
 		{
-			row = ypos;
-		}
-		else
-		{
-			row = info->height - ypos - 1;
-		}
-		for (xpos = 0; xpos < info->width; xpos+=2)
-		{
-			if (!gdGetByte(&current_byte, infile))
+			if (info->topdown)
 			{
-				return 1;
+				row = ypos;
 			}
-			index = (current_byte >> 4) & 0x0f;
-			if (im->open[index])
+			else
 			{
-				im->open[index] = 0;
+				row = info->height - ypos - 1;
 			}
-			gdImageSetPixel(im, xpos, row, index);
-			/* This condition may get called often, potential optimsations */
-			if (xpos >= info->width)
+			for (xpos = 0; xpos < info->width; xpos+=2)
 			{
-				break;
+				if (!gdGetByte(&current_byte, infile))
+				{
+					return 1;
+				}
+				index = (current_byte >> 4) & 0x0f;
+				if (im->open[index])
+				{
+					im->open[index] = 0;
+				}
+				gdImageSetPixel(im, xpos, row, index);
+				/* This condition may get called often, potential optimsations */
+				if (xpos >= info->width)
+				{
+					break;
+				}
+	
+				index = current_byte & 0x0f;
+				if (im->open[index])
+				{
+					im->open[index] = 0;
+				}
+				gdImageSetPixel(im, xpos + 1, row, index);
 			}
-
-			index = current_byte & 0x0f;
-			if (im->open[index])
+			for (xpos = padding; xpos > 0; --xpos)
 			{
-				im->open[index] = 0;
-			}
-			gdImageSetPixel(im, xpos + 1, row, index);
-		}
-		for (xpos = padding; xpos > 0; --xpos)
-		{
-			if (!gdGetByte(&index, infile))
-			{
-				return 1;
+				if (!gdGetByte(&index, infile))
+				{
+					return 1;
+				}
 			}
 		}
+		break;
+
+		case BMP_BI_RLE4:
+		if (bmp_read_rle(im, infile, info))
+		{
+			return 1;
+		}
+		break;
+
+		default:
+			return 1;
 	}
 	return 0;
 }
 {
 	int ypos = 0, xpos = 0, row = 0, index = 0;
 	int padding = 0;
-	if (info->enctype != BMP_BI_RGB/* && info->enctype != BMP_BI_RLE8*/)
+
+	if (info->enctype != BMP_BI_RGB && info->enctype != BMP_BI_RLE8)
 	{
 		return 1;
 	}
 			}
 		}
 		break;
+
 		case BMP_BI_RLE8:
-			BMP_DEBUG(printf("Run-length Encoding 8bits / pixel isn't supported\n"));
+		if (bmp_read_rle(im, infile, info))
+		{
+			return 1;
+		}
 		break;
+
 		default:
 			return 1;
 	}
 	return 0;
 }
 
+static int bmp_read_rle(gdImagePtr im, gdIOCtxPtr infile, bmp_info_t *info)
+{
+	int ypos = 0, xpos = 0, row = 0, index = 0;
+	int rle_length = 0, rle_data = 0;
+	int padding = 0;
+	int i = 0, j = 0;
+	int pixels_per_byte = 8 / info->depth;
+
+	for (ypos = 0; ypos < info->height && xpos <= info->width;)
+	{
+		if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile))
+		{
+			return 1;
+		}
+		row = info->height - ypos - 1;
+
+		if (rle_length != BMP_RLE_COMMAND)
+		{
+			if (im->open[rle_data])
+			{
+				im->open[rle_data] = 0;
+			}
+
+			for (i = 0; (i < rle_length) && (xpos < info->width);)
+			{
+				for (j = 1; (j <= pixels_per_byte) && (xpos < info->width) && (i < rle_length); j++, xpos++, i++)
+				{
+					index = (rle_data & (((1 << info->depth) - 1) << (8 - (j * info->depth)))) >> (8 - (j * info->depth));
+					if (im->open[index])
+					{
+						im->open[index] = 0;
+					}
+					gdImageSetPixel(im, xpos, row, index);
+				}
+			}
+		}
+		else if (rle_length == BMP_RLE_COMMAND && rle_data > 2)
+		{
+			/* Uncompressed RLE needs to be even */
+			padding = 0;
+			for (i = 0; (i < rle_data) && (xpos < info->width); i += pixels_per_byte)
+			{
+				int max_pixels = pixels_per_byte;
+
+				if (!gdGetByte(&index, infile))
+				{
+					return 1;
+				}
+				padding++;
+
+				if (rle_data - i < max_pixels)
+				{
+					max_pixels = rle_data - i;
+				}
+
+				for (j = 1; (j <= max_pixels)  && (xpos < info->width); j++, xpos++)
+				{
+					int temp = (index >> (8 - (j * info->depth))) & ((1 << info->depth) - 1);
+					if (im->open[temp])
+					{
+						im->open[temp] = 0;
+					}
+					gdImageSetPixel(im, xpos, row, temp);
+				}
+			}
+
+			/* Make sure the bytes read are even */
+			if (padding % 2 && !gdGetByte(&index, infile))
+			{
+				return 1;
+			}
+		}
+		else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFLINE)
+		{
+			/* Next Line */
+			xpos = 0;
+			ypos++;
+		}
+		else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_DELTA)
+		{
+			/* Delta Record, used for bmp files that contain other data*/
+			if (!gdGetByte(&rle_length, infile) || !gdGetByte(&rle_data, infile))
+			{
+				return 1;
+			}
+			xpos += rle_length;
+			ypos += rle_data;
+		}
+		else if (rle_length == BMP_RLE_COMMAND && rle_data == BMP_RLE_ENDOFBITMAP)
+		{
+			/* End of bitmap */
+			break;
+		}
+	}
+	return 0;
+}
+
 static int gdBMPGetWord(signed short int *result, gdIOCtx * ctx)
 {
 	int high = 0, low = 0;