GCC Code Coverage Report


Directory: src/gate/
File: src/gate/graphics/gifimages.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 214 566 37.8%
Functions: 11 26 42.3%
Branches: 98 321 30.5%

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