GCC Code Coverage Report


Directory: src/gate/
File: src/gate/graphics/bitmapimages.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 131 375 34.9%
Functions: 3 5 60.0%
Branches: 32 127 25.2%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/graphics/bitmapimages.h"
30 #include "gate/results.h"
31 #include "gate/serializers.h"
32 #include "gate/debugging.h"
33 #include "gate/mathematics.h"
34
35 typedef struct gate_bmp_header_class
36 {
37 gate_uint32_t header_sz;
38 gate_int32_t width;
39 gate_int32_t height;
40 gate_uint16_t nplanes;
41 gate_uint16_t bitspp;
42 gate_uint32_t compress_type;
43 gate_uint32_t bmp_bytesz;
44 gate_int32_t hres;
45 gate_int32_t vres;
46 gate_uint32_t ncolors;
47 gate_uint32_t nimpcolors;
48 } gate_bmp_header_t;
49
50
51
52 1 static gate_result_t load_bitmap_as_rgba_image(
53 gate_stream_t* srcstream,
54 gate_bmp_header_t const* ptr_header,
55 char const* palette,
56 gate_size_t linelen,
57 gate_rasterimage_t* image)
58 {
59 1 gate_result_t result = GATE_RESULT_FAILED;
60 1 const unsigned width = ptr_header->width < 0 ? (unsigned)(-ptr_header->width) : (unsigned)ptr_header->width;
61 1 const unsigned height = ptr_header->height < 0 ? (unsigned)(-ptr_header->height) : (unsigned)ptr_header->height;
62 gate_rasterimage_t raster;
63 gate_index_t lineadd;
64 gate_color_t* ptrpixel;
65 gate_color_t* ptrline;
66 unsigned x, y, z;
67 gate_size_t lenread;
68 char buffer[8192];
69 1 char* ptr_buffer = &buffer[0];
70 gate_uint8_t const* ptrbytes;
71 gate_uint8_t const* ptrpal;
72 1 gate_bool_t all_alphas_are_zero = true;
73
74 do
75 {
76 1 gate_mem_clear(&raster, sizeof(raster));
77
78
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_rasterimage_create(&raster, GATE_IMAGE_PIXELFORMAT_RGBA, width, height, NULL))
79 {
80 result = GATE_RESULT_OUTOFMEMORY;
81 break;
82 }
83
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (linelen > sizeof(buffer))
85 {
86 ptr_buffer = gate_mem_alloc(linelen);
87 if (NULL == ptr_buffer)
88 {
89 result = GATE_RESULT_OUTOFMEMORY;
90 break;
91 }
92 }
93
94
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ptr_header->height < 0)
95 {
96 ptrpixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&raster, 0);
97 lineadd = width;
98 }
99 else
100 {
101 1 ptrpixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&raster, (height - 1));
102 1 lineadd = -(gate_index_t)width;
103 }
104
105 1 result = GATE_RESULT_OK;
106
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1 times.
33 for (y = 0; y != height; ++y)
107 {
108
109 32 result = gate_stream_read_block(srcstream, ptr_buffer, linelen, &lenread);
110
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (GATE_FAILED(result))
111 {
112 break;
113 }
114
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (lenread < linelen)
115 {
116 result = GATE_RESULT_ENDOFSTREAM;
117 break;
118 }
119 32 ptrline = ptrpixel;
120 32 ptrbytes = (gate_uint8_t const*)ptr_buffer;
121
122
2/2
✓ Branch 0 taken 2048 times.
✓ Branch 1 taken 32 times.
2080 for (x = 0; x < width; ++x)
123 {
124
1/7
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 2048 times.
2048 switch (ptr_header->bitspp)
125 {
126 default:
127 {
128 ptrline->b = 0;
129 ptrline->g = 0;
130 ptrline->r = 0;
131 ptrline->a = 255;
132 ++ptrline;
133 break;
134 }
135 case 1:
136 {
137 for (z = 0; z != 8; ++z)
138 {
139 if (*ptrbytes & (gate_uint8_t)(1 << (7 - z)))
140 {
141 ptrpal = (gate_uint8_t const*)&palette[4];
142 }
143 else
144 {
145 ptrpal = (gate_uint8_t const*)&palette[0];
146 }
147 ptrline->b = ptrpal[0];
148 ptrline->g = ptrpal[1];
149 ptrline->r = ptrpal[2];
150 ptrline->a = ptrpal[3];
151 all_alphas_are_zero &= ((ptrline->a == 0) ? true : false);
152 ++ptrline;
153 }
154 ++ptrbytes;
155 x += 7;
156 break;
157 }
158 case 4:
159 {
160 ptrpal = (gate_uint8_t const*)&palette[4 * (unsigned)(*ptrbytes >> 4)];
161 ptrline->b = ptrpal[0];
162 ptrline->g = ptrpal[1];
163 ptrline->r = ptrpal[2];
164 ptrline->a = ptrpal[3];
165 all_alphas_are_zero &= ((ptrline->a == 0) ? true : false);
166 ++ptrline;
167
168 ptrpal = (gate_uint8_t const*)&palette[4 * (unsigned)(*ptrbytes & 0x0f)];
169 ptrline->b = ptrpal[0];
170 ptrline->g = ptrpal[1];
171 ptrline->r = ptrpal[2];
172 ptrline->a = ptrpal[3];
173 all_alphas_are_zero &= ((ptrline->a == 0) ? true : false);
174 ++ptrline;
175
176 ++ptrbytes;
177 ++x;
178 break;
179 }
180 case 8:
181 {
182 ptrpal = (gate_uint8_t const*)&palette[4 * (gate_size_t)*ptrbytes];
183 ++ptrbytes;
184 ptrline->b = ptrpal[0];
185 ptrline->g = ptrpal[1];
186 ptrline->r = ptrpal[2];
187 ptrline->a = ptrpal[3];
188 all_alphas_are_zero &= ((ptrline->a == 0) ? true : false);
189 ++ptrline;
190 break;
191 }
192 case 15:
193 case 16:
194 {
195 ptrline->b = (ptrbytes[0] & 0x1f) << 3;
196 ptrline->g = ((ptrbytes[0] >> 5) | ((ptrbytes[1] & 0x03) << 3)) << 3;
197 ptrline->r = (ptrbytes[1] & 0x7c) << 1;
198 ptrline->a = 255;
199 ++ptrline;
200 break;
201 }
202 case 24:
203 {
204 ptrline->b = *ptrbytes++;
205 ptrline->g = *ptrbytes++;
206 ptrline->r = *ptrbytes++;
207 ptrline->a = 255;
208 ++ptrline;
209 break;
210 }
211 2048 case 32:
212 {
213 2048 ptrline->b = *ptrbytes++;
214 2048 ptrline->g = *ptrbytes++;
215 2048 ptrline->r = *ptrbytes++;
216 2048 ptrline->a = *ptrbytes++;
217 2048 all_alphas_are_zero &= ((ptrline->a == 0) ? true : false);
218 2048 ++ptrline;
219 2048 break;
220 }
221 }
222 } /* for(x) */
223
224 32 ptrpixel += lineadd;
225 } /* for(y) */
226
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(result);
228
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (all_alphas_are_zero)
230 {
231 /* all alpha values are zero means, that
232 the soruce stream had no alpha-values stored
233 and all the reserved bits were zero.
234 Therfore we just patch all alpha-values to 255
235 */
236 gate_size_t pixelcount = (gate_size_t)raster.width * (gate_size_t)raster.height;
237 ptrpixel = (gate_color_t*)gate_rasterimage_get_line_ptr(&raster, 0);
238 while (pixelcount-- != 0)
239 {
240 ptrpixel->a = 255;
241 ++ptrpixel;
242 }
243 }
244
245 /* success case, transfer local image object into output object */
246 1 gate_mem_copy(image, &raster, sizeof(raster));
247 1 gate_mem_clear(&raster, sizeof(raster));
248 1 result = GATE_RESULT_OK;
249 } while (0);
250
251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if ((ptr_buffer != NULL) & (ptr_buffer != &buffer[0]))
252 {
253 gate_mem_dealloc(ptr_buffer);
254 }
255 1 gate_rasterimage_release(&raster);
256
257 1 return result;
258 }
259
260
261 static gate_result_t load_bitmap_generic(
262 gate_stream_t* srcstream,
263 gate_bmp_header_t const* ptr_header,
264 char const* palette,
265 gate_size_t linelen,
266 gate_enumint_t pixel_format,
267 gate_rasterimage_t* image)
268 {
269 gate_result_t result = GATE_RESULT_FAILED;
270 const unsigned width = ptr_header->width < 0 ? (unsigned)(-ptr_header->width) : (unsigned)ptr_header->width;
271 const unsigned height = ptr_header->height < 0 ? (unsigned)(-ptr_header->height) : (unsigned)ptr_header->height;
272 gate_rasterimage_t raster;
273 unsigned x, y, z;
274 gate_size_t lenread;
275 gate_color_t current_pixel;
276 char buffer[8192];
277 char* ptr_buffer = &buffer[0];
278 gate_uint8_t const* ptrpal;
279
280 do
281 {
282 gate_mem_clear(&raster, sizeof(raster));
283
284 if (NULL == gate_rasterimage_create(&raster, pixel_format, width, height, NULL))
285 {
286 result = GATE_RESULT_OUTOFMEMORY;
287 break;
288 }
289
290 if (linelen > sizeof(buffer))
291 {
292 ptr_buffer = gate_mem_alloc(linelen);
293 if (NULL == ptr_buffer)
294 {
295 result = GATE_RESULT_OUTOFMEMORY;
296 break;
297 }
298 }
299
300 result = GATE_RESULT_OK;
301 for (y = 0; y != height; ++y)
302 {
303 const unsigned dest_y = (ptr_header->height < 0) ? y : height - y - 1;
304 gate_uint8_t const* ptr_line_bytes = (gate_uint8_t const*)ptr_buffer;
305
306 result = gate_stream_read_block(srcstream, ptr_buffer, linelen, &lenread);
307 if (GATE_FAILED(result))
308 {
309 break;
310 }
311 if (lenread < linelen)
312 {
313 result = GATE_RESULT_ENDOFSTREAM;
314 break;
315 }
316
317 for (x = 0; x < width; ++x)
318 {
319 switch (ptr_header->bitspp)
320 {
321 default:
322 {
323 current_pixel.b = 0;
324 current_pixel.g = 0;
325 current_pixel.r = 0;
326 current_pixel.a = 255;
327 ++ptr_line_bytes;
328 break;
329 }
330 case 1:
331 {
332 for (z = 0; z != 8; ++z)
333 {
334 if (*ptr_line_bytes & (gate_uint8_t)(1 << (7 - z)))
335 {
336 ptrpal = (gate_uint8_t const*)&palette[4];
337 }
338 else
339 {
340 ptrpal = (gate_uint8_t const*)&palette[0];
341 }
342 current_pixel.b = ptrpal[0];
343 current_pixel.g = ptrpal[1];
344 current_pixel.r = ptrpal[2];
345 current_pixel.a = 255;
346
347 gate_rasterimage_set_pixel(&raster, x + z, dest_y, &current_pixel);
348 }
349 ++ptr_line_bytes;
350 x += 7;
351 break;
352 }
353 case 4:
354 {
355 ptrpal = (gate_uint8_t const*)&palette[4 * (unsigned)(*ptr_line_bytes >> 4)];
356 current_pixel.b = ptrpal[0];
357 current_pixel.g = ptrpal[1];
358 current_pixel.r = ptrpal[2];
359 current_pixel.a = 255;
360 gate_rasterimage_set_pixel(&raster, x, dest_y, &current_pixel);
361 ++ptr_line_bytes;
362 ++x;
363
364 ptrpal = (gate_uint8_t const*)&palette[4 * (unsigned)(*ptr_line_bytes & 0x0f)];
365 current_pixel.b = ptrpal[0];
366 current_pixel.g = ptrpal[1];
367 current_pixel.r = ptrpal[2];
368 current_pixel.a = 255;
369 ++ptr_line_bytes;
370 break;
371 }
372 case 8:
373 {
374 ptrpal = (gate_uint8_t const*)&palette[4 * (gate_size_t)*ptr_line_bytes];
375 current_pixel.b = ptrpal[0];
376 current_pixel.g = ptrpal[1];
377 current_pixel.r = ptrpal[2];
378 current_pixel.a = 255;
379 ++ptr_line_bytes;
380 break;
381 }
382 case 15:
383 case 16:
384 {
385 current_pixel.b = (ptr_line_bytes[0] & 0x1f) << 3;
386 current_pixel.g = ((ptr_line_bytes[0] >> 5) | ((ptr_line_bytes[1] & 0x03) << 3)) << 3;
387 current_pixel.r = (ptr_line_bytes[1] & 0x7c) << 1;
388 current_pixel.a = 255;
389 ptr_line_bytes += 2;
390 break;
391 }
392 case 24:
393 {
394 current_pixel.b = ptr_line_bytes[0];
395 current_pixel.g = ptr_line_bytes[1];
396 current_pixel.r = ptr_line_bytes[2];
397 current_pixel.a = 255;
398 ptr_line_bytes += 3;
399 break;
400 }
401 case 32:
402 {
403 current_pixel.b = ptr_line_bytes[0];
404 current_pixel.g = ptr_line_bytes[1];
405 current_pixel.r = ptr_line_bytes[2];
406 current_pixel.a = ptr_line_bytes[3];
407 ptr_line_bytes += 4;
408 break;
409 }
410 }
411
412 gate_rasterimage_set_pixel(&raster, x, y, &current_pixel);
413 } /* for(x) */
414 } /* for(y) */
415 GATE_BREAK_IF_FAILED(result);
416
417 /* success case, transfer local image object into output object */
418 gate_mem_copy(image, &raster, sizeof(raster));
419 gate_mem_clear(&raster, sizeof(raster));
420 result = GATE_RESULT_OK;
421 } while (0);
422
423 if ((ptr_buffer != NULL) & (ptr_buffer != &buffer[0]))
424 {
425 gate_mem_dealloc(ptr_buffer);
426 }
427 gate_rasterimage_release(&raster);
428
429 return result;
430 }
431
432
433 1 gate_result_t gate_bitmapimage_load(gate_stream_t* srcstream, gate_rasterimage_t* image, gate_enumint_t flags)
434 {
435 static gate_size_t const header_min_size = 54;
436 char palette[1024];
437 1 char* ptr_buffer = &palette[0];
438 gate_result_t result;
439 gate_uint32_t filesize;
440 gate_uint32_t creatorid;
441 gate_uint32_t bmpoffset;
442 gate_size_t bufferused;
443 gate_bmp_header_t header;
444
445 gate_size_t linelen;
446 gate_size_t remain;
447 1 const gate_enumint_t load_pixel_format = flags & GATE_IMAGE_FLAG_DEPTHMASK;
448
449 (void)flags;
450
451 do
452 {
453 1 gate_rasterimage_create(image, GATE_IMAGE_PIXELFORMAT_DEFAULT, 0, 0, NULL);
454
455 1 result = gate_stream_read_block(srcstream, ptr_buffer, header_min_size, &bufferused);
456
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
457 {
458 result = GATE_RESULT_INVALIDINPUT;
459 break;
460 }
461
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
1 if (bufferused < header_min_size || (ptr_buffer[0] != 'B') || (ptr_buffer[1] != 'M'))
462 {
463 result = GATE_RESULT_INVALIDHEADER;
464 break;
465 }
466 1 gate_deserialize_uint32_l(&ptr_buffer[2], 4, &filesize);
467 1 gate_deserialize_uint32_l(&ptr_buffer[6], 4, &creatorid);
468 1 gate_deserialize_uint32_l(&ptr_buffer[10], 4, &bmpoffset);
469
470
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (bmpoffset < header_min_size)
471 {
472 result = GATE_RESULT_INVALIDHEADER;
473 break;
474 }
475
476 1 gate_deserialize_uint32_l(&ptr_buffer[14], 4, &header.header_sz);
477 1 gate_deserialize_int32_l(&ptr_buffer[18], 4, &header.width);
478 1 gate_deserialize_int32_l(&ptr_buffer[22], 4, &header.height);
479 1 gate_deserialize_uint16_l(&ptr_buffer[26], 2, &header.nplanes);
480 1 gate_deserialize_uint16_l(&ptr_buffer[28], 2, &header.bitspp);
481 1 gate_deserialize_uint32_l(&ptr_buffer[30], 4, &header.compress_type);
482 1 gate_deserialize_uint32_l(&ptr_buffer[34], 4, &header.bmp_bytesz);
483 1 gate_deserialize_int32_l(&ptr_buffer[38], 4, &header.hres);
484 1 gate_deserialize_int32_l(&ptr_buffer[42], 4, &header.vres);
485 1 gate_deserialize_uint32_l(&ptr_buffer[46], 4, &header.ncolors);
486 1 gate_deserialize_uint32_l(&ptr_buffer[50], 4, &header.nimpcolors);
487
488 1 result = GATE_RESULT_OK;
489
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 switch (header.compress_type)
490 {
491 1 case 0: /* BI_RGB */
492 {
493 /* supported */
494 1 break;
495 }
496 case 1: /* BI_RLE8 */
497 case 2: /* BI_RLE4 */
498 case 3: /* BI_BITFIELDS */
499 case 4: /* BI_JPEG */
500 case 5: /* BI_PNG */
501 default: /* unknown */
502 {
503 /* unsupported encoding */
504 result = GATE_RESULT_NOTSUPPORTED;
505 break;
506 }
507 }
508
509
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(result);
510
511
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (bmpoffset > header_min_size)
512 {
513 result = gate_stream_read_block(srcstream, palette, bmpoffset - header_min_size, &bufferused);
514 if (GATE_FAILED(result))
515 {
516 result = GATE_RESULT_INVALIDHEADER;
517 break;
518 }
519 if (bufferused < (bmpoffset - header_min_size))
520 {
521 result = GATE_RESULT_INVALIDINPUT;
522 break;
523 }
524 }
525
526 1 linelen = 0;
527
528
1/7
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 switch (header.bitspp)
529 {
530 case 1:
531 {
532 linelen = (header.width + 7) / 8;
533 break;
534 }
535 case 4:
536 {
537 linelen = (header.width + 1) / 2;
538 break;
539 }
540 case 8:
541 {
542 linelen = header.width;
543 break;
544 }
545 case 15:
546 case 16:
547 {
548 linelen = (gate_size_t)gate_math_abs_i32(header.width) * 2;
549 break;
550 }
551 case 24:
552 {
553 linelen = (gate_size_t)gate_math_abs_i32(header.width) * 3;
554 break;
555 }
556 1 case 32:
557 {
558 1 linelen = (gate_size_t)gate_math_abs_i32(header.width) * 4;
559 1 break;
560 }
561 }
562
563 1 remain = linelen % 4;
564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (remain != 0)
565 {
566 linelen += 4 - remain;
567 }
568
569
1/5
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 switch (load_pixel_format)
570 {
571 case GATE_IMAGE_FLAG_DEPTH_8GRAY: result = load_bitmap_generic(srcstream, &header, palette, linelen, GATE_IMAGE_PIXELFORMAT_GRAY8, image); break;
572 case GATE_IMAGE_FLAG_DEPTH_24RGB: result = load_bitmap_generic(srcstream, &header, palette, linelen, GATE_IMAGE_PIXELFORMAT_RGB24, image); break;
573 case GATE_IMAGE_FLAG_DEPTH_15RGB: result = load_bitmap_generic(srcstream, &header, palette, linelen, GATE_IMAGE_PIXELFORMAT_RGB555, image); break;
574 case GATE_IMAGE_FLAG_DEPTH_4COL:
575 case GATE_IMAGE_FLAG_DEPTH_8COL: result = load_bitmap_generic(srcstream, &header, palette, linelen, GATE_IMAGE_PIXELFORMAT_PAL8, image); break;
576 1 case GATE_IMAGE_FLAG_DEPTH_32RGBA:
577 case GATE_IMAGE_FLAG_DEPTH_DEF:
578 1 default: result = load_bitmap_as_rgba_image(srcstream, &header, palette, linelen, image); break;
579 }
580
581 } while (0);
582
583 1 return result;
584 }
585
586
587 static void load_bgra_line(gate_uint8_t* bmp_buffer, gate_uint8_t const* ptr_line, gate_size_t length)
588 {
589 gate_mem_copy(bmp_buffer, ptr_line, length * 4);
590 }
591
592 1 gate_result_t gate_bitmapimage_save(gate_rasterimage_t const* image, gate_stream_t* deststream, gate_enumint_t flags)
593 {
594 gate_result_t result;
595 gate_size_t byteswritten;
596 char buffer[12288];
597 1 char* ptrbuffer = &buffer[0];
598
599 1 char header[54] = GATE_INIT_EMPTY;
600 gate_bmp_header_t bmp;
601 1 const gate_size_t bmp_linelen = (gate_size_t)image->width * 4; /* 4 bytes per pixel */
602 1 const gate_size_t bmp_datasize = bmp_linelen * image->height;
603 1 const gate_size_t img_linelen =
604 1 (gate_size_t)(image->width * (unsigned)image->bits_per_pixel / 8u + (unsigned)image->line_padding);
605 gate_uint8_t const* ptrline;
606 gate_uint8_t* ptrbyte;
607
608 1 const gate_uint32_t filesize = (gate_uint32_t)bmp_datasize + 54;
609 1 gate_uint32_t creator = 0;
610 1 gate_uint32_t offset = 54;
611
612 gate_uint32_t y;
613 1 void(*line_writer)(gate_uint8_t*, gate_uint8_t const*, gate_size_t) = NULL;
614
615 (void)flags;
616
617 1 bmp.header_sz = 40;
618 1 bmp.width = (gate_int32_t)image->width;
619 1 bmp.height = (gate_int32_t)image->height;
620 1 bmp.nplanes = 1;
621 1 bmp.bitspp = 32;
622 1 bmp.compress_type = 0; /* no compression */
623 1 bmp.bmp_bytesz = (gate_uint32_t)bmp_datasize;
624 1 bmp.hres = 0;
625 1 bmp.vres = 0;
626 1 bmp.ncolors = 0;
627 1 bmp.nimpcolors = 0;
628
629 1 header[0] = 'B';
630 1 header[1] = 'M';
631 1 gate_serialize_uint32_l(&header[2], 4, &filesize);
632 1 gate_serialize_uint32_l(&header[6], 4, &creator);
633 1 gate_serialize_uint32_l(&header[10], 4, &offset);
634
635 1 gate_serialize_uint32_l(&header[14], 4, &bmp.header_sz);
636 1 gate_serialize_int32_l(&header[18], 4, &bmp.width);
637 1 gate_serialize_int32_l(&header[22], 4, &bmp.height);
638 1 gate_serialize_uint16_l(&header[26], 2, &bmp.nplanes);
639 1 gate_serialize_uint16_l(&header[28], 2, &bmp.bitspp);
640 1 gate_serialize_uint32_l(&header[30], 4, &bmp.compress_type);
641 1 gate_serialize_uint32_l(&header[34], 4, &bmp.bmp_bytesz);
642
643 1 gate_serialize_int32_l(&header[38], 4, &bmp.hres);
644 1 gate_serialize_int32_l(&header[42], 4, &bmp.vres);
645 1 gate_serialize_uint32_l(&header[46], 4, &bmp.ncolors);
646 1 gate_serialize_uint32_l(&header[50], 4, &bmp.nimpcolors);
647
648 do
649 {
650
1/11
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
1 switch (image->pixel_format)
651 {
652 case GATE_IMAGE_PIXELFORMAT_RGBA:
653 case GATE_IMAGE_PIXELFORMAT_RGB32: line_writer = &gate_color_convert_bgra_from_rgba_32; break;
654 case GATE_IMAGE_PIXELFORMAT_BGRA:
655 case GATE_IMAGE_PIXELFORMAT_BGR32: line_writer = &load_bgra_line; break;
656
657 1 case GATE_IMAGE_PIXELFORMAT_RGB24: line_writer = &gate_color_convert_bgra_from_rgb_24; break;
658 case GATE_IMAGE_PIXELFORMAT_BGR24: line_writer = &gate_color_convert_bgra_from_bgr_24; break;
659
660 case GATE_IMAGE_PIXELFORMAT_RGB555: line_writer = &gate_color_convert_bgra_from_rgb_15; break;
661 case GATE_IMAGE_PIXELFORMAT_RGB565: line_writer = &gate_color_convert_bgra_from_rgb_16; break;
662 case GATE_IMAGE_PIXELFORMAT_ARGB4: line_writer = &gate_color_convert_bgra_from_argb_16; break;
663 case GATE_IMAGE_PIXELFORMAT_YUV2: line_writer = &gate_color_convert_bgra_from_yuv2_16; break;
664
665 case GATE_IMAGE_PIXELFORMAT_PAL8: line_writer = &gate_color_convert_bgra_from_pal_8; break;
666 case GATE_IMAGE_PIXELFORMAT_GRAY8: line_writer = &gate_color_convert_bgra_from_gray_8; break;
667 default:
668 break;
669 }
670
671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (line_writer == NULL)
672 {
673 result = GATE_RESULT_INVALIDCONTENT;
674 break;
675 }
676
677
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (bmp_linelen > sizeof(buffer))
678 {
679 ptrbuffer = gate_mem_alloc(bmp_linelen);
680 if (ptrbuffer == NULL)
681 {
682 result = GATE_RESULT_OUTOFMEMORY;
683 break;
684 }
685 }
686
687 1 result = gate_stream_write_block(deststream, header, sizeof(header), &byteswritten);
688
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(result))
689 {
690 result = GATE_RESULT_INVALIDOUTPUT;
691 break;
692 }
693
694 1 ptrline = (gate_uint8_t const*)gate_rasterimage_get_line_ptr(image, (image->height - 1));
695
696
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1 times.
33 for (y = 0; y != image->height; ++y)
697 {
698 32 ptrbyte = (gate_uint8_t*)ptrbuffer;
699 32 line_writer(ptrbyte, ptrline, image->width);
700
701 32 result = gate_stream_write_block(deststream, ptrbuffer, bmp_linelen, &byteswritten);
702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (GATE_FAILED(result))
703 {
704 result = GATE_RESULT_INVALIDOUTPUT;
705 break;
706 }
707 32 GATE_DEBUG_ASSERT(bmp_linelen == byteswritten);
708
709 32 ptrline -= img_linelen;
710 }
711 } while (0);
712
713
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if ((ptrbuffer != &buffer[0]) && (ptrbuffer != NULL))
714 {
715 gate_mem_dealloc(ptrbuffer);
716 }
717
718 1 return result;
719 }
720
721