LiteRGSS 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/ext/LiteRGSS/Bitmap.cpp +316 -0
  3. data/ext/LiteRGSS/Bitmap.h +24 -0
  4. data/ext/LiteRGSS/BlendMode.cpp +202 -0
  5. data/ext/LiteRGSS/BlendMode.h +20 -0
  6. data/ext/LiteRGSS/CBitmap_Element.cpp +50 -0
  7. data/ext/LiteRGSS/CBitmap_Element.h +17 -0
  8. data/ext/LiteRGSS/CDrawable_Element.cpp +38 -0
  9. data/ext/LiteRGSS/CDrawable_Element.h +30 -0
  10. data/ext/LiteRGSS/CRect_Element.h +15 -0
  11. data/ext/LiteRGSS/CShaderSprite_Element.cpp +17 -0
  12. data/ext/LiteRGSS/CShaderSprite_Element.h +17 -0
  13. data/ext/LiteRGSS/CSprite_Element.cpp +15 -0
  14. data/ext/LiteRGSS/CSprite_Element.h +36 -0
  15. data/ext/LiteRGSS/CText_Element.cpp +12 -0
  16. data/ext/LiteRGSS/CText_Element.h +29 -0
  17. data/ext/LiteRGSS/CTone_Element.h +17 -0
  18. data/ext/LiteRGSS/CViewport_Element.cpp +224 -0
  19. data/ext/LiteRGSS/CViewport_Element.h +57 -0
  20. data/ext/LiteRGSS/Color.cpp +200 -0
  21. data/ext/LiteRGSS/Color.h +22 -0
  22. data/ext/LiteRGSS/Fonts.cpp +126 -0
  23. data/ext/LiteRGSS/Fonts.h +20 -0
  24. data/ext/LiteRGSS/Graphics.cpp +314 -0
  25. data/ext/LiteRGSS/Graphics.h +31 -0
  26. data/ext/LiteRGSS/Graphics.local.cpp +365 -0
  27. data/ext/LiteRGSS/Graphics.local.h +37 -0
  28. data/ext/LiteRGSS/Image.cpp +460 -0
  29. data/ext/LiteRGSS/Image.h +32 -0
  30. data/ext/LiteRGSS/Input.cpp +664 -0
  31. data/ext/LiteRGSS/Input.h +38 -0
  32. data/ext/LiteRGSS/LiteRGSS.cpp +34 -0
  33. data/ext/LiteRGSS/LiteRGSS.h +113 -0
  34. data/ext/LiteRGSS/Rect.cpp +324 -0
  35. data/ext/LiteRGSS/Rect.h +24 -0
  36. data/ext/LiteRGSS/Shader.cpp +279 -0
  37. data/ext/LiteRGSS/Shader.h +13 -0
  38. data/ext/LiteRGSS/ShaderSprite.cpp +78 -0
  39. data/ext/LiteRGSS/ShaderSprite.h +8 -0
  40. data/ext/LiteRGSS/Sprite.cpp +495 -0
  41. data/ext/LiteRGSS/Sprite.h +43 -0
  42. data/ext/LiteRGSS/Table.cpp +228 -0
  43. data/ext/LiteRGSS/Table.h +29 -0
  44. data/ext/LiteRGSS/Table32.cpp +228 -0
  45. data/ext/LiteRGSS/Table32.h +29 -0
  46. data/ext/LiteRGSS/Text.cpp +574 -0
  47. data/ext/LiteRGSS/Text.h +52 -0
  48. data/ext/LiteRGSS/Texture.hpp +735 -0
  49. data/ext/LiteRGSS/Tone.cpp +228 -0
  50. data/ext/LiteRGSS/Tone.h +22 -0
  51. data/ext/LiteRGSS/Viewport.cpp +491 -0
  52. data/ext/LiteRGSS/Viewport.h +33 -0
  53. data/ext/LiteRGSS/Yuki.cpp +29 -0
  54. data/ext/LiteRGSS/Yuki.h +8 -0
  55. data/ext/LiteRGSS/Yuki_Gif.cpp +218 -0
  56. data/ext/LiteRGSS/Yuki_Gif.h +25 -0
  57. data/ext/LiteRGSS/extconf.rb +8 -0
  58. data/ext/LiteRGSS/libnsgif.c +1169 -0
  59. data/ext/LiteRGSS/libnsgif.h +183 -0
  60. data/ext/LiteRGSS/libnsgif.hpp +184 -0
  61. data/ext/LiteRGSS/lodepng.cpp +6245 -0
  62. data/ext/LiteRGSS/lodepng.h +1769 -0
  63. data/ext/LiteRGSS/lzw.c +377 -0
  64. data/ext/LiteRGSS/lzw.h +105 -0
  65. data/ext/LiteRGSS/sf_Text2.cpp +690 -0
  66. data/ext/LiteRGSS/sf_Text2.hpp +549 -0
  67. data/ext/LiteRGSS/utils/log.h +21 -0
  68. metadata +112 -0
