GCC Code Coverage Report


Directory: src/gate/
File: src/gate/graphics/gifimages.c
Date: 2026-06-21 00:38:37
Exec Total Coverage
Lines: 219 566 38.7%
Functions: 12 26 46.2%
Branches: 100 321 31.2%

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