2525#include "gdhelpers.h"
2626#include "bmp.h"
2727
28- static int compress_row (unsigned char * uncompressed_row , int length );
29- static int build_rle_packet (unsigned char * row , int packet_type , int length , unsigned char * data );
28+ static int compress_row (unsigned char * uncompressed_row , int length , int ppbyte , int nibble );
29+ static int build_rle_packet (unsigned char * row , int packet_type , int length , unsigned char * data , int ppbyte , int nibble );
3030
3131static int bmp_read_header (gdIOCtxPtr infile , bmp_hdr_t * hdr );
3232static int bmp_read_info (gdIOCtxPtr infile , bmp_info_t * info );
@@ -90,6 +90,7 @@ void gdImageBmp(gdImagePtr im, FILE *outFile, int compression)
9090*/
9191void gdImageBmpCtx (gdImagePtr im , gdIOCtxPtr out , int compression )
9292{
93+ int bpp , ppbyte , compression_method ;
9394 int bitmap_size = 0 , info_size , total_size , padding ;
9495 int i , row , xpos , pixel ;
9596 int error = 0 ;
@@ -116,7 +117,17 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
116117 }
117118 }
118119
119- bitmap_size = ((im -> sx * (im -> trueColor ? 24 : 8 )) / 8 ) * im -> sy ;
120+ if (im -> trueColor ) {
121+ bpp = 24 ;
122+ } else if (im -> colorsTotal <= 2 && !compression ) {
123+ bpp = 1 ;
124+ } else if (im -> colorsTotal <= 16 ) {
125+ bpp = 4 ;
126+ } else {
127+ bpp = 8 ;
128+ }
129+ ppbyte = 8 / bpp ; /* for palette images */
130+ bitmap_size = ((im -> sx * bpp + 7 ) / 8 ) * im -> sy ;
120131
121132 /* 40 byte Windows v3 header */
122133 info_size = BMP_WINDOWS_V3 ;
@@ -132,6 +143,8 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
132143 /* bitmap header + info header + data */
133144 total_size = 14 + info_size + bitmap_size ;
134145
146+ compression_method = (compression ? (bpp >= 8 ? BMP_BI_RLE8 : BMP_BI_RLE4 ) : BMP_BI_RGB );
147+
135148 /* write bmp header info */
136149 gdPutBuf ("BM" , 2 , out );
137150 gdBMPPutInt (out , total_size );
@@ -144,16 +157,20 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
144157 gdBMPPutInt (out , im -> sx ); /* width */
145158 gdBMPPutInt (out , im -> sy ); /* height */
146159 gdBMPPutWord (out , 1 ); /* colour planes */
147- gdBMPPutWord (out , ( im -> trueColor ? 24 : 8 ) ); /* bit count */
148- gdBMPPutInt (out , ( compression ? BMP_BI_RLE8 : BMP_BI_RGB ) ); /* compression */
160+ gdBMPPutWord (out , bpp ); /* bit count */
161+ gdBMPPutInt (out , compression_method ); /* compression */
149162 gdBMPPutInt (out , bitmap_size ); /* image size */
150163 gdBMPPutInt (out , 0 ); /* H resolution */
151164 gdBMPPutInt (out , 0 ); /* V ressolution */
152165 gdBMPPutInt (out , im -> colorsTotal ); /* colours used */
153166 gdBMPPutInt (out , 0 ); /* important colours */
154167
155168 /* The line must be divisible by 4, else it's padded with NULLs */
156- padding = ((int )(im -> trueColor ? 3 : 1 ) * im -> sx ) % 4 ;
169+ if (bpp < 8 ) {
170+ padding = (im -> sx + ppbyte - 1 ) / ppbyte % 4 ;
171+ } else {
172+ padding = ((int )(im -> trueColor ? 3 : 1 ) * im -> sx ) % 4 ;
173+ }
157174 if (padding ) {
158175 padding = 4 - padding ;
159176 }
@@ -177,13 +194,37 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
177194 }
178195
179196 for (row = (im -> sy - 1 ); row >= 0 ; row -- ) {
197+ int byte = 0 ;
180198 if (compression ) {
181199 memset (uncompressed_row , 0 , gdImageSX (im ));
182200 }
183201
184202 for (xpos = 0 ; xpos < im -> sx ; xpos ++ ) {
185- if (compression ) {
203+ if (compression && bpp < 8 ) {
204+ byte = (byte << bpp ) + gdImageGetPixel (im , xpos , row );
205+ if (xpos % 2 == 1 ) {
206+ * uncompressed_row ++ = (unsigned char ) byte ;
207+ byte = 0 ;
208+ } else if (xpos + 1 == im -> sx ) {
209+ /* imcomplete byte at end of row */
210+ byte <<= bpp ;
211+ * uncompressed_row ++ = (unsigned char ) byte ;
212+ byte = 0 ;
213+ }
214+ } else if (compression ) {
186215 * uncompressed_row ++ = (unsigned char )gdImageGetPixel (im , xpos , row );
216+ } else if (bpp < 8 ) {
217+ int next_chunk_in_byte = (xpos + 1 ) % ppbyte ;
218+ byte = (byte << bpp ) + gdImageGetPixel (im , xpos , row );
219+ if (next_chunk_in_byte == 0 ) {
220+ Putchar (byte , out );
221+ byte = 0 ;
222+ } else if (xpos + 1 == im -> sx ) {
223+ /* imcomplete byte at end of row */
224+ byte <<= bpp * (ppbyte - next_chunk_in_byte );
225+ Putchar (byte , out );
226+ byte = 0 ;
227+ }
187228 } else {
188229 Putchar (gdImageGetPixel (im , xpos , row ), out );
189230 }
@@ -196,8 +237,10 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
196237 }
197238 } else {
198239 int compressed_size = 0 ;
240+ int length = (gdImageSX (im ) + ppbyte - 1 ) / ppbyte ;
241+ int nibble = gdImageSX (im ) % ppbyte ;
199242 uncompressed_row = uncompressed_row_start ;
200- if ((compressed_size = compress_row (uncompressed_row , gdImageSX ( im ) )) < 0 ) {
243+ if ((compressed_size = compress_row (uncompressed_row , length , ppbyte , nibble )) < 0 ) {
201244 error = 1 ;
202245 break ;
203246 }
@@ -289,7 +332,7 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
289332 return ;
290333}
291334
292- static int compress_row (unsigned char * row , int length )
335+ static int compress_row (unsigned char * row , int length , int ppbyte , int nibble )
293336{
294337 int rle_type = 0 ;
295338 int compressed_length = 0 ;
@@ -321,9 +364,9 @@ static int compress_row(unsigned char *row, int length)
321364 }
322365
323366 if (rle_type == BMP_RLE_TYPE_RLE ) {
324- if (compressed_run >= 128 || memcmp (uncompressed_rowp , uncompressed_rowp - 1 , 1 ) != 0 ) {
367+ if (compressed_run >= 127 || memcmp (uncompressed_rowp , uncompressed_rowp - 1 , 1 ) != 0 ) {
325368 /* more than what we can store in a single run or run is over due to non match, force write */
326- rle_compression = build_rle_packet (row , rle_type , compressed_run , uncompressed_row );
369+ rle_compression = build_rle_packet (row , rle_type , compressed_run , uncompressed_row , ppbyte , 0 );
327370 row += rle_compression ;
328371 compressed_length += rle_compression ;
329372 compressed_run = 0 ;
@@ -333,9 +376,9 @@ static int compress_row(unsigned char *row, int length)
333376 uncompressed_rowp ++ ;
334377 }
335378 } else {
336- if (compressed_run >= 128 || memcmp (uncompressed_rowp , uncompressed_rowp - 1 , 1 ) == 0 ) {
379+ if (compressed_run >= 127 || memcmp (uncompressed_rowp , uncompressed_rowp - 1 , 1 ) == 0 ) {
337380 /* more than what we can store in a single run or run is over due to match, force write */
338- rle_compression = build_rle_packet (row , rle_type , compressed_run , uncompressed_row );
381+ rle_compression = build_rle_packet (row , rle_type , compressed_run , uncompressed_row , ppbyte , 0 );
339382 row += rle_compression ;
340383 compressed_length += rle_compression ;
341384 compressed_run = 0 ;
@@ -349,19 +392,20 @@ static int compress_row(unsigned char *row, int length)
349392 }
350393 }
351394
395+ /* the following condition is always true */
352396 if (compressed_run ) {
353- compressed_length += build_rle_packet (row , rle_type , compressed_run , uncompressed_row );
397+ compressed_length += build_rle_packet (row , rle_type , compressed_run , uncompressed_row , ppbyte , nibble );
354398 }
355399
356400 gdFree (uncompressed_start );
357401
358402 return compressed_length ;
359403}
360404
361- static int build_rle_packet (unsigned char * row , int packet_type , int length , unsigned char * data )
405+ static int build_rle_packet (unsigned char * row , int packet_type , int length , unsigned char * data , int ppbyte , int nibble )
362406{
363407 int compressed_size = 0 ;
364- if (length < 1 || length > 128 ) {
408+ if (length < 1 || length > 127 ) {
365409 return 0 ;
366410 }
367411
@@ -370,15 +414,15 @@ static int build_rle_packet(unsigned char *row, int packet_type, int length, uns
370414 int i = 0 ;
371415 for (i = 0 ; i < length ; i ++ ) {
372416 compressed_size += 2 ;
373- memset (row , 1 , 1 );
417+ memset (row , ppbyte * 1 - ( i + 1 == length ? nibble : 0 ) , 1 );
374418 row ++ ;
375419
376420 memcpy (row , data ++ , 1 );
377421 row ++ ;
378422 }
379423 } else if (packet_type == BMP_RLE_TYPE_RLE ) {
380424 compressed_size = 2 ;
381- memset (row , length , 1 );
425+ memset (row , ppbyte * length - nibble , 1 );
382426 row ++ ;
383427
384428 memcpy (row , data , 1 );
@@ -388,7 +432,7 @@ static int build_rle_packet(unsigned char *row, int packet_type, int length, uns
388432 memset (row , BMP_RLE_COMMAND , 1 );
389433 row ++ ;
390434
391- memset (row , length , 1 );
435+ memset (row , ppbyte * length - nibble , 1 );
392436 row ++ ;
393437
394438 memcpy (row , data , length );
0 commit comments