GCC Code Coverage Report


Directory: src/gate/
File: src/gate/graphics/gifimages.c
Date: 2026-08-06 22:46:24
Exec Total Coverage
Lines: 231 563 41.0%
Functions: 12 26 46.2%
Branches: 107 309 34.6%

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