@@ -0,0 +1,33 @@
1
+ #ifndef L_VIEWPORT_H
2
+ #define L_VIEWPORT_H
3
+
4
+ VALUE rb_Viewport_Initialize(int argc, VALUE* argv, VALUE self);
5
+ VALUE rb_Viewport_Copy(VALUE self);
6
+ VALUE rb_Viewport_getOX(VALUE self);
7
+ VALUE rb_Viewport_setOX(VALUE self, VALUE val);
8
+ VALUE rb_Viewport_getOY(VALUE self);
9
+ VALUE rb_Viewport_setOY(VALUE self, VALUE val);
10
+ VALUE rb_Viewport_getRect(VALUE self);
11
+ VALUE rb_Viewport_setRect(VALUE self, VALUE val);
12
+ VALUE rb_Viewport_Dispose(VALUE self);
13
+ VALUE rb_Viewport_Disposed(VALUE self);
14
+ VALUE rb_Viewport_getTone(VALUE self);
15
+ VALUE rb_Viewport_setTone(VALUE self, VALUE val);
16
+ VALUE rb_Viewport_getColor(VALUE self);
17
+ VALUE rb_Viewport_setColor(VALUE self, VALUE val);
18
+ VALUE rb_Viewport_getVisible(VALUE self);
19
+ VALUE rb_Viewport_setVisible(VALUE self, VALUE val);
20
+ VALUE rb_Viewport_getAngle(VALUE self);
21
+ VALUE rb_Viewport_setAngle(VALUE self, VALUE val);
22
+ VALUE rb_Viewport_getZoom(VALUE self);
23
+ VALUE rb_Viewport_setZoom(VALUE self, VALUE val);
24
+ VALUE rb_Viewport_Update(VALUE self);
25
+ VALUE rb_Viewport_getZ(VALUE self);
26
+ VALUE rb_Viewport_setZ(VALUE self, VALUE val);
27
+ VALUE rb_Viewport_getRenderState(VALUE self);
28
+ VALUE rb_Viewport_setRenderState(VALUE self, VALUE val);
29
+ VALUE rb_Viewport_ReloadStack(VALUE self);
30
+ VALUE rb_Viewport_Index(VALUE self);
31
+
32
+ void Viewport_SetView(CViewport_Element* viewport, long x, long y, long width, long height);
33
+ #endif
@@ -0,0 +1,29 @@
1
+ #include "LiteRGSS.h"
2
+
3
+ VALUE rb_mYuki = Qnil;
4
+
5
+ void Init_Yuki()
6
+ {
7
+ rb_mYuki = rb_define_module("Yuki");
8
+ rb_define_module_function(rb_mYuki, "get_clipboard", _rbf rb_Yuki_getClipboard, 0);
9
+ rb_define_module_function(rb_mYuki, "set_clipboard", _rbf rb_Yuki_setClipboard, 1);
10
+ }
11
+
12
+ /* Clipboard */
13
+ VALUE rb_Yuki_getClipboard(VALUE self)
14
+ {
15
+ sf::String str = sf::Clipboard::getString();
16
+ if (str.getSize() == 0)
17
+ {
18
+ return Qnil;
19
+ }
20
+ return rb_utf8_str_new_cstr(reinterpret_cast<const char*>(str.toUtf8().c_str()));
21
+ }
22
+
23
+ VALUE rb_Yuki_setClipboard(VALUE self, VALUE val)
24
+ {
25
+ rb_check_type(val, T_STRING);
26
+ std::string stru8(RSTRING_PTR(val));
27
+ sf::Clipboard::setString(sf::String::fromUtf8(stru8.begin(), stru8.end()));
28
+ return self;
29
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef YUKI_HEADER_F
2
+ #define YUIK_HEADER_F
3
+
4
+
5
+ VALUE rb_Yuki_getClipboard(VALUE self);
6
+ VALUE rb_Yuki_setClipboard(VALUE self, VALUE val);
7
+
8
+ #endif
@@ -0,0 +1,218 @@
1
+ #include "LiteRGSS.h"
2
+
3
+ VALUE rb_cYukiGifReader = Qnil;
4
+ VALUE rb_cYukiGifError = Qnil;
5
+ double rb_YukiGifReaderFrameDelta = 16.666666666666666;
6
+
7
+ #define GIF_PROTECT if(RDATA(self)->data == nullptr) \
8
+ {\
9
+ rb_raise(rb_eRGSSError, "Disposed GIF."); \
10
+ return self; \
11
+ }
12
+
13
+ #define GET_GIF rb_yuki_gif_data* gif; \
14
+ Data_Get_Struct(self, rb_yuki_gif_data, gif); \
15
+ GIF_PROTECT \
16
+
17
+ void rb_Yuki_Gif_Free(void* data)
18
+ {
19
+ rb_yuki_gif_data* gif = reinterpret_cast<rb_yuki_gif_data*>(data);
20
+ if (gif != nullptr)
21
+ {
22
+ gif_finalise(&gif->gif);
23
+ delete gif;
24
+ }
25
+ }
26
+
27
+ //--- LibNgif util functions ---
28
+ static void *bitmap_create(int width, int height)
29
+ {
30
+ /* ensure a stupidly large bitmap is not created */
31
+ if (((long long)width * (long long)height) > (MAX_IMAGE_BYTES / BYTES_PER_PIXEL)) {
32
+ return NULL;
33
+ }
34
+ return calloc(width * height, BYTES_PER_PIXEL);
35
+ }
36
+
37
+ static void bitmap_set_opaque(void *bitmap, bool opaque)
38
+ {
39
+ // (void)opaque; /* unused */
40
+ // (void)bitmap; /* unused */
41
+ // assert(bitmap);
42
+ }
43
+
44
+
45
+ static bool bitmap_test_opaque(void *bitmap)
46
+ {
47
+ //(void)bitmap; /* unused */
48
+ //assert(bitmap);
49
+ return false;
50
+ }
51
+
52
+ static unsigned char *bitmap_get_buffer(void *bitmap)
53
+ {
54
+ //assert(bitmap);
55
+ return reinterpret_cast<unsigned char*>(bitmap);
56
+ }
57
+
58
+ static void bitmap_destroy(void *bitmap)
59
+ {
60
+ //assert(bitmap);
61
+ free(bitmap);
62
+ }
63
+
64
+ static void bitmap_modified(void *bitmap)
65
+ {
66
+ //(void)bitmap; /* unused */
67
+ //assert(bitmap);
68
+ return;
69
+ }
70
+
71
+ static gif_bitmap_callback_vt bitmap_callbacks = {
72
+ bitmap_create,
73
+ bitmap_destroy,
74
+ bitmap_get_buffer,
75
+ bitmap_set_opaque,
76
+ bitmap_test_opaque,
77
+ bitmap_modified
78
+ };
79
+
80
+ VALUE rb_Yuki_Gif_Alloc(VALUE klass)
81
+ {
82
+ rb_yuki_gif_data* gif = new rb_yuki_gif_data();
83
+ gif->frame = 0;
84
+ gif->counter = 0;
85
+ gif_create(&gif->gif, &bitmap_callbacks);
86
+ return Data_Wrap_Struct(klass, NULL, rb_Yuki_Gif_Free, gif);
87
+ }
88
+
89
+ void Init_YukiGifReader()
90
+ {
91
+ rb_cYukiGifReader = rb_define_class_under(rb_mYuki, "GifReader", rb_cObject);
92
+ rb_define_alloc_func(rb_cYukiGifReader, rb_Yuki_Gif_Alloc);
93
+ rb_cYukiGifError = rb_define_class_under(rb_cYukiGifReader, "Error", rb_eStandardError);
94
+ rb_define_method(rb_cYukiGifReader, "initialize", _rbf rb_Yuki_GifReader_Initialize, -1);
95
+ rb_define_method(rb_cYukiGifReader, "update", _rbf rb_Yuki_GifReader_Update, 1);
96
+ rb_define_method(rb_cYukiGifReader, "draw", _rbf rb_Yuki_GifReader_Draw, 1);
97
+ rb_define_method(rb_cYukiGifReader, "width", _rbf rb_Yuki_GifReader_Width, 0);
98
+ rb_define_method(rb_cYukiGifReader, "height", _rbf rb_Yuki_GifReader_Height, 0);
99
+ rb_define_method(rb_cYukiGifReader, "frame", _rbf rb_Yuki_GifReader_Frame, 0);
100
+ rb_define_method(rb_cYukiGifReader, "frame=", _rbf rb_Yuki_GifReader_Frame_set, 1);
101
+ rb_define_method(rb_cYukiGifReader, "frame_count", _rbf rb_Yuki_GifReader_FrameCount, 0);
102
+
103
+ rb_define_method(rb_cYukiGifReader, "clone", _rbf rb_Yuki_GifReader_Copy, 0);
104
+ rb_define_method(rb_cYukiGifReader, "dup", _rbf rb_Yuki_GifReader_Copy, 0);
105
+
106
+ rb_define_singleton_method(rb_cYukiGifReader, "delta_counter=", _rbf rb_Yuki_GifReader_SetDeltaCounter, 1);
107
+ }
108
+
109
+ VALUE rb_Yuki_GifReader_Initialize(int argc, VALUE *argv, VALUE self)
110
+ {
111
+ GET_GIF;
112
+ VALUE str, from_memory;
113
+ rb_scan_args(argc, argv, "11", &str, &from_memory);
114
+ rb_check_type(str, T_STRING);
115
+ if (RTEST(from_memory))
116
+ {
117
+ rb_str_freeze(str);
118
+ rb_iv_set(self, "@__gif_data", str);
119
+ gif_result code = gif_initialise(&gif->gif, RSTRING_LEN(str), reinterpret_cast<unsigned char*>(RSTRING_PTR(str)));
120
+ if (code != GIF_OK && code != GIF_WORKING)
121
+ {
122
+ rb_raise(rb_cYukiGifError, "Failed to load GIF from Memory");
123
+ }
124
+ }
125
+ else
126
+ {
127
+ VALUE file = rb_file_open(RSTRING_PTR(str), "rb");
128
+ VALUE data = rb_funcall(file, rb_intern("size"), 0);
129
+ data = rb_funcall(file, rb_intern("read"), 1, data);
130
+ rb_io_close(file);
131
+ rb_str_freeze(data);
132
+ rb_iv_set(self, "@__gif_data", data);
133
+ gif_result code = gif_initialise(&gif->gif, RSTRING_LEN(data), reinterpret_cast<unsigned char*>(RSTRING_PTR(data)));
134
+ if (code != GIF_OK && code != GIF_WORKING)
135
+ {
136
+ rb_raise(rb_cYukiGifError, "Failed to load GIF from File (%s)", RSTRING_PTR(str));
137
+ }
138
+ }
139
+ return self;
140
+ }
141
+
142
+ VALUE rb_Yuki_GifReader_Update(VALUE self, VALUE bitmap)
143
+ {
144
+ GET_GIF;
145
+ gif_animation* gifa = &gif->gif;
146
+ if (((gifa->frames[gif->frame].frame_delay * 10) <= gif->counter) || (gif->frame == 0 && gif->counter == 0))
147
+ {
148
+ if (gif->frame != 0 || gif->counter != 0)
149
+ {
150
+ gif->counter -= gifa->frames[gif->frame].frame_delay * 10;
151
+ gif->frame++;
152
+ if (gif->frame >= gifa->frame_count)
153
+ gif->frame = 0;
154
+ }
155
+ if (gif_decode_frame(gifa, gif->frame) == GIF_OK)
156
+ {
157
+ rb_Yuki_GifReader_Draw(self, bitmap);
158
+ }
159
+ else
160
+ rb_raise(rb_cYukiGifError, "Failed to decode GIF frame");
161
+ }
162
+ gif->counter += rb_YukiGifReaderFrameDelta;
163
+ return self;
164
+ }
165
+
166
+ VALUE rb_Yuki_GifReader_Draw(VALUE self, VALUE bitmap)
167
+ {
168
+ GET_GIF;
169
+ if (rb_obj_is_kind_of(bitmap, rb_cBitmap) == Qtrue)
170
+ {
171
+ sf::Texture* text = rb_Bitmap_getTexture(bitmap);
172
+ text->update(reinterpret_cast<sf::Uint8*>(gif->gif.frame_image), gif->gif.width, gif->gif.height, 0, 0);
173
+ }
174
+ return self;
175
+ }
176
+
177
+ VALUE rb_Yuki_GifReader_Width(VALUE self)
178
+ {
179
+ GET_GIF;
180
+ return UINT2NUM(gif->gif.width);
181
+ }
182
+
183
+ VALUE rb_Yuki_GifReader_Height(VALUE self)
184
+ {
185
+ GET_GIF;
186
+ return UINT2NUM(gif->gif.height);
187
+ }
188
+
189
+ VALUE rb_Yuki_GifReader_Frame(VALUE self)
190
+ {
191
+ GET_GIF;
192
+ return ULONG2NUM(gif->frame);
193
+ }
194
+
195
+ VALUE rb_Yuki_GifReader_Frame_set(VALUE self, VALUE frame)
196
+ {
197
+ GET_GIF;
198
+ gif->frame = NUM2ULONG(frame);
199
+ return self;
200
+ }
201
+
202
+ VALUE rb_Yuki_GifReader_FrameCount(VALUE self)
203
+ {
204
+ GET_GIF;
205
+ return UINT2NUM(gif->gif.frame_count);
206
+ }
207
+
208
+ VALUE rb_Yuki_GifReader_SetDeltaCounter(VALUE self, VALUE delta)
209
+ {
210
+ rb_YukiGifReaderFrameDelta = NUM2DBL(delta);
211
+ return delta;
212
+ }
213
+
214
+ VALUE rb_Yuki_GifReader_Copy(VALUE self)
215
+ {
216
+ rb_raise(rb_eRGSSError, "Gif cannot be cloned or duplicated.");
217
+ return self;
218
+ }
@@ -0,0 +1,25 @@
1
+ #ifndef YUKI_GIF_H
2
+ #include "libnsgif.hpp"
3
+
4
+ #define BYTES_PER_PIXEL 4
5
+ #define MAX_IMAGE_BYTES (48 * 1024 * 1024)
6
+
7
+ VALUE rb_Yuki_GifReader_Initialize(int argc, VALUE *argv, VALUE self);
8
+ VALUE rb_Yuki_GifReader_Update(VALUE self, VALUE bitmap);
9
+ VALUE rb_Yuki_GifReader_Draw(VALUE self, VALUE bitmap);
10
+ VALUE rb_Yuki_GifReader_Width(VALUE self);
11
+ VALUE rb_Yuki_GifReader_Height(VALUE self);
12
+ VALUE rb_Yuki_GifReader_Frame(VALUE self);
13
+ VALUE rb_Yuki_GifReader_Frame_set(VALUE self, VALUE frame);
14
+ VALUE rb_Yuki_GifReader_FrameCount(VALUE self);
15
+ VALUE rb_Yuki_GifReader_Copy(VALUE self);
16
+
17
+ VALUE rb_Yuki_GifReader_SetDeltaCounter(VALUE self, VALUE delta);
18
+
19
+ struct rb_yuki_gif_data {
20
+ gif_animation gif;
21
+ unsigned long frame;
22
+ double counter;
23
+ };
24
+
25
+ #endif // !YUKI_GIF_H
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+ ext_name = 'LiteRGSS'
3
+
4
+ have_library('sfml-graphics')
5
+ have_library('sfml-window')
6
+ have_library('sfml-system')
7
+
8
+ create_makefile(ext_name)
@@ -0,0 +1,1169 @@
1
+ /*
2
+ * Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>
3
+ * Copyright 2008 Sean Fox <dyntryx@gmail.com>
4
+ *
5
+ * This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
6
+ * Licenced under the MIT License,
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ */
9
+
10
+ #include <stdbool.h>
11
+ #include <stdint.h>
12
+ #include <stdio.h>
13
+ #include <string.h>
14
+ #include <stdlib.h>
15
+ #include <assert.h>
16
+ #include "libnsgif.h"
17
+ #include "utils/log.h"
18
+
19
+ #include "lzw.h"
20
+
21
+ /**
22
+ *
23
+ * \file
24
+ * \brief GIF image decoder
25
+ *
26
+ * The GIF format is thoroughly documented; a full description can be found at
27
+ * http://www.w3.org/Graphics/GIF/spec-gif89a.txt
28
+ *
29
+ * \todo Plain text and comment extensions should be implemented.
30
+ */
31
+
32
+
33
+ /** Maximum colour table size */
34
+ #define GIF_MAX_COLOURS 256
35
+
36
+ /** Internal flag that the colour table needs to be processed */
37
+ #define GIF_PROCESS_COLOURS 0xaa000000
38
+
39
+ /** Internal flag that a frame is invalid/unprocessed */
40
+ #define GIF_INVALID_FRAME -1
41
+
42
+ /** Transparent colour */
43
+ #define GIF_TRANSPARENT_COLOUR 0x00
44
+
45
+ /* GIF Flags */
46
+ #define GIF_FRAME_COMBINE 1
47
+ #define GIF_FRAME_CLEAR 2
48
+ #define GIF_FRAME_RESTORE 3
49
+ #define GIF_FRAME_QUIRKS_RESTORE 4
50
+
51
+ #define GIF_IMAGE_SEPARATOR 0x2c
52
+ #define GIF_INTERLACE_MASK 0x40
53
+ #define GIF_COLOUR_TABLE_MASK 0x80
54
+ #define GIF_COLOUR_TABLE_SIZE_MASK 0x07
55
+ #define GIF_EXTENSION_INTRODUCER 0x21
56
+ #define GIF_EXTENSION_GRAPHIC_CONTROL 0xf9
57
+ #define GIF_DISPOSAL_MASK 0x1c
58
+ #define GIF_TRANSPARENCY_MASK 0x01
59
+ #define GIF_EXTENSION_COMMENT 0xfe
60
+ #define GIF_EXTENSION_PLAIN_TEXT 0x01
61
+ #define GIF_EXTENSION_APPLICATION 0xff
62
+ #define GIF_BLOCK_TERMINATOR 0x00
63
+ #define GIF_TRAILER 0x3b
64
+
65
+ /** standard GIF header size */
66
+ #define GIF_STANDARD_HEADER_SIZE 13
67
+
68
+
69
+ /**
70
+ * Updates the sprite memory size
71
+ *
72
+ * \param gif The animation context
73
+ * \param width The width of the sprite
74
+ * \param height The height of the sprite
75
+ * \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
76
+ */
77
+ static gif_result
78
+ gif_initialise_sprite(gif_animation *gif,
79
+ unsigned int width,
80
+ unsigned int height)
81
+ {
82
+ unsigned int max_width;
83
+ unsigned int max_height;
84
+ struct bitmap *buffer;
85
+
86
+ /* Check if we've changed */
87
+ if ((width <= gif->width) && (height <= gif->height)) {
88
+ return GIF_OK;
89
+ }
90
+
91
+ /* Get our maximum values */
92
+ max_width = (width > gif->width) ? width : gif->width;
93
+ max_height = (height > gif->height) ? height : gif->height;
94
+
95
+ /* Allocate some more memory */
96
+ assert(gif->bitmap_callbacks.bitmap_create);
97
+ buffer = gif->bitmap_callbacks.bitmap_create(max_width, max_height);
98
+ if (buffer == NULL) {
99
+ return GIF_INSUFFICIENT_MEMORY;
100
+ }
101
+
102
+ assert(gif->bitmap_callbacks.bitmap_destroy);
103
+ gif->bitmap_callbacks.bitmap_destroy(gif->frame_image);
104
+ gif->frame_image = buffer;
105
+ gif->width = max_width;
106
+ gif->height = max_height;
107
+
108
+ /* Invalidate our currently decoded image */
109
+ gif->decoded_frame = GIF_INVALID_FRAME;
110
+ return GIF_OK;
111
+ }
112
+
113
+
114
+ /**
115
+ * Attempts to initialise the frame's extensions
116
+ *
117
+ * \param gif The animation context
118
+ * \param frame The frame number
119
+ * @return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the
120
+ * frame GIF_OK for successful initialisation.
121
+ */
122
+ static gif_result
123
+ gif_initialise_frame_extensions(gif_animation *gif, const int frame)
124
+ {
125
+ unsigned char *gif_data, *gif_end;
126
+ int gif_bytes;
127
+ unsigned int block_size;
128
+
129
+ /* Get our buffer position etc. */
130
+ gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
131
+ gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
132
+
133
+ /* Initialise the extensions */
134
+ while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
135
+ ++gif_data;
136
+ if ((gif_bytes = (gif_end - gif_data)) < 1) {
137
+ return GIF_INSUFFICIENT_FRAME_DATA;
138
+ }
139
+
140
+ /* Switch on extension label */
141
+ switch (gif_data[0]) {
142
+ case GIF_EXTENSION_GRAPHIC_CONTROL:
143
+ /* 6-byte Graphic Control Extension is:
144
+ *
145
+ * +0 CHAR Graphic Control Label
146
+ * +1 CHAR Block Size
147
+ * +2 CHAR __Packed Fields__
148
+ * 3BITS Reserved
149
+ * 3BITS Disposal Method
150
+ * 1BIT User Input Flag
151
+ * 1BIT Transparent Color Flag
152
+ * +3 SHORT Delay Time
153
+ * +5 CHAR Transparent Color Index
154
+ */
155
+ if (gif_bytes < 6) {
156
+ return GIF_INSUFFICIENT_FRAME_DATA;
157
+ }
158
+
159
+ gif->frames[frame].frame_delay = gif_data[3] | (gif_data[4] << 8);
160
+ if (gif_data[2] & GIF_TRANSPARENCY_MASK) {
161
+ gif->frames[frame].transparency = true;
162
+ gif->frames[frame].transparency_index = gif_data[5];
163
+ }
164
+ gif->frames[frame].disposal_method = ((gif_data[2] & GIF_DISPOSAL_MASK) >> 2);
165
+ /* I have encountered documentation and GIFs in the
166
+ * wild that use 0x04 to restore the previous frame,
167
+ * rather than the officially documented 0x03. I
168
+ * believe some (older?) software may even actually
169
+ * export this way. We handle this as a type of
170
+ * "quirks" mode.
171
+ */
172
+ if (gif->frames[frame].disposal_method == GIF_FRAME_QUIRKS_RESTORE) {
173
+ gif->frames[frame].disposal_method = GIF_FRAME_RESTORE;
174
+ }
175
+ gif_data += (2 + gif_data[1]);
176
+ break;
177
+
178
+ case GIF_EXTENSION_APPLICATION:
179
+ /* 14-byte+ Application Extension is:
180
+ *
181
+ * +0 CHAR Application Extension Label
182
+ * +1 CHAR Block Size
183
+ * +2 8CHARS Application Identifier
184
+ * +10 3CHARS Appl. Authentication Code
185
+ * +13 1-256 Application Data (Data sub-blocks)
186
+ */
187
+ if (gif_bytes < 17) {
188
+ return GIF_INSUFFICIENT_FRAME_DATA;
189
+ }
190
+ if ((gif_data[1] == 0x0b) &&
191
+ (strncmp((const char *) gif_data + 2,
192
+ "NETSCAPE2.0", 11) == 0) &&
193
+ (gif_data[13] == 0x03) &&
194
+ (gif_data[14] == 0x01)) {
195
+ gif->loop_count = gif_data[15] | (gif_data[16] << 8);
196
+ }
197
+ gif_data += (2 + gif_data[1]);
198
+ break;
199
+
200
+ case GIF_EXTENSION_COMMENT:
201
+ /* Move the pointer to the first data sub-block Skip 1
202
+ * byte for the extension label
203
+ */
204
+ ++gif_data;
205
+ break;
206
+
207
+ default:
208
+ /* Move the pointer to the first data sub-block Skip 2
209
+ * bytes for the extension label and size fields Skip
210
+ * the extension size itself
211
+ */
212
+ if (gif_bytes < 2) {
213
+ return GIF_INSUFFICIENT_FRAME_DATA;
214
+ }
215
+ gif_data += (2 + gif_data[1]);
216
+ }
217
+
218
+ /* Repeatedly skip blocks until we get a zero block or run out
219
+ * of data This data is ignored by this gif decoder
220
+ */
221
+ gif_bytes = (gif_end - gif_data);
222
+ block_size = 0;
223
+ while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
224
+ block_size = gif_data[0] + 1;
225
+ if ((gif_bytes -= block_size) < 0) {
226
+ return GIF_INSUFFICIENT_FRAME_DATA;
227
+ }
228
+ gif_data += block_size;
229
+ }
230
+ ++gif_data;
231
+ }
232
+
233
+ /* Set buffer position and return */
234
+ gif->buffer_position = (gif_data - gif->gif_data);
235
+ return GIF_OK;
236
+ }
237
+
238
+
239
+ /**
240
+ * Attempts to initialise the next frame
241
+ *
242
+ * \param gif The animation context
243
+ * \return error code
244
+ * - GIF_INSUFFICIENT_DATA for insufficient data to do anything
245
+ * - GIF_FRAME_DATA_ERROR for GIF frame data error
246
+ * - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
247
+ * - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
248
+ * - GIF_DATA_ERROR for GIF error (invalid frame header)
249
+ * - GIF_OK for successful decoding
250
+ * - GIF_WORKING for successful decoding if more frames are expected
251
+ */
252
+ static gif_result gif_initialise_frame(gif_animation *gif)
253
+ {
254
+ int frame;
255
+ gif_frame *temp_buf;
256
+
257
+ unsigned char *gif_data, *gif_end;
258
+ int gif_bytes;
259
+ unsigned int flags = 0;
260
+ unsigned int width, height, offset_x, offset_y;
261
+ unsigned int block_size, colour_table_size;
262
+ bool first_image = true;
263
+ gif_result return_value;
264
+
265
+ /* Get the frame to decode and our data position */
266
+ frame = gif->frame_count;
267
+
268
+ /* Get our buffer position etc. */
269
+ gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
270
+ gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
271
+ gif_bytes = (gif_end - gif_data);
272
+
273
+ /* Check if we've finished */
274
+ if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
275
+ return GIF_OK;
276
+ }
277
+
278
+ /* Check if there is enough data remaining. The shortest block of data
279
+ * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
280
+ * trailer
281
+ */
282
+ if (gif_bytes < 6) {
283
+ return GIF_INSUFFICIENT_DATA;
284
+ }
285
+
286
+ /* We could theoretically get some junk data that gives us millions of
287
+ * frames, so we ensure that we don't have a silly number
288
+ */
289
+ if (frame > 4096) {
290
+ return GIF_FRAME_DATA_ERROR;
291
+ }
292
+
293
+ /* Get some memory to store our pointers in etc. */
294
+ if ((int)gif->frame_holders <= frame) {
295
+ /* Allocate more memory */
296
+ temp_buf = (gif_frame *)realloc(gif->frames, (frame + 1) * sizeof(gif_frame));
297
+ if (temp_buf == NULL) {
298
+ return GIF_INSUFFICIENT_MEMORY;
299
+ }
300
+ gif->frames = temp_buf;
301
+ gif->frame_holders = frame + 1;
302
+ }
303
+
304
+ /* Store our frame pointer. We would do it when allocating except we
305
+ * start off with one frame allocated so we can always use realloc.
306
+ */
307
+ gif->frames[frame].frame_pointer = gif->buffer_position;
308
+ gif->frames[frame].display = false;
309
+ gif->frames[frame].virgin = true;
310
+ gif->frames[frame].disposal_method = 0;
311
+ gif->frames[frame].transparency = false;
312
+ gif->frames[frame].frame_delay = 100;
313
+ gif->frames[frame].redraw_required = false;
314
+
315
+ /* Invalidate any previous decoding we have of this frame */
316
+ if (gif->decoded_frame == frame) {
317
+ gif->decoded_frame = GIF_INVALID_FRAME;
318
+ }
319
+
320
+ /* We pretend to initialise the frames, but really we just skip over
321
+ * all the data contained within. This is all basically a cut down
322
+ * version of gif_decode_frame that doesn't have any of the LZW bits in
323
+ * it.
324
+ */
325
+
326
+ /* Initialise any extensions */
327
+ gif->buffer_position = gif_data - gif->gif_data;
328
+ return_value = gif_initialise_frame_extensions(gif, frame);
329
+ if (return_value != GIF_OK) {
330
+ return return_value;
331
+ }
332
+ gif_data = (gif->gif_data + gif->buffer_position);
333
+ gif_bytes = (gif_end - gif_data);
334
+
335
+ /* Check if we've finished */
336
+ if ((gif_bytes = (gif_end - gif_data)) < 1) {
337
+ return GIF_INSUFFICIENT_FRAME_DATA;
338
+ }
339
+
340
+ if (gif_data[0] == GIF_TRAILER) {
341
+ gif->buffer_position = (gif_data - gif->gif_data);
342
+ gif->frame_count = frame + 1;
343
+ return GIF_OK;
344
+ }
345
+
346
+ /* If we're not done, there should be an image descriptor */
347
+ if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
348
+ return GIF_FRAME_DATA_ERROR;
349
+ }
350
+
351
+ /* Do some simple boundary checking */
352
+ if (gif_bytes < 10) {
353
+ return GIF_INSUFFICIENT_FRAME_DATA;
354
+ }
355
+ offset_x = gif_data[1] | (gif_data[2] << 8);
356
+ offset_y = gif_data[3] | (gif_data[4] << 8);
357
+ width = gif_data[5] | (gif_data[6] << 8);
358
+ height = gif_data[7] | (gif_data[8] << 8);
359
+
360
+ /* Set up the redraw characteristics. We have to check for extending
361
+ * the area due to multi-image frames.
362
+ */
363
+ if (!first_image) {
364
+ if (gif->frames[frame].redraw_x > offset_x) {
365
+ gif->frames[frame].redraw_width += (gif->frames[frame].redraw_x - offset_x);
366
+ gif->frames[frame].redraw_x = offset_x;
367
+ }
368
+
369
+ if (gif->frames[frame].redraw_y > offset_y) {
370
+ gif->frames[frame].redraw_height += (gif->frames[frame].redraw_y - offset_y);
371
+ gif->frames[frame].redraw_y = offset_y;
372
+ }
373
+
374
+ if ((offset_x + width) > (gif->frames[frame].redraw_x + gif->frames[frame].redraw_width)) {
375
+ gif->frames[frame].redraw_width = (offset_x + width) - gif->frames[frame].redraw_x;
376
+ }
377
+
378
+ if ((offset_y + height) > (gif->frames[frame].redraw_y + gif->frames[frame].redraw_height)) {
379
+ gif->frames[frame].redraw_height = (offset_y + height) - gif->frames[frame].redraw_y;
380
+ }
381
+ } else {
382
+ first_image = false;
383
+ gif->frames[frame].redraw_x = offset_x;
384
+ gif->frames[frame].redraw_y = offset_y;
385
+ gif->frames[frame].redraw_width = width;
386
+ gif->frames[frame].redraw_height = height;
387
+ }
388
+
389
+ /* if we are clearing the background then we need to redraw enough to
390
+ * cover the previous frame too
391
+ */
392
+ gif->frames[frame].redraw_required = ((gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) ||
393
+ (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE));
394
+
395
+ /* Boundary checking - shouldn't ever happen except with junk data */
396
+ if (gif_initialise_sprite(gif, (offset_x + width), (offset_y + height))) {
397
+ return GIF_INSUFFICIENT_MEMORY;
398
+ }
399
+
400
+ /* Decode the flags */
401
+ flags = gif_data[9];
402
+ colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);
403
+
404
+ /* Move our data onwards and remember we've got a bit of this frame */
405
+ gif_data += 10;
406
+ gif_bytes = (gif_end - gif_data);
407
+ gif->frame_count_partial = frame + 1;
408
+
409
+ /* Skip the local colour table */
410
+ if (flags & GIF_COLOUR_TABLE_MASK) {
411
+ gif_data += 3 * colour_table_size;
412
+ if ((gif_bytes = (gif_end - gif_data)) < 0) {
413
+ return GIF_INSUFFICIENT_FRAME_DATA;
414
+ }
415
+ }
416
+
417
+ /* Ensure we have a correct code size */
418
+ if (gif_bytes < 1) {
419
+ return GIF_INSUFFICIENT_FRAME_DATA;
420
+ }
421
+ if (gif_data[0] > LZW_CODE_MAX) {
422
+ return GIF_DATA_ERROR;
423
+ }
424
+
425
+ /* Move our pointer to the actual image data */
426
+ gif_data++;
427
+ --gif_bytes;
428
+
429
+ /* Repeatedly skip blocks until we get a zero block or run out of data
430
+ * These blocks of image data are processed later by gif_decode_frame()
431
+ */
432
+ block_size = 0;
433
+ while (block_size != 1) {
434
+ if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
435
+ block_size = gif_data[0] + 1;
436
+ /* Check if the frame data runs off the end of the file */
437
+ if ((int)(gif_bytes - block_size) < 0) {
438
+ /* Try to recover by signaling the end of the gif.
439
+ * Once we get garbage data, there is no logical way to
440
+ * determine where the next frame is. It's probably
441
+ * better to partially load the gif than not at all.
442
+ */
443
+ if (gif_bytes >= 2) {
444
+ gif_data[0] = 0;
445
+ gif_data[1] = GIF_TRAILER;
446
+ gif_bytes = 1;
447
+ ++gif_data;
448
+ break;
449
+ } else {
450
+ return GIF_INSUFFICIENT_FRAME_DATA;
451
+ }
452
+ } else {
453
+ gif_bytes -= block_size;
454
+ gif_data += block_size;
455
+ }
456
+ }
457
+
458
+ /* Add the frame and set the display flag */
459
+ gif->buffer_position = gif_data - gif->gif_data;
460
+ gif->frame_count = frame + 1;
461
+ gif->frames[frame].display = true;
462
+
463
+ /* Check if we've finished */
464
+ if (gif_bytes < 1) {
465
+ return GIF_INSUFFICIENT_FRAME_DATA;
466
+ } else {
467
+ if (gif_data[0] == GIF_TRAILER) {
468
+ return GIF_OK;
469
+ }
470
+ }
471
+ return GIF_WORKING;
472
+ }
473
+
474
+
475
+
476
+
477
+ /**
478
+ * Skips the frame's extensions (which have been previously initialised)
479
+ *
480
+ * \param gif The animation context
481
+ * \return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the
482
+ * frame GIF_OK for successful decoding
483
+ */
484
+ static gif_result gif_skip_frame_extensions(gif_animation *gif)
485
+ {
486
+ unsigned char *gif_data, *gif_end;
487
+ int gif_bytes;
488
+ unsigned int block_size;
489
+
490
+ /* Get our buffer position etc. */
491
+ gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
492
+ gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
493
+ gif_bytes = (gif_end - gif_data);
494
+
495
+ /* Skip the extensions */
496
+ while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
497
+ ++gif_data;
498
+ if (gif_data >= gif_end) {
499
+ return GIF_INSUFFICIENT_FRAME_DATA;
500
+ }
501
+
502
+ /* Switch on extension label */
503
+ switch(gif_data[0]) {
504
+ case GIF_EXTENSION_COMMENT:
505
+ /* Move the pointer to the first data sub-block
506
+ * 1 byte for the extension label
507
+ */
508
+ ++gif_data;
509
+ break;
510
+
511
+ default:
512
+ /* Move the pointer to the first data sub-block 2 bytes
513
+ * for the extension label and size fields Skip the
514
+ * extension size itself
515
+ */
516
+ if (gif_data + 1 >= gif_end) {
517
+ return GIF_INSUFFICIENT_FRAME_DATA;
518
+ }
519
+ gif_data += (2 + gif_data[1]);
520
+ }
521
+
522
+ /* Repeatedly skip blocks until we get a zero block or run out
523
+ * of data This data is ignored by this gif decoder
524
+ */
525
+ gif_bytes = (gif_end - gif_data);
526
+ block_size = 0;
527
+ while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
528
+ block_size = gif_data[0] + 1;
529
+ if ((gif_bytes -= block_size) < 0) {
530
+ return GIF_INSUFFICIENT_FRAME_DATA;
531
+ }
532
+ gif_data += block_size;
533
+ }
534
+ ++gif_data;
535
+ }
536
+
537
+ /* Set buffer position and return */
538
+ gif->buffer_position = (gif_data - gif->gif_data);
539
+ return GIF_OK;
540
+ }
541
+
542
+ static unsigned int gif_interlaced_line(int height, int y) {
543
+ if ((y << 3) < height) {
544
+ return (y << 3);
545
+ }
546
+ y -= ((height + 7) >> 3);
547
+ if ((y << 3) < (height - 4)) {
548
+ return (y << 3) + 4;
549
+ }
550
+ y -= ((height + 3) >> 3);
551
+ if ((y << 2) < (height - 2)) {
552
+ return (y << 2) + 2;
553
+ }
554
+ y -= ((height + 1) >> 2);
555
+ return (y << 1) + 1;
556
+ }
557
+
558
+
559
+ static gif_result gif_error_from_lzw(lzw_result l_res)
560
+ {
561
+ static const gif_result g_res[] = {
562
+ [LZW_OK] = GIF_OK,
563
+ [LZW_OK_EOD] = GIF_END_OF_FRAME,
564
+ [LZW_NO_MEM] = GIF_INSUFFICIENT_MEMORY,
565
+ [LZW_NO_DATA] = GIF_INSUFFICIENT_FRAME_DATA,
566
+ [LZW_EOI_CODE] = GIF_FRAME_DATA_ERROR,
567
+ [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
568
+ [LZW_BAD_CODE] = GIF_FRAME_DATA_ERROR,
569
+ };
570
+ return g_res[l_res];
571
+ }
572
+
573
+
574
+ /**
575
+ * decode a gif frame
576
+ *
577
+ * \param gif gif animation context.
578
+ * \param frame The frame number to decode.
579
+ * \param clear_image flag for image data being cleared instead of plotted.
580
+ */
581
+ static gif_result
582
+ gif_internal_decode_frame(gif_animation *gif,
583
+ unsigned int frame,
584
+ bool clear_image)
585
+ {
586
+ unsigned int index = 0;
587
+ unsigned char *gif_data, *gif_end;
588
+ int gif_bytes;
589
+ unsigned int width, height, offset_x, offset_y;
590
+ unsigned int flags, colour_table_size, interlace;
591
+ unsigned int *colour_table;
592
+ unsigned int *frame_data = 0; // Set to 0 for no warnings
593
+ unsigned int *frame_scanline;
594
+ unsigned int save_buffer_position;
595
+ unsigned int return_value = 0;
596
+ unsigned int x, y, decode_y, burst_bytes;
597
+ register unsigned char colour;
598
+
599
+ /* Ensure this frame is supposed to be decoded */
600
+ if (gif->frames[frame].display == false) {
601
+ return GIF_OK;
602
+ }
603
+
604
+ /* Ensure the frame is in range to decode */
605
+ if (frame > gif->frame_count_partial) {
606
+ return GIF_INSUFFICIENT_DATA;
607
+ }
608
+
609
+ /* done if frame is already decoded */
610
+ if ((!clear_image) &&
611
+ ((int)frame == gif->decoded_frame)) {
612
+ return GIF_OK;
613
+ }
614
+
615
+ /* Get the start of our frame data and the end of the GIF data */
616
+ gif_data = gif->gif_data + gif->frames[frame].frame_pointer;
617
+ gif_end = gif->gif_data + gif->buffer_size;
618
+ gif_bytes = (gif_end - gif_data);
619
+
620
+ /*
621
+ * Ensure there is a minimal amount of data to proceed. The shortest
622
+ * block of data is a 10-byte image descriptor + 1-byte gif trailer
623
+ */
624
+ if (gif_bytes < 12) {
625
+ return GIF_INSUFFICIENT_FRAME_DATA;
626
+ }
627
+
628
+ /* Save the buffer position */
629
+ save_buffer_position = gif->buffer_position;
630
+ gif->buffer_position = gif_data - gif->gif_data;
631
+
632
+ /* Skip any extensions because they have allready been processed */
633
+ if ((return_value = gif_skip_frame_extensions(gif)) != GIF_OK) {
634
+ goto gif_decode_frame_exit;
635
+ }
636
+ gif_data = (gif->gif_data + gif->buffer_position);
637
+ gif_bytes = (gif_end - gif_data);
638
+
639
+ /* Ensure we have enough data for the 10-byte image descriptor + 1-byte
640
+ * gif trailer
641
+ */
642
+ if (gif_bytes < 12) {
643
+ return_value = GIF_INSUFFICIENT_FRAME_DATA;
644
+ goto gif_decode_frame_exit;
645
+ }
646
+
647
+ /* 10-byte Image Descriptor is:
648
+ *
649
+ * +0 CHAR Image Separator (0x2c)
650
+ * +1 SHORT Image Left Position
651
+ * +3 SHORT Image Top Position
652
+ * +5 SHORT Width
653
+ * +7 SHORT Height
654
+ * +9 CHAR __Packed Fields__
655
+ * 1BIT Local Colour Table Flag
656
+ * 1BIT Interlace Flag
657
+ * 1BIT Sort Flag
658
+ * 2BITS Reserved
659
+ * 3BITS Size of Local Colour Table
660
+ */
661
+ if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
662
+ return_value = GIF_DATA_ERROR;
663
+ goto gif_decode_frame_exit;
664
+ }
665
+ offset_x = gif_data[1] | (gif_data[2] << 8);
666
+ offset_y = gif_data[3] | (gif_data[4] << 8);
667
+ width = gif_data[5] | (gif_data[6] << 8);
668
+ height = gif_data[7] | (gif_data[8] << 8);
669
+
670
+ /* Boundary checking - shouldn't ever happen except unless the data has
671
+ * been modified since initialisation.
672
+ */
673
+ if ((offset_x + width > gif->width) ||
674
+ (offset_y + height > gif->height)) {
675
+ return_value = GIF_DATA_ERROR;
676
+ goto gif_decode_frame_exit;
677
+ }
678
+
679
+ /* Decode the flags */
680
+ flags = gif_data[9];
681
+ colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);
682
+ interlace = flags & GIF_INTERLACE_MASK;
683
+
684
+ /* Advance data pointer to next block either colour table or image
685
+ * data.
686
+ */
687
+ gif_data += 10;
688
+ gif_bytes = (gif_end - gif_data);
689
+
690
+ /* Set up the colour table */
691
+ if (flags & GIF_COLOUR_TABLE_MASK) {
692
+ if (gif_bytes < (int)(3 * colour_table_size)) {
693
+ return_value = GIF_INSUFFICIENT_FRAME_DATA;
694
+ goto gif_decode_frame_exit;
695
+ }
696
+ colour_table = gif->local_colour_table;
697
+ if (!clear_image) {
698
+ for (index = 0; index < colour_table_size; index++) {
699
+ /* Gif colour map contents are r,g,b.
700
+ *
701
+ * We want to pack them bytewise into the
702
+ * colour table, such that the red component
703
+ * is in byte 0 and the alpha component is in
704
+ * byte 3.
705
+ */
706
+ unsigned char *entry =
707
+ (unsigned char *) &colour_table[index];
708
+
709
+ entry[0] = gif_data[0]; /* r */
710
+ entry[1] = gif_data[1]; /* g */
711
+ entry[2] = gif_data[2]; /* b */
712
+ entry[3] = 0xff; /* a */
713
+
714
+ gif_data += 3;
715
+ }
716
+ } else {
717
+ gif_data += 3 * colour_table_size;
718
+ }
719
+ gif_bytes = (gif_end - gif_data);
720
+ } else {
721
+ colour_table = gif->global_colour_table;
722
+ }
723
+
724
+ /* Ensure sufficient data remains */
725
+ if (gif_bytes < 1) {
726
+ return_value = GIF_INSUFFICIENT_FRAME_DATA;
727
+ goto gif_decode_frame_exit;
728
+ }
729
+
730
+ /* check for an end marker */
731
+ if (gif_data[0] == GIF_TRAILER) {
732
+ return_value = GIF_OK;
733
+ goto gif_decode_frame_exit;
734
+ }
735
+
736
+ /* Get the frame data */
737
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
738
+ frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
739
+ if (!frame_data) {
740
+ return GIF_INSUFFICIENT_MEMORY;
741
+ }
742
+
743
+ /* If we are clearing the image we just clear, if not decode */
744
+ if (!clear_image) {
745
+ lzw_result res;
746
+ const uint8_t *stack_base;
747
+ const uint8_t *stack_pos;
748
+
749
+ /* Ensure we have enough data for a 1-byte LZW code size +
750
+ * 1-byte gif trailer
751
+ */
752
+ if (gif_bytes < 2) {
753
+ return_value = GIF_INSUFFICIENT_FRAME_DATA;
754
+ goto gif_decode_frame_exit;
755
+ }
756
+
757
+ /* If we only have a 1-byte LZW code size + 1-byte gif trailer,
758
+ * we're finished
759
+ */
760
+ if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {
761
+ return_value = GIF_OK;
762
+ goto gif_decode_frame_exit;
763
+ }
764
+
765
+ /* If the previous frame's disposal method requires we restore
766
+ * the background colour or this is the first frame, clear
767
+ * the frame data
768
+ */
769
+ if ((frame == 0) || (gif->decoded_frame == GIF_INVALID_FRAME)) {
770
+ memset((char*)frame_data,
771
+ GIF_TRANSPARENT_COLOUR,
772
+ gif->width * gif->height * sizeof(int));
773
+ gif->decoded_frame = frame;
774
+ /* The line below would fill the image with its
775
+ * background color, but because GIFs support
776
+ * transparency we likely wouldn't want to do that. */
777
+ /* memset((char*)frame_data, colour_table[gif->background_index], gif->width * gif->height * sizeof(int)); */
778
+ } else if ((frame != 0) &&
779
+ (gif->frames[frame - 1].disposal_method == GIF_FRAME_CLEAR)) {
780
+ return_value = gif_internal_decode_frame(gif,
781
+ (frame - 1),
782
+ true);
783
+ if (return_value != GIF_OK) {
784
+ goto gif_decode_frame_exit;
785
+ }
786
+
787
+ } else if ((frame != 0) &&
788
+ (gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) {
789
+ /*
790
+ * If the previous frame's disposal method requires we
791
+ * restore the previous image, find the last image set
792
+ * to "do not dispose" and get that frame data
793
+ */
794
+ int last_undisposed_frame = frame - 2;
795
+ while ((last_undisposed_frame >= 0) &&
796
+ (gif->frames[last_undisposed_frame].disposal_method == GIF_FRAME_RESTORE)) {
797
+ last_undisposed_frame--;
798
+ }
799
+
800
+ /* If we don't find one, clear the frame data */
801
+ if (last_undisposed_frame == -1) {
802
+ /* see notes above on transparency
803
+ * vs. background color
804
+ */
805
+ memset((char*)frame_data,
806
+ GIF_TRANSPARENT_COLOUR,
807
+ gif->width * gif->height * sizeof(int));
808
+ } else {
809
+ return_value = gif_internal_decode_frame(gif, last_undisposed_frame, false);
810
+ if (return_value != GIF_OK) {
811
+ goto gif_decode_frame_exit;
812
+ }
813
+ /* Get this frame's data */
814
+ assert(gif->bitmap_callbacks.bitmap_get_buffer);
815
+ frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
816
+ if (!frame_data) {
817
+ return GIF_INSUFFICIENT_MEMORY;
818
+ }
819
+ }
820
+ }
821
+ gif->decoded_frame = frame;
822
+ gif->buffer_position = (gif_data - gif->gif_data) + 1;
823
+
824
+ /* Initialise the LZW decoding */
825
+ res = lzw_decode_init(gif->lzw_ctx, gif->gif_data,
826
+ gif->buffer_size, gif->buffer_position,
827
+ gif_data[0], &stack_base, &stack_pos);
828
+ if (res != LZW_OK) {
829
+ return gif_error_from_lzw(res);
830
+ }
831
+
832
+ /* Decompress the data */
833
+ for (y = 0; y < height; y++) {
834
+ if (interlace) {
835
+ decode_y = gif_interlaced_line(height, y) + offset_y;
836
+ } else {
837
+ decode_y = y + offset_y;
838
+ }
839
+ frame_scanline = frame_data + offset_x + (decode_y * gif->width);
840
+
841
+ /* Rather than decoding pixel by pixel, we try to burst
842
+ * out streams of data to remove the need for end-of
843
+ * data checks every pixel.
844
+ */
845
+ x = width;
846
+ while (x > 0) {
847
+ burst_bytes = (stack_pos - stack_base);
848
+ if (burst_bytes > 0) {
849
+ if (burst_bytes > x) {
850
+ burst_bytes = x;
851
+ }
852
+ x -= burst_bytes;
853
+ while (burst_bytes-- > 0) {
854
+ colour = *--stack_pos;
855
+ if (((gif->frames[frame].transparency) &&
856
+ (colour != gif->frames[frame].transparency_index)) ||
857
+ (!gif->frames[frame].transparency)) {
858
+ *frame_scanline = colour_table[colour];
859
+ }
860
+ frame_scanline++;
861
+ }
862
+ } else {
863
+ res = lzw_decode(gif->lzw_ctx, &stack_pos);
864
+ if (res != LZW_OK) {
865
+ /* Unexpected end of frame, try to recover */
866
+ if (res == LZW_OK_EOD) {
867
+ return_value = GIF_OK;
868
+ } else {
869
+ return_value = gif_error_from_lzw(res);
870
+ }
871
+ goto gif_decode_frame_exit;
872
+ }
873
+ }
874
+ }
875
+ }
876
+ } else {
877
+ /* Clear our frame */
878
+ if (gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) {
879
+ for (y = 0; y < height; y++) {
880
+ frame_scanline = frame_data + offset_x + ((offset_y + y) * gif->width);
881
+ if (gif->frames[frame].transparency) {
882
+ memset(frame_scanline,
883
+ GIF_TRANSPARENT_COLOUR,
884
+ width * 4);
885
+ } else {
886
+ memset(frame_scanline,
887
+ colour_table[gif->background_index],
888
+ width * 4);
889
+ }
890
+ }
891
+ }
892
+ }
893
+ gif_decode_frame_exit:
894
+
895
+ /* Check if we should test for optimisation */
896
+ if (gif->frames[frame].virgin) {
897
+ if (gif->bitmap_callbacks.bitmap_test_opaque) {
898
+ gif->frames[frame].opaque = gif->bitmap_callbacks.bitmap_test_opaque(gif->frame_image);
899
+ } else {
900
+ gif->frames[frame].opaque = false;
901
+ }
902
+ gif->frames[frame].virgin = false;
903
+ }
904
+
905
+ if (gif->bitmap_callbacks.bitmap_set_opaque) {
906
+ gif->bitmap_callbacks.bitmap_set_opaque(gif->frame_image, gif->frames[frame].opaque);
907
+ }
908
+
909
+ if (gif->bitmap_callbacks.bitmap_modified) {
910
+ gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
911
+ }
912
+
913
+ /* Restore the buffer position */
914
+ gif->buffer_position = save_buffer_position;
915
+
916
+ return return_value;
917
+ }
918
+
919
+
920
+ /* exported function documented in libnsgif.h */
921
+ void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
922
+ {
923
+ memset(gif, 0, sizeof(gif_animation));
924
+ gif->bitmap_callbacks = *bitmap_callbacks;
925
+ gif->decoded_frame = GIF_INVALID_FRAME;
926
+ }
927
+
928
+
929
+ /* exported function documented in libnsgif.h */
930
+ gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
931
+ {
932
+ unsigned char *gif_data;
933
+ unsigned int index;
934
+ gif_result return_value;
935
+
936
+ /* Initialize values */
937
+ gif->buffer_size = size;
938
+ gif->gif_data = data;
939
+
940
+ if (gif->lzw_ctx == NULL) {
941
+ lzw_result res = lzw_context_create(
942
+ (struct lzw_ctx **)&gif->lzw_ctx);
943
+ if (res != LZW_OK) {
944
+ return gif_error_from_lzw(res);
945
+ }
946
+ }
947
+
948
+ /* Check for sufficient data to be a GIF (6-byte header + 7-byte
949
+ * logical screen descriptor)
950
+ */
951
+ if (gif->buffer_size < GIF_STANDARD_HEADER_SIZE) {
952
+ return GIF_INSUFFICIENT_DATA;
953
+ }
954
+
955
+ /* Get our current processing position */
956
+ gif_data = gif->gif_data + gif->buffer_position;
957
+
958
+ /* See if we should initialise the GIF */
959
+ if (gif->buffer_position == 0) {
960
+ /* We want everything to be NULL before we start so we've no
961
+ * chance of freeing bad pointers (paranoia)
962
+ */
963
+ gif->frame_image = NULL;
964
+ gif->frames = NULL;
965
+ gif->local_colour_table = NULL;
966
+ gif->global_colour_table = NULL;
967
+
968
+ /* The caller may have been lazy and not reset any values */
969
+ gif->frame_count = 0;
970
+ gif->frame_count_partial = 0;
971
+ gif->decoded_frame = GIF_INVALID_FRAME;
972
+
973
+ /* 6-byte GIF file header is:
974
+ *
975
+ * +0 3CHARS Signature ('GIF')
976
+ * +3 3CHARS Version ('87a' or '89a')
977
+ */
978
+ if (strncmp((const char *) gif_data, "GIF", 3) != 0) {
979
+ return GIF_DATA_ERROR;
980
+ }
981
+ gif_data += 3;
982
+
983
+ /* Ensure GIF reports version 87a or 89a */
984
+ /*
985
+ if ((strncmp(gif_data, "87a", 3) != 0) &&
986
+ (strncmp(gif_data, "89a", 3) != 0))
987
+ LOG(("Unknown GIF format - proceeding anyway"));
988
+ */
989
+ gif_data += 3;
990
+
991
+ /* 7-byte Logical Screen Descriptor is:
992
+ *
993
+ * +0 SHORT Logical Screen Width
994
+ * +2 SHORT Logical Screen Height
995
+ * +4 CHAR __Packed Fields__
996
+ * 1BIT Global Colour Table Flag
997
+ * 3BITS Colour Resolution
998
+ * 1BIT Sort Flag
999
+ * 3BITS Size of Global Colour Table
1000
+ * +5 CHAR Background Colour Index
1001
+ * +6 CHAR Pixel Aspect Ratio
1002
+ */
1003
+ gif->width = gif_data[0] | (gif_data[1] << 8);
1004
+ gif->height = gif_data[2] | (gif_data[3] << 8);
1005
+ gif->global_colours = (gif_data[4] & GIF_COLOUR_TABLE_MASK);
1006
+ gif->colour_table_size = (2 << (gif_data[4] & GIF_COLOUR_TABLE_SIZE_MASK));
1007
+ gif->background_index = gif_data[5];
1008
+ gif->aspect_ratio = gif_data[6];
1009
+ gif->loop_count = 1;
1010
+ gif_data += 7;
1011
+
1012
+ /* Some broken GIFs report the size as the screen size they
1013
+ * were created in. As such, we detect for the common cases and
1014
+ * set the sizes as 0 if they are found which results in the
1015
+ * GIF being the maximum size of the frames.
1016
+ */
1017
+ if (((gif->width == 640) && (gif->height == 480)) ||
1018
+ ((gif->width == 640) && (gif->height == 512)) ||
1019
+ ((gif->width == 800) && (gif->height == 600)) ||
1020
+ ((gif->width == 1024) && (gif->height == 768)) ||
1021
+ ((gif->width == 1280) && (gif->height == 1024)) ||
1022
+ ((gif->width == 1600) && (gif->height == 1200)) ||
1023
+ ((gif->width == 0) || (gif->height == 0)) ||
1024
+ ((gif->width > 2048) || (gif->height > 2048))) {
1025
+ gif->width = 1;
1026
+ gif->height = 1;
1027
+ }
1028
+
1029
+ /* Allocate some data irrespective of whether we've got any
1030
+ * colour tables. We always get the maximum size in case a GIF
1031
+ * is lying to us. It's far better to give the wrong colours
1032
+ * than to trample over some memory somewhere.
1033
+ */
1034
+ gif->global_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
1035
+ gif->local_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
1036
+ if ((gif->global_colour_table == NULL) ||
1037
+ (gif->local_colour_table == NULL)) {
1038
+ gif_finalise(gif);
1039
+ return GIF_INSUFFICIENT_MEMORY;
1040
+ }
1041
+
1042
+ /* Set the first colour to a value that will never occur in
1043
+ * reality so we know if we've processed it
1044
+ */
1045
+ gif->global_colour_table[0] = GIF_PROCESS_COLOURS;
1046
+
1047
+ /* Check if the GIF has no frame data (13-byte header + 1-byte
1048
+ * termination block) Although generally useless, the GIF
1049
+ * specification does not expressly prohibit this
1050
+ */
1051
+ if (gif->buffer_size == (GIF_STANDARD_HEADER_SIZE + 1)) {
1052
+ if (gif_data[0] == GIF_TRAILER) {
1053
+ return GIF_OK;
1054
+ } else {
1055
+ return GIF_INSUFFICIENT_DATA;
1056
+ }
1057
+ }
1058
+
1059
+ /* Initialise enough workspace for a frame */
1060
+ if ((gif->frames = (gif_frame *)malloc(sizeof(gif_frame))) == NULL) {
1061
+ gif_finalise(gif);
1062
+ return GIF_INSUFFICIENT_MEMORY;
1063
+ }
1064
+ gif->frame_holders = 1;
1065
+
1066
+ /* Initialise the bitmap header */
1067
+ assert(gif->bitmap_callbacks.bitmap_create);
1068
+ gif->frame_image = gif->bitmap_callbacks.bitmap_create(gif->width, gif->height);
1069
+ if (gif->frame_image == NULL) {
1070
+ gif_finalise(gif);
1071
+ return GIF_INSUFFICIENT_MEMORY;
1072
+ }
1073
+
1074
+ /* Remember we've done this now */
1075
+ gif->buffer_position = gif_data - gif->gif_data;
1076
+ }
1077
+
1078
+ /* Do the colour map if we haven't already. As the top byte is always
1079
+ * 0xff or 0x00 depending on the transparency we know if it's been
1080
+ * filled in.
1081
+ */
1082
+ if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) {
1083
+ /* Check for a global colour map signified by bit 7 */
1084
+ if (gif->global_colours) {
1085
+ if (gif->buffer_size < (gif->colour_table_size * 3 + GIF_STANDARD_HEADER_SIZE)) {
1086
+ return GIF_INSUFFICIENT_DATA;
1087
+ }
1088
+ for (index = 0; index < gif->colour_table_size; index++) {
1089
+ /* Gif colour map contents are r,g,b.
1090
+ *
1091
+ * We want to pack them bytewise into the
1092
+ * colour table, such that the red component
1093
+ * is in byte 0 and the alpha component is in
1094
+ * byte 3.
1095
+ */
1096
+ unsigned char *entry = (unsigned char *) &gif->
1097
+ global_colour_table[index];
1098
+
1099
+ entry[0] = gif_data[0]; /* r */
1100
+ entry[1] = gif_data[1]; /* g */
1101
+ entry[2] = gif_data[2]; /* b */
1102
+ entry[3] = 0xff; /* a */
1103
+
1104
+ gif_data += 3;
1105
+ }
1106
+ gif->buffer_position = (gif_data - gif->gif_data);
1107
+ } else {
1108
+ /* Create a default colour table with the first two
1109
+ * colours as black and white
1110
+ */
1111
+ unsigned int *entry = gif->global_colour_table;
1112
+
1113
+ entry[0] = 0x00000000;
1114
+ /* Force Alpha channel to opaque */
1115
+ ((unsigned char *) entry)[3] = 0xff;
1116
+
1117
+ entry[1] = 0xffffffff;
1118
+ }
1119
+ }
1120
+
1121
+ /* Repeatedly try to initialise frames */
1122
+ while ((return_value = gif_initialise_frame(gif)) == GIF_WORKING);
1123
+
1124
+ /* If there was a memory error tell the caller */
1125
+ if ((return_value == GIF_INSUFFICIENT_MEMORY) ||
1126
+ (return_value == GIF_DATA_ERROR)) {
1127
+ return return_value;
1128
+ }
1129
+
1130
+ /* If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a
1131
+ * GIF_INSUFFICIENT_FRAME_DATA
1132
+ */
1133
+ if ((return_value == GIF_INSUFFICIENT_DATA) &&
1134
+ (gif->frame_count_partial > 0)) {
1135
+ return GIF_INSUFFICIENT_FRAME_DATA;
1136
+ }
1137
+
1138
+ /* Return how many we got */
1139
+ return return_value;
1140
+ }
1141
+
1142
+
1143
+ /* exported function documented in libnsgif.h */
1144
+ gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
1145
+ {
1146
+ return gif_internal_decode_frame(gif, frame, false);
1147
+ }
1148
+
1149
+
1150
+ /* exported function documented in libnsgif.h */
1151
+ void gif_finalise(gif_animation *gif)
1152
+ {
1153
+ /* Release all our memory blocks */
1154
+ if (gif->frame_image) {
1155
+ assert(gif->bitmap_callbacks.bitmap_destroy);
1156
+ gif->bitmap_callbacks.bitmap_destroy(gif->frame_image);
1157
+ }
1158
+
1159
+ gif->frame_image = NULL;
1160
+ free(gif->frames);
1161
+ gif->frames = NULL;
1162
+ free(gif->local_colour_table);
1163
+ gif->local_colour_table = NULL;
1164
+ free(gif->global_colour_table);
1165
+ gif->global_colour_table = NULL;
1166
+
1167
+ lzw_context_destroy(gif->lzw_ctx);
1168
+ gif->lzw_ctx = NULL;
1169
+ }