GCC Code Coverage Report


Directory: src/gate/
File: src/gate/graphics/gifimages.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 217 569 38.1%
Functions: 11 26 42.3%
Branches: 98 317 30.9%

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