25
25
#include "gdhelpers.h"
26
26
#include "bmp.h"
27
27
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 );
30
30
31
31
static int bmp_read_header (gdIOCtxPtr infile , bmp_hdr_t * hdr );
32
32
static int bmp_read_info (gdIOCtxPtr infile , bmp_info_t * info );
@@ -90,6 +90,7 @@ void gdImageBmp(gdImagePtr im, FILE *outFile, int compression)
90
90
*/
91
91
void gdImageBmpCtx (gdImagePtr im , gdIOCtxPtr out , int compression )
92
92
{
93
+ int bpp , ppbyte , compression_method ;
93
94
int bitmap_size = 0 , info_size , total_size , padding ;
94
95
int i , row , xpos , pixel ;
95
96
int error = 0 ;
@@ -116,7 +117,17 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
116
117
}
117
118
}
118
119
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 ;
120
131
121
132
/* 40 byte Windows v3 header */
122
133
info_size = BMP_WINDOWS_V3 ;
@@ -132,6 +143,8 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
132
143
/* bitmap header + info header + data */
133
144
total_size = 14 + info_size + bitmap_size ;
134
145
146
+ compression_method = (compression ? (bpp >= 8 ? BMP_BI_RLE8 : BMP_BI_RLE4 ) : BMP_BI_RGB );
147
+
135
148
/* write bmp header info */
136
149
gdPutBuf ("BM" , 2 , out );
137
150
gdBMPPutInt (out , total_size );
@@ -144,16 +157,20 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
144
157
gdBMPPutInt (out , im -> sx ); /* width */
145
158
gdBMPPutInt (out , im -> sy ); /* height */
146
159
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 */
149
162
gdBMPPutInt (out , bitmap_size ); /* image size */
150
163
gdBMPPutInt (out , 0 ); /* H resolution */
151
164
gdBMPPutInt (out , 0 ); /* V ressolution */
152
165
gdBMPPutInt (out , im -> colorsTotal ); /* colours used */
153
166
gdBMPPutInt (out , 0 ); /* important colours */
154
167
155
168
/* 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
+ }
157
174
if (padding ) {
158
175
padding = 4 - padding ;
159
176
}
@@ -177,13 +194,37 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
177
194
}
178
195
179
196
for (row = (im -> sy - 1 ); row >= 0 ; row -- ) {
197
+ int byte = 0 ;
180
198
if (compression ) {
181
199
memset (uncompressed_row , 0 , gdImageSX (im ));
182
200
}
183
201
184
202
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 ) {
186
215
* 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
+ }
187
228
} else {
188
229
Putchar (gdImageGetPixel (im , xpos , row ), out );
189
230
}
@@ -196,8 +237,10 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
196
237
}
197
238
} else {
198
239
int compressed_size = 0 ;
240
+ int length = (gdImageSX (im ) + ppbyte - 1 ) / ppbyte ;
241
+ int nibble = gdImageSX (im ) % ppbyte ;
199
242
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 ) {
201
244
error = 1 ;
202
245
break ;
203
246
}
@@ -289,7 +332,7 @@ void gdImageBmpCtx(gdImagePtr im, gdIOCtxPtr out, int compression)
289
332
return ;
290
333
}
291
334
292
- static int compress_row (unsigned char * row , int length )
335
+ static int compress_row (unsigned char * row , int length , int ppbyte , int nibble )
293
336
{
294
337
int rle_type = 0 ;
295
338
int compressed_length = 0 ;
@@ -321,9 +364,9 @@ static int compress_row(unsigned char *row, int length)
321
364
}
322
365
323
366
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 ) {
325
368
/* 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 );
327
370
row += rle_compression ;
328
371
compressed_length += rle_compression ;
329
372
compressed_run = 0 ;
@@ -333,9 +376,9 @@ static int compress_row(unsigned char *row, int length)
333
376
uncompressed_rowp ++ ;
334
377
}
335
378
} 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 ) {
337
380
/* 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 );
339
382
row += rle_compression ;
340
383
compressed_length += rle_compression ;
341
384
compressed_run = 0 ;
@@ -349,19 +392,20 @@ static int compress_row(unsigned char *row, int length)
349
392
}
350
393
}
351
394
395
+ /* the following condition is always true */
352
396
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 );
354
398
}
355
399
356
400
gdFree (uncompressed_start );
357
401
358
402
return compressed_length ;
359
403
}
360
404
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 )
362
406
{
363
407
int compressed_size = 0 ;
364
- if (length < 1 || length > 128 ) {
408
+ if (length < 1 || length > 127 ) {
365
409
return 0 ;
366
410
}
367
411
@@ -370,15 +414,15 @@ static int build_rle_packet(unsigned char *row, int packet_type, int length, uns
370
414
int i = 0 ;
371
415
for (i = 0 ; i < length ; i ++ ) {
372
416
compressed_size += 2 ;
373
- memset (row , 1 , 1 );
417
+ memset (row , ppbyte * 1 - ( i + 1 == length ? nibble : 0 ) , 1 );
374
418
row ++ ;
375
419
376
420
memcpy (row , data ++ , 1 );
377
421
row ++ ;
378
422
}
379
423
} else if (packet_type == BMP_RLE_TYPE_RLE ) {
380
424
compressed_size = 2 ;
381
- memset (row , length , 1 );
425
+ memset (row , ppbyte * length - nibble , 1 );
382
426
row ++ ;
383
427
384
428
memcpy (row , data , 1 );
@@ -388,7 +432,7 @@ static int build_rle_packet(unsigned char *row, int packet_type, int length, uns
388
432
memset (row , BMP_RLE_COMMAND , 1 );
389
433
row ++ ;
390
434
391
- memset (row , length , 1 );
435
+ memset (row , ppbyte * length - nibble , 1 );
392
436
row ++ ;
393
437
394
438
memcpy (row , data , length );
0 commit comments