LiteRGSS 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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
+ }