GCC Code Coverage Report


Directory: src/gate/
File: src/gate/graphics/gifimages.c
Date: 2025-09-22 22:57:09
Exec Total Coverage
Lines: 215 568 37.9%
Functions: 11 26 42.3%
Branches: 98 319 30.7%

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/gifimages.h"
30 #include "gate/results.h"
31 #include "gate/debugging.h"
32
33 #if defined(GATE_EXTLIB_GIFLIB)
34 # define GATE_GRAPHICS_GIF_USE_GIFLIB 1
35 #else
36 # define GATE_GRAPHICS_GIF_NO_IMPL 1
37 #endif
38
39
40 #if defined(GATE_GRAPHICS_GIF_USE_GIFLIB)
41
42 #include "gif_lib.h"
43
44 529 static int gate_gifimage_output(GifFileType* gif_file, GifByteType const* buffer, int length)
45 {
46 529 int ret = 0;
47 gate_result_t result;
48 529 gate_stream_t* stream = (gate_stream_t*)gif_file->UserData;
49 529 gate_size_t written = 0;
50
51
1/2
✓ Branch 0 taken 529 times.
✗ Branch 1 not taken.
529 if (stream != NULL)
52 {
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 529 times.
529 if (length == 0)
54 {
55 ✗ result = gate_stream_flush(stream);
56 ✗ GATE_DEBUG_TRACE_FAILED_RESULT(result);
57 }
58 else
59 {
60 529 result = gate_stream_write_block(stream, (char const*)buffer, length, &written);
61
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 529 times.
529 GATE_DEBUG_TRACE_FAILED_RESULT(result);
62
63
1/2
✓ Branch 0 taken 529 times.
✗ Branch 1 not taken.
529 if (GATE_SUCCEEDED(result))
64 {
65 529 ret = (int)written;
66 }
67 }
68 }
69 529 return ret;
70 }
71
72 531 static int gate_gifimage_input(GifFileType* gif_file, GifByteType* buffer, int length)
73 {
74 531 int ret = 0;
75 gate_result_t result;
76 531 gate_stream_t* stream = (gate_stream_t*)gif_file->UserData;
77 531 gate_size_t received = 0;
78
79
1/2
✓ Branch 0 taken 531 times.
✗ Branch 1 not taken.
531 if (stream != NULL)
80 {
81 531 result = gate_stream_read_block(stream, (char*)buffer, length, &received);
82
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 531 times.
531 GATE_DEBUG_TRACE_FAILED_RESULT(result);
83
84
1/2
✓ Branch 0 taken 531 times.
✗ Branch 1 not taken.
531 if (GATE_SUCCEEDED(result))
85 {
86 531 ret = (int)received;
87 }
88 }
89 531 return ret;
90 }
91
92
93 1 static gate_result_t gifimage_to_gateimage8(
94 GifImageDesc const* gif_image_descr, ColorMapObject const* gif_palette, unsigned transparent_index,
95 GifByteType const* gif_bits, gate_rasterimage_t* rasterimage)
96 {
97 gate_uint8_t* ptr_pixel8_index;
98 gate_color_rgb_t rgb;
99 unsigned x, y;
100
101
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_rasterimage_create(rasterimage, GATE_IMAGE_PIXELFORMAT_PAL8, gif_image_descr->Width, gif_image_descr->Height, NULL))
102 {
103 ✗ return GATE_RESULT_OUTOFMEMORY;
104 }
105
106
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1 times.
33 for (y = 0; y != gif_image_descr->Height; ++y)
107 {
108 32 ptr_pixel8_index = (gate_uint8_t*)gate_rasterimage_get_line_ptr(rasterimage, y);
109
2/2
✓ Branch 0 taken 2048 times.
✓ Branch 1 taken 32 times.
2080 for (x = 0; x != gif_image_descr->Width; ++x)
110 {
111 2048 unsigned colorindex = *gif_bits;
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2048 times.
2048 if (colorindex == transparent_index)
113 {
114 ✗ *ptr_pixel8_index = 255;
115 }
116 else
117 {
118 2048 rgb.r = gif_palette->Colors[colorindex].Red;
119 2048 rgb.g = gif_palette->Colors[colorindex].Green;
120 2048 rgb.b = gif_palette->Colors[colorindex].Blue;
121 2048 *ptr_pixel8_index = gate_color_palette_rgb8_index_from_color(rgb);
122 }
123 2048 ++gif_bits;
124 2048 ++ptr_pixel8_index;
125 }
126 }
127 1 return GATE_RESULT_OK;
128 }
129
130
131 ✗ static gate_result_t gifimage_to_gateimage32(
132 GifImageDesc const* gif_image_descr, ColorMapObject const* gif_palette, unsigned transparent_index,
133 GifByteType const* gif_bits, gate_rasterimage_t* rasterimage)
134 {
135 gate_color_t* ptr_pixel;
136 ✗ gate_size_t pixelcount = (gate_size_t)gif_image_descr->Width * (gate_size_t)gif_image_descr->Height;
137
138 ✗ if (NULL == gate_rasterimage_create(rasterimage, GATE_IMAGE_PIXELFORMAT_RGBA, gif_image_descr->Width, gif_image_descr->Height, NULL))
139 {
140 ✗ return GATE_RESULT_OUTOFMEMORY;
141 }
142 ✗ ptr_pixel = (gate_color_t*)gate_rasterimage_get_line_ptr(rasterimage, 0);
143
144 ✗ for (; pixelcount != 0; --pixelcount)
145 {
146 ✗ unsigned colorindex = *gif_bits;
147 ✗ ptr_pixel->r = gif_palette->Colors[colorindex].Red;
148 ✗ ptr_pixel->g = gif_palette->Colors[colorindex].Green;
149 ✗ ptr_pixel->b = gif_palette->Colors[colorindex].Blue;
150 ✗ if (colorindex == transparent_index)
151 {
152 ✗ ptr_pixel->a = 0;
153 }
154 else
155 {
156 ✗ ptr_pixel->a = 255;
157 }
158 ✗ ++gif_bits;
159 ✗ ++ptr_pixel;
160 }
161 ✗ return GATE_RESULT_OK;
162 }
163
164
165 1 static gate_result_t decode_one_gif_image(GifFileType* gif_file, gate_rasterimage_t* ptr_output_image,
166 unsigned* ptr_left, unsigned* ptr_top, gate_uint32_t* ptr_display_time_ms)
167 {
168 1 gate_result_t ret = GATE_RESULT_FAILED;
169 gate_size_t image_content_length;
170 1 GifPixelType* image_content = NULL;
171 1 unsigned transparent_color_index = 0xffff;
172 1 gate_uint32_t display_time_ms = 1000;
173 GraphicsControlBlock gcb;
174 1 gate_bool_t generate_pal8 = true;
175
176 for (;;)
177 1 {
178 GifRecordType gif_record_type;
179
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (GIF_ERROR == DGifGetRecordType(gif_file, &gif_record_type))
180 {
181 ✗ ret = GATE_RESULT_INVALIDHEADER;
182 ✗ break;
183 }
184
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (gif_record_type == TERMINATE_RECORD_TYPE)
186 {
187 ✗ ret = GATE_RESULT_ENDOFSTREAM;
188 ✗ break;
189 }
190
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 else if (gif_record_type == EXTENSION_RECORD_TYPE)
191 {
192 int gif_ext_code;
193 GifByteType* gif_ext_bytes;
194
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (GIF_ERROR == DGifGetExtension(gif_file, &gif_ext_code, &gif_ext_bytes))
195 {
196 ✗ ret = GATE_RESULT_INVALIDCONTENT;
197 ✗ break;
198 }
199
200 for (;;)
201 {
202
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (gif_ext_bytes == NULL)
203 {
204 /* end of extension reached */
205 1 ret = GATE_RESULT_OK;
206 1 break;
207 }
208
209
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GRAPHICS_EXT_FUNC_CODE == gif_ext_code)
210 {
211
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (GIF_OK == DGifExtensionToGCB(gif_ext_bytes[0], &gif_ext_bytes[1], &gcb))
212 {
213 1 display_time_ms = (gate_uint32_t)gcb.DelayTime * 10;
214 1 transparent_color_index = (unsigned)gcb.TransparentColor;
215 }
216 }
217
218
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (GIF_ERROR == DGifGetExtensionNext(gif_file, &gif_ext_bytes))
219 {
220 ✗ ret = GATE_RESULT_INVALIDCONTENT;
221 ✗ break;
222 }
223 }
224
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
225
226 1 continue;
227 }
228
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 else if (gif_record_type == IMAGE_DESC_RECORD_TYPE)
229 {
230 /* if(GIF_ERROR == DGifGetImageHeader(gif_file)) */
231
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (GIF_ERROR == DGifGetImageDesc(gif_file))
232 {
233 ✗ ret = GATE_RESULT_INVALIDCONTENT;
234 ✗ break;
235 }
236
237 1 image_content_length = (gate_size_t)gif_file->Image.Width * (gate_size_t)gif_file->Image.Height;
238 1 image_content = (GifPixelType*)gate_mem_alloc(image_content_length * sizeof(GifPixelType));
239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == image_content)
240 {
241 ✗ ret = GATE_RESULT_OUTOFMEMORY;
242 ✗ break;
243 }
244
245 1 ret = GATE_RESULT_OK;
246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (gif_file->Image.Interlace)
247 {
248 ✗ GifImageDesc const* desc = &gif_file->Image;
249 int i, j;
250 static const int InterlacedOffset[] = { 0, 4, 2, 1 };
251 static const int InterlacedJumps[] = { 8, 8, 4, 2 };
252
253 ✗ for (i = 0; (i < 4) && GATE_SUCCEEDED(ret); i++)
254 {
255 ✗ for (j = InterlacedOffset[i]; j < desc->Height; j += InterlacedJumps[i])
256 {
257 ✗ if (GIF_ERROR == DGifGetLine(gif_file, image_content + j * desc->Width, desc->Width))
258 {
259 ✗ ret = GATE_RESULT_INVALIDCONTENT;
260 ✗ break;
261 }
262 }
263 }
264 }
265 else
266 {
267 1 unsigned const height = gif_file->Image.Height;
268 1 unsigned const width = gif_file->Image.Width;
269 unsigned y;
270
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1 times.
33 for (y = 0; y < height; ++y)
271 {
272
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
32 if (GIF_ERROR == DGifGetLine(gif_file, &image_content[y * width], (int)width))
273 {
274 ✗ ret = GATE_RESULT_INVALIDCONTENT;
275 ✗ break;
276 }
277 }
278 }
279
280
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(ret))
281 {
282
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ptr_left) *ptr_left = gif_file->Image.Left;
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ptr_top) *ptr_top = gif_file->Image.Top;
284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ptr_display_time_ms) *ptr_display_time_ms = display_time_ms;
285
286
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (generate_pal8)
287 {
288 1 ret = gifimage_to_gateimage8(
289 1 &gif_file->Image,
290
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 gif_file->Image.ColorMap ? gif_file->Image.ColorMap : gif_file->SColorMap,
291 transparent_color_index,
292 image_content, ptr_output_image);
293 }
294 else
295 {
296 ✗ ret = gifimage_to_gateimage32(
297 ✗ &gif_file->Image,
298 ✗ gif_file->Image.ColorMap ? gif_file->Image.ColorMap : gif_file->SColorMap,
299 transparent_color_index,
300 image_content, ptr_output_image);
301 }
302 }
303 1 gate_mem_dealloc(image_content);
304 1 break;
305 }
306 else
307 {
308 ✗ ret = GATE_RESULT_INVALIDHEADER;
309 ✗ break;
310 }
311 } /* for(;;) */
312
313 1 return ret;
314 }
315
316
317 1 gate_result_t gate_gifimage_load(gate_stream_t* srcstream, gate_rasterimage_t* image, gate_enumint_t flags)
318 {
319 1 gate_result_t ret = GATE_RESULT_FAILED;
320 1 GifFileType* gif_file = NULL;
321 1 int gif_error_code = 0;
322 int gif_result;
323 1 gate_int32_t width = 0;
324 1 gate_int32_t height = 0;
325 GATE_UNUSED_ARG(flags);
326
327 do
328 {
329 1 gif_file = DGifOpen(srcstream, &gate_gifimage_input, &gif_error_code);
330
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == gif_file)
331 {
332 ✗ GATE_DEBUG_TRACE_MSG_VALUE("DGifOpen", gif_error_code);
333 ✗ if (gif_error_code == D_GIF_ERR_NOT_ENOUGH_MEM)
334 {
335 ✗ ret = GATE_RESULT_OUTOFMEMORY;
336 }
337 else
338 {
339 ✗ ret = GATE_RESULT_FAILED;
340 }
341 ✗ break;
342 }
343
344 1 width = (gate_int32_t)gif_file->SWidth;
345 1 height = (gate_int32_t)gif_file->SHeight;
346
347
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((width <= 0) || (height <= 0))
348 {
349 ✗ ret = GATE_RESULT_INVALIDDATA;
350 ✗ break;
351 }
352
353 #if 1
354 1 ret = decode_one_gif_image(gif_file, image, NULL, NULL, NULL);
355
356 #else
357 gif_result = DGifSlurp(gif_file);
358 if (gif_result != GIF_OK)
359 {
360 GATE_DEBUG_TRACE_MSG_VALUE("DGifSlurp", gif_result);
361 ret = GATE_RESULT_FAILED;
362 break;
363 }
364 else
365 {
366 GraphicsControlBlock gcb;
367 unsigned transparent_index = (unsigned)-1;
368 gif_result = DGifSavedExtensionToGCB(gif_file, 0, &gcb);
369 if(GIF_OK == gif_result)
370 {
371 if(gcb.TransparentColor >= 0)
372 {
373 transparent_index = (gate_size_t)gcb.TransparentColor;
374 }
375 }
376
377 ret = gifimage_to_gateimage32(
378 &gif_file->SavedImages->ImageDesc, gif_file->SColorMap, transparent_index,
379 gif_file->SavedImages->RasterBits, image);
380 }
381 #endif
382
383 } while (0);
384
385
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (NULL != gif_file)
386 {
387 1 gif_result = DGifCloseFile(gif_file, &gif_error_code);
388
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (gif_result != GIF_OK)
389 {
390 ✗ GATE_DEBUG_TRACE_MSG_VALUE("DGifCloseFile", gif_error_code);
391 }
392 }
393 1 return ret;
394 }
395
396 2048 static gate_size_t gate_gifimage_resolve_color_index(GifColorType const* pal_colors, gate_size_t pal_color_count, gate_color_t const* pixel)
397 {
398 gate_size_t index;
399 2048 GifByteType r = pixel->r;
400 2048 GifByteType g = pixel->g;
401 2048 GifByteType b = pixel->b;
402
2/2
✓ Branch 0 taken 3970 times.
✓ Branch 1 taken 2 times.
3972 for (index = 0; index != pal_color_count; ++index, ++pal_colors)
403 {
404
4/6
✓ Branch 0 taken 2046 times.
✓ Branch 1 taken 1924 times.
✓ Branch 2 taken 2046 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2046 times.
✗ Branch 5 not taken.
3970 if ((pal_colors->Red == r) && (pal_colors->Green == g) && (pal_colors->Blue == b))
405 {
406 2046 return index;
407 }
408 }
409 2 return pal_color_count;
410 }
411
412 #define GATE_GIFIMAGE_PIXEL_DIFF(a, b) ((a) > (b)) ? ((a) - (b)) : ((b) - (a))
413
414 2048 static gate_size_t gate_gifimage_resolve_next_color_index(GifColorType const* pal_colors, gate_size_t pal_color_count, gate_color_t const* pixel)
415 {
416 2048 gate_uint16_t best_diff = 0xffff;
417 2048 gate_size_t best_index = pal_color_count;
418 gate_uint16_t diff, diff_r, diff_g, diff_b;
419 gate_size_t index;
420 2048 GifByteType r = pixel->r;
421 2048 GifByteType g = pixel->g;
422 2048 GifByteType b = pixel->b;
423
1/2
✓ Branch 0 taken 3972 times.
✗ Branch 1 not taken.
3972 for (index = 0; index != pal_color_count; ++index, ++pal_colors)
424 {
425
2/2
✓ Branch 0 taken 1924 times.
✓ Branch 1 taken 2048 times.
3972 diff_r = GATE_GIFIMAGE_PIXEL_DIFF(pal_colors->Red, r);
426
2/2
✓ Branch 0 taken 1924 times.
✓ Branch 1 taken 2048 times.
3972 diff_g = GATE_GIFIMAGE_PIXEL_DIFF(pal_colors->Green, g);
427
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3972 times.
3972 diff_b = GATE_GIFIMAGE_PIXEL_DIFF(pal_colors->Blue, b);
428
429 3972 diff = diff_r + diff_g + diff_b;
430
2/2
✓ Branch 0 taken 2048 times.
✓ Branch 1 taken 1924 times.
3972 if (diff == 0)
431 {
432 2048 best_index = index;
433 2048 break;
434 }
435
1/2
✓ Branch 0 taken 1924 times.
✗ Branch 1 not taken.
1924 if (diff < best_diff)
436 {
437 1924 best_diff = diff;
438 1924 best_index = index;
439 }
440 }
441 2048 return best_index;
442 }
443
444
445
446 #define GATE_GIFIMAGE_TRANSPARENCY_LIMIT 64 /* 0 is full transparent, 255 is opaque */
447
448 1 static gate_size_t gate_gifimage_create_palette(GifColorType pal_colors[256], gate_rasterimage_t const* image, gate_color_converter_t line_writer, gate_uint8_t* ptr_buffer)
449 {
450 1 gate_size_t colors_used = 0;
451 gate_size_t color_index;
452 unsigned y, x;
453 gate_color_t const* ptr_pixel;
454 gate_uint8_t const* ptr_line;
455 gate_color_rgb_t default_palette[256];
456
457
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1 times.
33 for (y = 0; y != image->height; ++y)
458 {
459 32 ptr_line = (gate_uint8_t const*)gate_rasterimage_get_line_ptr(image, y);
460 32 line_writer(ptr_buffer, ptr_line, image->width);
461
462 32 ptr_pixel = (gate_color_t const*)ptr_buffer;
463
2/2
✓ Branch 0 taken 2048 times.
✓ Branch 1 taken 32 times.
2080 for (x = 0; x != image->width; ++x, ++ptr_pixel)
464 {
465
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2048 times.
2048 if (ptr_pixel->a <= GATE_GIFIMAGE_TRANSPARENCY_LIMIT)
466 {
467 ✗ continue;
468 }
469
470 2048 color_index = gate_gifimage_resolve_color_index(pal_colors, colors_used, ptr_pixel);
471
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2046 times.
2048 if (color_index == colors_used)
472 {
473
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (colors_used >= 256)
474 {
475 // too many colors -> switch generic palette
476 ✗ colors_used = gate_color_palette_rgb8_create(default_palette);
477 ✗ for (color_index = 0; color_index != colors_used; ++color_index)
478 {
479 ✗ pal_colors[color_index].Red = default_palette[color_index].r;
480 ✗ pal_colors[color_index].Green = default_palette[color_index].g;
481 ✗ pal_colors[color_index].Blue = default_palette[color_index].b;
482 }
483 ✗ break;
484 }
485
486 /* not found -> add it */
487 2 pal_colors[colors_used].Red = ptr_pixel->r;
488 2 pal_colors[colors_used].Green = ptr_pixel->g;
489 2 pal_colors[colors_used].Blue = ptr_pixel->b;
490 2 ++colors_used;
491 }
492 }
493 }
494 1 return colors_used;
495 }
496
497 32 static gate_result_t encode_color_line(gate_color_t const* pixels, gate_size_t length,
498 GifColorType const* ptr_palette, gate_size_t palette_length, gate_size_t transparent_index,
499 GifPixelType* ptr_line)
500 {
501 gate_size_t index;
502
503
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 if (transparent_index == palette_length - 1)
504 {
505 32 --palette_length;
506 }
507
508
2/2
✓ Branch 0 taken 2048 times.
✓ Branch 1 taken 32 times.
2080 while (length-- != 0)
509 {
510
2/4
✓ Branch 0 taken 2048 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2048 times.
2048 if ((transparent_index < 256) && (pixels->a < GATE_GIFIMAGE_TRANSPARENCY_LIMIT))
511 {
512 ✗ index = transparent_index;
513 }
514 2048 index = gate_gifimage_resolve_next_color_index(ptr_palette, palette_length, pixels);
515
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2048 times.
2048 if (index == palette_length)
516 {
517 ✗ index = transparent_index;
518 }
519 2048 *ptr_line = (gate_uint8_t)index;
520 2048 ++ptr_line;
521 2048 ++pixels;
522 }
523 32 return GATE_RESULT_OK;
524 }
525
526 ✗ static void copy_rgba(gate_uint8_t* dst, gate_uint8_t const* src, gate_size_t count)
527 {
528 ✗ gate_mem_copy(dst, src, count * 4);
529 ✗ }
530
531 1 static gate_color_converter_t get_line_writer(gate_rasterimage_t const* image)
532 {
533
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)
534 {
535 ✗ case GATE_IMAGE_PIXELFORMAT_RGBA:
536 ✗ case GATE_IMAGE_PIXELFORMAT_RGB32: return &copy_rgba; break;
537 ✗ case GATE_IMAGE_PIXELFORMAT_BGRA:
538 ✗ case GATE_IMAGE_PIXELFORMAT_BGR32: return &gate_color_convert_rgba_from_bgra_32; break;
539
540 1 case GATE_IMAGE_PIXELFORMAT_RGB24: return &gate_color_convert_rgba_from_rgb_24; break;
541 ✗ case GATE_IMAGE_PIXELFORMAT_BGR24: return &gate_color_convert_rgba_from_bgr_24; break;
542
543 ✗ case GATE_IMAGE_PIXELFORMAT_RGB555: return &gate_color_convert_rgba_from_rgb_15; break;
544 ✗ case GATE_IMAGE_PIXELFORMAT_RGB565: return &gate_color_convert_rgba_from_rgb_16; break;
545 ✗ case GATE_IMAGE_PIXELFORMAT_ARGB4: return &gate_color_convert_rgba_from_argb_16; break;
546 ✗ case GATE_IMAGE_PIXELFORMAT_YUV2: return &gate_color_convert_rgba_from_yuv2_16; break;
547
548 ✗ case GATE_IMAGE_PIXELFORMAT_PAL8: return &gate_color_convert_rgba_from_pal_8; break;
549 ✗ case GATE_IMAGE_PIXELFORMAT_GRAY8: return &gate_color_convert_rgba_from_gray_8; break;
550 ✗ default: return NULL;
551 }
552 }
553
554 1 gate_result_t gate_gifimage_save(gate_rasterimage_t const* image, gate_stream_t* deststream, gate_enumint_t flags)
555 {
556 1 gate_result_t ret = GATE_RESULT_FAILED;
557 1 GifFileType* gif_file = NULL;
558 1 ColorMapObject* color_map = NULL;
559 1 int error_code = 0;
560 int gif_result;
561 GifColorType pal_colors[256];
562 1 gate_size_t pal_color_count = 256;
563 1 gate_size_t transparent_index = 0xffff;
564 gate_uint8_t gif_extension[4];
565 1 int width = 0;
566 1 int height = 0;
567 int y;
568 1 gate_uint8_t const* ptr_image_pixels = NULL;
569 GifPixelType line[2048];
570 1 GifPixelType* ptr_gif_line = &line[0];
571 unsigned char buffer[8192];
572 1 unsigned char* ptr_buffer = &buffer[0];
573 gate_size_t buffer_required_len;
574 1 gate_color_converter_t line_writer = NULL;
575
576 GATE_UNUSED_ARG(flags);
577 do
578 {
579
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((image == NULL) || (deststream == NULL))
580 {
581 ✗ GATE_DEBUG_TRACE("gate_gifimage_save() invalid arguments");
582 ✗ ret = GATE_RESULT_INVALIDARG;
583 ✗ break;
584 }
585 1 width = (int)gate_rasterimage_width(image);
586 1 height = (int)gate_rasterimage_height(image);
587
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((width == 0) || (height == 0))
588 {
589 ✗ GATE_DEBUG_TRACE("gate_gifimage_save() invalid dimension arguments");
590 ✗ ret = GATE_RESULT_INVALIDARG;
591 ✗ break;
592 }
593
594 1 line_writer = get_line_writer(image);
595
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (line_writer == NULL)
596 {
597 ✗ ret = GATE_RESULT_NOTSUPPORTED;
598 ✗ break;
599 }
600
601
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (width > sizeof(line) / sizeof(line[0]))
602 {
603 ✗ ptr_gif_line = gate_mem_alloc((gate_size_t)(sizeof(GifPixelType) * width));
604 ✗ if (ptr_gif_line == NULL)
605 {
606 ✗ ret = GATE_RESULT_OUTOFMEMORY;
607 ✗ break;
608 }
609 }
610
611 1 buffer_required_len = (gate_size_t)image->width * 4;
612
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (buffer_required_len > sizeof(buffer))
613 {
614 ✗ ptr_buffer = gate_mem_alloc(buffer_required_len);
615 ✗ if (NULL == ptr_buffer)
616 {
617 ✗ ret = GATE_RESULT_OUTOFMEMORY;
618 ✗ break;
619 }
620 }
621
622 /* create color palette and fill it to 256 entries */
623 1 pal_color_count = gate_gifimage_create_palette(pal_colors, image, line_writer, ptr_buffer);
624
2/2
✓ Branch 0 taken 253 times.
✓ Branch 1 taken 1 times.
254 while (pal_color_count < 255)
625 {
626 253 pal_colors[pal_color_count].Red = 0xde;
627 253 pal_colors[pal_color_count].Green = 0xad;
628 253 pal_colors[pal_color_count].Blue = 0xbf;
629 253 ++pal_color_count;
630 }
631
632
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (pal_color_count < 256)
633 {
634 1 transparent_index = pal_color_count;
635 1 pal_colors[transparent_index].Red = 0xba;
636 1 pal_colors[transparent_index].Green = 0xdc;
637 1 pal_colors[transparent_index].Blue = 0x01; // badc01 -> bad-col 15 c001
638 1 ++pal_color_count;
639 }
640
641 1 color_map = GifMakeMapObject((int)pal_color_count, pal_colors);
642
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == color_map)
643 {
644 ✗ GATE_DEBUG_TRACE("GifMakeMapObject() failed");
645 ✗ ret = GATE_RESULT_OUTOFRESOURCES;
646 ✗ break;
647 }
648
649 /* open GIF output stream */
650 1 gif_file = EGifOpen(deststream, &gate_gifimage_output, &error_code);
651
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == gif_file)
652 {
653 ✗ GATE_DEBUG_TRACE_MSG_VALUE("EGifOpen() failed", error_code);
654 ✗ ret = (error_code == E_GIF_ERR_NOT_ENOUGH_MEM) ? GATE_RESULT_OUTOFMEMORY : GATE_RESULT_FAILED;
655 ✗ break;
656 }
657
658 /* write GIF screen description */
659 1 gif_result = EGifPutScreenDesc(gif_file, gate_rasterimage_width(image), gate_rasterimage_height(image), 8, 0, color_map);
660
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GIF_OK != gif_result)
661 {
662 ✗ GATE_DEBUG_TRACE_MSG_VALUE("EGifPutScreenDesc() failed", gif_result);
663 ✗ ret = GATE_RESULT_FAILED;
664 ✗ break;
665 }
666
667
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (transparent_index < 256)
668 {
669 /* writing extension block with transparent color */
670 1 gif_extension[0] = 1;
671 1 gif_extension[1] = 0;
672 1 gif_extension[2] = 0;
673 1 gif_extension[3] = (gate_uint8_t)transparent_index;
674 1 gif_result = EGifPutExtension(gif_file, GRAPHICS_EXT_FUNC_CODE, sizeof(gif_extension), gif_extension);
675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GIF_OK != gif_result)
676 {
677 ✗ GATE_DEBUG_TRACE_MSG_VALUE("EGifPutExtension() failed", gif_result);
678 ✗ ret = GATE_RESULT_FAILED;
679 ✗ break;
680 }
681 1 gif_file->SBackGroundColor = (GifWord)transparent_index;
682 }
683
684 /* write GIF image description */
685 1 gif_result = EGifPutImageDesc(gif_file, 0, 0, width, height, 0, color_map);
686
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GIF_OK != gif_result)
687 {
688 ✗ GATE_DEBUG_TRACE_MSG_VALUE("EGifPutImageDesc() failed", gif_result);
689 ✗ ret = GATE_RESULT_FAILED;
690 ✗ break;
691 }
692
693 1 ret = GATE_RESULT_OK;
694
695 /* write GIF image data line by line */
696
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1 times.
33 for (y = 0; y != height; ++y)
697 {
698 32 ptr_image_pixels = (gate_uint8_t const*)gate_rasterimage_get_line_ptr(image, y);
699 32 line_writer(ptr_buffer, ptr_image_pixels, image->width);
700 /* create pixel to color-index mapping for one line */
701 32 ret = encode_color_line((gate_color_t const*)ptr_buffer, width, pal_colors, pal_color_count, transparent_index, ptr_gif_line);
702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (GATE_FAILED(ret))
703 {
704 ✗ break;
705 }
706
707 /* write index line */
708 32 gif_result = EGifPutLine(gif_file, ptr_gif_line, (int)width);
709
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
32 if (GIF_OK != gif_result)
710 {
711 ✗ ret = GATE_RESULT_INVALIDCONTENT;
712 ✗ break;
713 }
714
715 }
716
717 } while (0);
718
719
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (color_map != NULL)
720 {
721 1 GifFreeMapObject(color_map);
722 }
723
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (gif_file != NULL)
724 {
725 1 EGifCloseFile(gif_file, &error_code);
726 }
727
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((ptr_gif_line != NULL) && (ptr_gif_line != &line[0]))
728 {
729 ✗ gate_mem_dealloc(ptr_gif_line);
730 }
731
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if ((ptr_buffer != NULL) && (ptr_buffer != &buffer[0]))
732 {
733 ✗ gate_mem_dealloc(ptr_buffer);
734 }
735
736 1 return ret;
737 }
738
739
740 typedef struct gifani_writer_class
741 {
742 ColorMapObject* color_map_ptr;
743 unsigned transparent_index;
744 gate_stream_t* out_stream;
745 GifFileType* gif_file;
746 unsigned width;
747 unsigned height;
748
749 } gifani_writer_t;
750
751 ✗ static void gifani_writer_release(gifani_writer_t* impl)
752 {
753 ✗ int gif_err = 0;
754
755 ✗ if (impl->gif_file)
756 {
757 ✗ EGifCloseFile(impl->gif_file, &gif_err);
758 ✗ impl->gif_file = NULL;
759 }
760
761 ✗ if (impl->out_stream)
762 {
763 ✗ gate_object_release(impl->out_stream);
764 ✗ impl->out_stream = NULL;
765 }
766 ✗ }
767
768 ✗ gate_result_t gate_gifanimation_writer_destroy(gate_gifanimation_writer_t animation)
769 {
770 ✗ gifani_writer_t* impl = (gifani_writer_t*)animation;
771 ✗ if (impl)
772 {
773 ✗ gifani_writer_release(impl);
774
775 ✗ if (impl->color_map_ptr)
776 {
777 ✗ GifFreeMapObject(impl->color_map_ptr);
778 ✗ impl->color_map_ptr = NULL;
779 }
780 ✗ gate_mem_dealloc(animation);
781 }
782 ✗ return GATE_RESULT_OK;
783 }
784
785 ✗ gate_result_t gate_gifanimation_writer_create(gate_gifanimation_writer_t* ptr_animation)
786 {
787 ✗ gate_result_t ret = GATE_RESULT_FAILED;
788 gate_color_rgb_t default_palette[256];
789 GifColorType gif_palette[256];
790 gate_size_t ndx;
791 gate_size_t colors_used;
792 ✗ gifani_writer_t* impl = NULL;
793
794 do
795 {
796 ✗ impl = (gifani_writer_t*)gate_mem_alloc(sizeof(gifani_writer_t));
797 ✗ if (impl == NULL)
798 {
799 ✗ ret = GATE_RESULT_OUTOFMEMORY;
800 ✗ break;
801 }
802 ✗ gate_mem_clear(impl, sizeof(gifani_writer_t));
803
804
805 /* generate default palette */
806 ✗ colors_used = gate_color_palette_rgb8_create(default_palette);
807 ✗ for (ndx = 0; ndx != colors_used; ++ndx)
808 {
809 ✗ gif_palette[ndx].Red = default_palette[ndx].r;
810 ✗ gif_palette[ndx].Green = default_palette[ndx].g;
811 ✗ gif_palette[ndx].Blue = default_palette[ndx].b;
812 }
813 ✗ while (colors_used < 255)
814 {
815 ✗ gif_palette[colors_used].Red = 0xde;
816 ✗ gif_palette[colors_used].Green = 0xad;
817 ✗ gif_palette[colors_used].Blue = 0xbf;
818 ✗ ++colors_used;
819 }
820 ✗ if (colors_used < 256)
821 {
822 ✗ impl->transparent_index = (unsigned)colors_used;
823 ✗ gif_palette[colors_used].Red = 0xba;
824 ✗ gif_palette[colors_used].Green = 0xdc;
825 ✗ gif_palette[colors_used].Blue = 0x01;
826 ✗ ++colors_used;
827 }
828 ✗ impl->color_map_ptr = GifMakeMapObject((int)colors_used, gif_palette);
829 ✗ if (impl->color_map_ptr == NULL)
830 {
831 ✗ ret = GATE_RESULT_OUTOFMEMORY;
832 ✗ break;
833 }
834
835 ✗ ret = GATE_RESULT_OK;
836 } while (0);
837
838 ✗ if (GATE_FAILED(ret) || (ptr_animation == NULL))
839 {
840 ✗ gate_gifanimation_writer_destroy(impl);
841 }
842 else
843 {
844 ✗ *ptr_animation = (void*)impl;
845 }
846 ✗ return ret;
847 }
848
849
850 ✗ gate_result_t gate_gifanimation_writer_start(gate_gifanimation_writer_t animation, gate_stream_t* outstream, unsigned width, unsigned height, gate_enumint_t flags)
851 {
852 ✗ gate_result_t ret = GATE_RESULT_FAILED;
853 ✗ gifani_writer_t* impl = (gifani_writer_t*)animation;
854 ✗ int gif_result = 0;
855
856 GATE_UNUSED_ARG(flags);
857 ✗ GATE_DEBUG_ASSERT(impl != NULL);
858
859 do
860 {
861 ✗ if (!outstream || !width || !height)
862 {
863 ✗ ret = GATE_RESULT_INVALIDARG;
864 ✗ break;
865 }
866
867 ✗ gifani_writer_release(impl);
868
869 ✗ impl->out_stream = outstream;
870 ✗ gate_object_retain(impl->out_stream);
871 ✗ impl->width = width;
872 ✗ impl->height = height;
873
874 ✗ impl->gif_file = EGifOpen(impl->out_stream, &gate_gifimage_output, &gif_result);
875 ✗ if (impl->gif_file == NULL)
876 {
877 ✗ ret = (gif_result == E_GIF_ERR_NOT_ENOUGH_MEM) ? GATE_RESULT_OUTOFMEMORY : GATE_RESULT_FAILED;;
878 ✗ break;
879 }
880
881 ✗ gif_result = EGifPutScreenDesc(impl->gif_file, (int)impl->width, (int)impl->height, 8, 0, impl->color_map_ptr);
882 ✗ if (GIF_OK != gif_result)
883 {
884 ✗ ret = GATE_RESULT_INVALIDHEADER;
885 ✗ break;
886 }
887
888 ✗ ret = GATE_RESULT_OK;
889 } while (0);
890
891 ✗ if (GATE_FAILED(ret))
892 {
893 ✗ gifani_writer_release(impl);
894 }
895
896 ✗ return ret;
897 }
898
899 ✗ gate_result_t gate_gifanimation_writer_import(gate_gifanimation_writer_t animation, gate_stream_t* instream, gate_enumint_t flags)
900 {
901 ✗ gate_result_t ret = GATE_RESULT_FAILED;
902 ✗ gifani_writer_t* impl = (gifani_writer_t*)animation;
903 ✗ GifFileType* gif_file = NULL;
904 int gif_result;
905 ✗ int err_code = 0;
906 int ndx, block_ndx;
907 GifWord cy;
908
909 GATE_UNUSED_ARG(flags);
910
911 do
912 {
913 ✗ gif_file = DGifOpen(instream, &gate_gifimage_input, &err_code);
914 ✗ if (NULL == gif_file)
915 {
916 ✗ GATE_DEBUG_TRACE_MSG_VALUE("DGifOpen", err_code);
917 ✗ if (err_code == D_GIF_ERR_NOT_ENOUGH_MEM)
918 {
919 ✗ ret = GATE_RESULT_OUTOFMEMORY;
920 }
921 else
922 {
923 ✗ ret = GATE_RESULT_FAILED;
924 }
925 ✗ break;
926 }
927
928 ✗ gif_result = DGifSlurp(gif_file);
929 ✗ if (gif_result != GIF_OK)
930 {
931 ✗ GATE_DEBUG_TRACE_MSG_VALUE("DGifSlurp", gif_result);
932 ✗ ret = GATE_RESULT_FAILED;
933 ✗ break;
934 }
935
936 ✗ ret = GATE_RESULT_OK;
937 ✗ for (ndx = 0; ndx < gif_file->ImageCount; ++ndx)
938 {
939 /* read frame from input GIF */
940 ✗ SavedImage* ptr_frame = &gif_file->SavedImages[ndx];
941 ✗ GifImageDesc* ptr_frame_desc = &ptr_frame->ImageDesc;
942 ✗ ColorMapObject* ptr_frame_color_map = ptr_frame_desc->ColorMap ? ptr_frame_desc->ColorMap : gif_file->SColorMap;
943
944 /* add frame's extension blocks to output GIF */
945 ✗ for (block_ndx = 0; block_ndx < ptr_frame->ExtensionBlockCount; ++block_ndx)
946 {
947 ✗ ExtensionBlock* eb = &ptr_frame->ExtensionBlocks[block_ndx];
948 ✗ EGifPutExtension(impl->gif_file, eb->Function, eb->ByteCount, eb->Bytes);
949 }
950
951 /* add frame image description to output GIF */
952 ✗ gif_result = EGifPutImageDesc(impl->gif_file, ptr_frame_desc->Left, ptr_frame_desc->Top, ptr_frame_desc->Width, ptr_frame_desc->Height, ptr_frame_desc->Interlace, ptr_frame_color_map);
953 ✗ if (gif_result != GIF_OK)
954 {
955 ✗ ret = GATE_RESULT_FAILED;
956 ✗ break;
957 }
958
959 /* add frame image contents to output GIF */
960 ✗ for (cy = 0; cy < ptr_frame_desc->Height; ++cy)
961 {
962 ✗ gif_result = EGifPutLine(impl->gif_file, &ptr_frame->RasterBits[cy * ptr_frame_desc->Width], (int)ptr_frame_desc->Width);
963 ✗ if (GIF_OK != gif_result)
964 {
965 ✗ ret = GATE_RESULT_INVALIDCONTENT;
966 ✗ break;
967 }
968 }
969 ✗ GATE_BREAK_IF_FAILED(ret);
970 }
971 ✗ GATE_BREAK_IF_FAILED(ret);
972
973 /* success*/
974 ✗ ret = GATE_RESULT_OK;
975 } while (0);
976
977 ✗ if (gif_file != NULL)
978 {
979 ✗ DGifCloseFile(gif_file, &err_code);
980 }
981
982 ✗ return ret;
983 }
984
985 ✗ gate_result_t gate_gifanimation_writer_add_frame(gate_gifanimation_writer_t animation, gate_rasterimage_t const* image,
986 unsigned x, unsigned y, gate_uint32_t display_time_ms, gate_enumint_t flags)
987 {
988 ✗ gate_result_t ret = GATE_RESULT_FAILED;
989 ✗ gifani_writer_t* impl = (gifani_writer_t*)animation;
990 unsigned image_width, image_height;
991 int gif_result;
992 unsigned cy;
993 GraphicsControlBlock gcb;
994 GifByteType gif_ext[32];
995 size_t gif_ext_used;
996 GifByteType line[8192];
997 gate_color_converter_t line_writer;
998
999 GATE_UNUSED_ARG(flags);
1000
1001 do
1002 {
1003 ✗ line_writer = get_line_writer(image);
1004 ✗ if (line_writer == NULL)
1005 {
1006 ✗ ret = GATE_RESULT_NOTSUPPORTED;
1007 ✗ break;
1008 }
1009
1010 ✗ if ((x >= impl->width) || (y >= impl->height))
1011 {
1012 ✗ ret = GATE_RESULT_INVALIDINPUT;
1013 ✗ break;
1014 }
1015
1016
1017 ✗ image_width = gate_rasterimage_width(image);
1018 ✗ image_height = gate_rasterimage_height(image);
1019
1020 ✗ if (image_width == 0 || image_height == 0)
1021 {
1022 ✗ ret = GATE_RESULT_INVALIDINPUT;
1023 ✗ break;
1024 }
1025 ✗ if (x + image_width > impl->width)
1026 {
1027 ✗ image_width = impl->width - x;
1028 }
1029 ✗ if (y + image_height > impl->height)
1030 {
1031 ✗ image_height = impl->height - y;
1032 }
1033
1034 /* write transparent color extension*/
1035 ✗ gcb.DisposalMode = DISPOSE_DO_NOT;
1036 ✗ gcb.UserInputFlag = 0;
1037 ✗ gcb.DelayTime = (display_time_ms + 5) / 10;
1038 ✗ gcb.TransparentColor = (int)impl->transparent_index;
1039 ✗ gif_ext_used = EGifGCBToExtension(&gcb, gif_ext);
1040 ✗ gif_result = EGifPutExtension(impl->gif_file, GRAPHICS_EXT_FUNC_CODE, (int)gif_ext_used, gif_ext);
1041 ✗ if (GIF_OK != gif_result)
1042 {
1043 ✗ ret = GATE_RESULT_INVALIDHEADER;
1044 ✗ break;
1045 }
1046
1047 /* write image description */
1048 ✗ gif_result = EGifPutImageDesc(impl->gif_file, (int)x, (int)y, (int)image_width, (int)image_height, 0, impl->color_map_ptr);
1049 ✗ if (GIF_OK != gif_result)
1050 {
1051 ✗ GATE_DEBUG_TRACE_MSG_VALUE("EGifPutImageDesc() failed", gif_result);
1052 ✗ ret = GATE_RESULT_FAILED;
1053 ✗ break;
1054 }
1055
1056 /* write image pixels */
1057 ✗ for (cy = 0; cy < image_height; ++cy)
1058 {
1059 ✗ gate_uint8_t const* ptr_image_pixels = (gate_uint8_t const*)gate_rasterimage_get_line_ptr(image, cy);
1060 ✗ line_writer(line, ptr_image_pixels, image->width);
1061 /* create pixel to color-index mapping for one line */
1062 ✗ ret = encode_color_line((gate_color_t const*)line, image_width,
1063 ✗ impl->color_map_ptr->Colors, impl->transparent_index + 1, impl->transparent_index, line);
1064 ✗ if (GATE_FAILED(ret))
1065 {
1066 ✗ break;
1067 }
1068
1069 /* write index line */
1070 ✗ gif_result = EGifPutLine(impl->gif_file, line, (int)image_width);
1071 ✗ if (GIF_OK != gif_result)
1072 {
1073 ✗ ret = GATE_RESULT_INVALIDCONTENT;
1074 ✗ break;
1075 }
1076 }
1077 ✗ ret = GATE_RESULT_OK;
1078 } while (0);
1079 ✗ return ret;
1080 }
1081
1082 ✗ gate_result_t gate_gifanimation_writer_finish(gate_gifanimation_writer_t animation, gate_enumint_t flags)
1083 {
1084 ✗ gifani_writer_t* impl = (gifani_writer_t*)animation;
1085
1086 ✗ if (GATE_FLAG_ENABLED(flags, GATE_GIFANIMATION_FLAG_LOOP))
1087 {
1088 static char const nsle[12] = "NETSCAPE2.0";
1089 static char const subblock[3] = { 1, 0, 0 }; // Loop forever
1090 ✗ EGifPutExtension(impl->gif_file, APPLICATION_EXT_FUNC_CODE, 11, &nsle[0]);
1091 ✗ EGifPutExtension(impl->gif_file, CONTINUE_EXT_FUNC_CODE, 3, subblock);
1092 }
1093
1094 ✗ if (impl->out_stream)
1095 {
1096 ✗ gate_stream_flush(impl->out_stream);
1097 }
1098
1099 ✗ return GATE_RESULT_OK;
1100 }
1101
1102
1103
1104
1105 typedef struct gifani_reader_class
1106 {
1107 GifFileType* gif_file; /**< giflib handle */
1108 gate_stream_t* in_stream; /**< input stream reference to read from */
1109 int err_code; /**< stored last error / error-mode indicator */
1110 gate_bool_t end_reached; /**< end-of-stream reached */
1111 } gifani_reader_t;
1112
1113
1114 ✗ static void gifani_reader_reset(gifani_reader_t* impl)
1115 {
1116 ✗ int gif_err_code = 0;
1117 ✗ int gif_result = 0;
1118 ✗ if (impl->gif_file)
1119 {
1120 ✗ gif_result = DGifCloseFile(impl->gif_file, &gif_err_code);
1121 ✗ impl->gif_file = NULL;
1122 }
1123 ✗ if (impl->in_stream)
1124 {
1125 ✗ gate_object_release(impl->in_stream);
1126 ✗ impl->in_stream = NULL;
1127 }
1128 ✗ impl->err_code = 0;
1129 ✗ }
1130
1131
1132 ✗ static void gifani_reader_release(gifani_reader_t* impl)
1133 {
1134 ✗ gifani_reader_reset(impl);
1135 ✗ gate_mem_dealloc(impl);
1136 ✗ }
1137
1138 ✗ gate_result_t gate_gifanimation_reader_create(gate_gifanimation_reader_t* ptr_animation)
1139 {
1140 ✗ gate_result_t ret = GATE_RESULT_FAILED;
1141 ✗ gifani_reader_t* impl = NULL;
1142
1143 do
1144 {
1145 ✗ impl = (gifani_reader_t*)gate_mem_alloc(sizeof(gifani_reader_t));
1146 ✗ if (impl == NULL)
1147 {
1148 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1149 ✗ break;
1150 }
1151 ✗ gate_mem_clear(impl, sizeof(gifani_reader_t));
1152
1153
1154 /* success case */
1155 ✗ ret = GATE_RESULT_OK;
1156 ✗ if (ptr_animation)
1157 {
1158 ✗ *ptr_animation = impl;
1159 ✗ impl = NULL;
1160 }
1161 } while (0);
1162
1163 ✗ if (impl != NULL)
1164 {
1165 ✗ gifani_reader_release(impl);
1166 }
1167 ✗ return ret;
1168 }
1169
1170 ✗ gate_result_t gate_gifanimation_reader_load(gate_gifanimation_reader_t animation, gate_stream_t* instream,
1171 unsigned* ptr_width, unsigned* ptr_height, gate_enumint_t* ptr_flags)
1172 {
1173 ✗ gate_result_t ret = GATE_RESULT_FAILED;
1174 ✗ gifani_reader_t* impl = (gifani_reader_t*)animation;
1175 ✗ int gif_err_code = 0;
1176
1177 do
1178 {
1179 ✗ if (!impl || !instream)
1180 {
1181 ✗ ret = GATE_RESULT_INVALIDARG;
1182 ✗ break;
1183 }
1184
1185 ✗ gifani_reader_reset(impl);
1186
1187
1188 ✗ impl->gif_file = DGifOpen(instream, &gate_gifimage_input, &gif_err_code);
1189 ✗ if (NULL == impl->gif_file)
1190 {
1191 ✗ GATE_DEBUG_TRACE_MSG_VALUE("DGifOpen", gif_err_code);
1192 ✗ if (gif_err_code == D_GIF_ERR_NOT_ENOUGH_MEM)
1193 {
1194 ✗ ret = GATE_RESULT_OUTOFMEMORY;
1195 }
1196 else
1197 {
1198 ✗ ret = GATE_RESULT_FAILED;
1199 }
1200 ✗ break;
1201 }
1202
1203 /* success case*/
1204 ✗ ret = GATE_RESULT_OK;
1205
1206 ✗ impl->in_stream = instream;
1207 ✗ gate_object_retain(impl->in_stream);
1208
1209 ✗ if (ptr_width)
1210 {
1211 ✗ *ptr_width = (unsigned)impl->gif_file->SWidth;
1212 }
1213 ✗ if (ptr_height)
1214 {
1215 ✗ *ptr_height = (unsigned)impl->gif_file->SHeight;
1216 }
1217 ✗ if (ptr_flags)
1218 {
1219 ✗ *ptr_flags = 0; /* TODO */
1220 }
1221 } while (0);
1222
1223 ✗ return ret;
1224 }
1225
1226 ✗ gate_result_t gate_gifanimation_reader_get_frame(gate_gifanimation_reader_t animation,
1227 gate_rasterimage_t* ptr_output_image, unsigned* ptr_x, unsigned* ptr_y,
1228 gate_uint32_t* ptr_display_time_ms)
1229 {
1230 ✗ gate_result_t ret = GATE_RESULT_FAILED;
1231 ✗ gifani_reader_t* impl = (gifani_reader_t*)animation;
1232
1233 ✗ if (impl->end_reached)
1234 {
1235 ✗ ret = GATE_RESULT_ENDOFSTREAM;
1236 }
1237 else
1238 {
1239 ✗ ret = decode_one_gif_image(impl->gif_file, ptr_output_image, ptr_x, ptr_y, ptr_display_time_ms);
1240 ✗ if (ret == GATE_RESULT_ENDOFSTREAM)
1241 {
1242 ✗ impl->end_reached = true;
1243 }
1244
1245 }
1246 ✗ return ret;
1247 }
1248
1249 ✗ gate_result_t gate_gifanimation_reader_destroy(gate_gifanimation_reader_t animation)
1250 {
1251 ✗ gifani_reader_t* impl = (gifani_reader_t*)animation;
1252 ✗ if (!impl)
1253 {
1254 ✗ return GATE_RESULT_INVALIDARG;
1255 }
1256 ✗ gifani_reader_release(impl);
1257 ✗ return GATE_RESULT_OK;
1258 }
1259
1260
1261
1262 #endif /* GATE_GRAPHICS_GIF_USE_GIFLIB */
1263
1264
1265
1266 #if defined(GATE_GRAPHICS_GIF_NO_IMPL)
1267
1268 gate_result_t gate_gifimage_load(gate_stream_t* srcstream, gate_rasterimage_t* image, gate_enumint_t flags)
1269 {
1270 (void)srcstream;
1271 (void)image;
1272 (void)flags;
1273 return GATE_RESULT_NOTIMPLEMENTED;
1274 }
1275
1276 gate_result_t gate_gifimage_save(gate_rasterimage_t const* image, gate_stream_t* deststream, gate_enumint_t flags)
1277 {
1278 (void)image;
1279 (void)deststream;
1280 (void)flags;
1281 return GATE_RESULT_NOTIMPLEMENTED;
1282 }
1283
1284
1285 gate_result_t gate_gifanimation_writer_create(gate_gifanimation_writer_t* ptr_animation)
1286 {
1287 return GATE_RESULT_NOTIMPLEMENTED;
1288 }
1289
1290 gate_result_t gate_gifanimation_writer_start(gate_gifanimation_writer_t animation, gate_stream_t* outstream,
1291 unsigned width, unsigned height, gate_enumint_t flags)
1292 {
1293 return GATE_RESULT_NOTIMPLEMENTED;
1294 }
1295
1296 gate_result_t gate_gifanimation_writer_import(gate_gifanimation_writer_t animation, gate_stream_t* instream, gate_enumint_t flags)
1297 {
1298 return GATE_RESULT_NOTIMPLEMENTED;
1299 }
1300
1301 gate_result_t gate_gifanimation_writer_add_frame(gate_gifanimation_writer_t animation,
1302 gate_rasterimage_t const* image, unsigned x, unsigned y,
1303 gate_uint32_t display_time_ms, gate_enumint_t flags)
1304 {
1305 return GATE_RESULT_NOTIMPLEMENTED;
1306 }
1307
1308 gate_result_t gate_gifanimation_writer_finish(gate_gifanimation_writer_t animation, gate_enumint_t flags)
1309 {
1310 return GATE_RESULT_NOTIMPLEMENTED;
1311 }
1312
1313
1314 gate_result_t gate_gifanimation_writer_destroy(gate_gifanimation_writer_t animation)
1315 {
1316 return GATE_RESULT_NOTIMPLEMENTED;
1317 }
1318
1319 gate_result_t gate_gifanimation_reader_create(gate_gifanimation_reader_t* ptr_animation)
1320 {
1321 return GATE_RESULT_NOTIMPLEMENTED;
1322 }
1323
1324 gate_result_t gate_gifanimation_reader_load(gate_gifanimation_reader_t animation, gate_stream_t* instream,
1325 unsigned* ptr_width, unsigned* ptr_height, gate_enumint_t* ptr_flags)
1326 {
1327 return GATE_RESULT_NOTIMPLEMENTED;
1328 }
1329
1330 gate_result_t gate_gifanimation_reader_get_frame(gate_gifanimation_reader_t animation,
1331 gate_rasterimage_t* ptr_image, unsigned* ptr_x, unsigned* ptr_y,
1332 gate_uint32_t* ptr_display_time_ms)
1333 {
1334 return GATE_RESULT_NOTIMPLEMENTED;
1335 }
1336
1337 gate_result_t gate_gifanimation_reader_destroy(gate_gifanimation_reader_t animation)
1338 {
1339 return GATE_RESULT_NOTIMPLEMENTED;
1340 }
1341
1342
1343 #endif /* GATE_GRAPHICS_GIF_NO_IMPL */
1344
1345