diff options
| author | Chong Yidong | 2011-06-06 17:03:43 -0400 |
|---|---|---|
| committer | Chong Yidong | 2011-06-06 17:03:43 -0400 |
| commit | 60002bf5c84f68a641c4acebe56cd1e36b6a3e86 (patch) | |
| tree | 9e6c1c3e9e03a9f1cc0beb8cb5a15b010a1db389 | |
| parent | 9afafefb1b8341816a20eac9491f3f9aaf2aea61 (diff) | |
| download | emacs-60002bf5c84f68a641c4acebe56cd1e36b6a3e86.tar.gz emacs-60002bf5c84f68a641c4acebe56cd1e36b6a3e86.zip | |
* src/image.c (gif_load): Implement gif89a spec "no disposal" method.
| -rw-r--r-- | src/ChangeLog | 4 | ||||
| -rw-r--r-- | src/image.c | 232 |
2 files changed, 129 insertions, 107 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 7452b53da5b..6afb661e759 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | 2011-06-06 Chong Yidong <cyd@stupidchicken.com> | ||
| 2 | |||
| 3 | * image.c (gif_load): Implement gif89a spec "no disposal" method. | ||
| 4 | |||
| 1 | 2011-06-06 Paul Eggert <eggert@cs.ucla.edu> | 5 | 2011-06-06 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 6 | ||
| 3 | Cons<->int and similar integer overflow fixes (Bug#8794). | 7 | Cons<->int and similar integer overflow fixes (Bug#8794). |
diff --git a/src/image.c b/src/image.c index a179568cb85..cdf05c78764 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -7074,22 +7074,19 @@ static const int interlace_increment[] = {8, 8, 4, 2}; | |||
| 7074 | static int | 7074 | static int |
| 7075 | gif_load (struct frame *f, struct image *img) | 7075 | gif_load (struct frame *f, struct image *img) |
| 7076 | { | 7076 | { |
| 7077 | Lisp_Object file, specified_file; | 7077 | Lisp_Object file; |
| 7078 | Lisp_Object specified_data; | 7078 | int rc, width, height, x, y, i, j; |
| 7079 | int rc, width, height, x, y, i; | ||
| 7080 | boolean transparent_p = 0; | ||
| 7081 | XImagePtr ximg; | 7079 | XImagePtr ximg; |
| 7082 | ColorMapObject *gif_color_map; | 7080 | ColorMapObject *gif_color_map; |
| 7083 | unsigned long pixel_colors[256]; | 7081 | unsigned long pixel_colors[256]; |
| 7084 | GifFileType *gif; | 7082 | GifFileType *gif; |
| 7085 | Lisp_Object image; | 7083 | int image_height, image_width; |
| 7086 | int ino, image_height, image_width; | ||
| 7087 | gif_memory_source memsrc; | 7084 | gif_memory_source memsrc; |
| 7088 | unsigned char *raster; | 7085 | Lisp_Object specified_bg = image_spec_value (img->spec, QCbackground, NULL); |
| 7089 | unsigned int transparency_color_index IF_LINT (= 0); | 7086 | Lisp_Object specified_file = image_spec_value (img->spec, QCfile, NULL); |
| 7090 | 7087 | Lisp_Object specified_data = image_spec_value (img->spec, QCdata, NULL); | |
| 7091 | specified_file = image_spec_value (img->spec, QCfile, NULL); | 7088 | unsigned long bgcolor = 0; |
| 7092 | specified_data = image_spec_value (img->spec, QCdata, NULL); | 7089 | int idx; |
| 7093 | 7090 | ||
| 7094 | if (NILP (specified_data)) | 7091 | if (NILP (specified_data)) |
| 7095 | { | 7092 | { |
| @@ -7140,40 +7137,31 @@ gif_load (struct frame *f, struct image *img) | |||
| 7140 | 7137 | ||
| 7141 | /* Read entire contents. */ | 7138 | /* Read entire contents. */ |
| 7142 | rc = fn_DGifSlurp (gif); | 7139 | rc = fn_DGifSlurp (gif); |
| 7143 | if (rc == GIF_ERROR) | 7140 | if (rc == GIF_ERROR || gif->ImageCount <= 0) |
| 7144 | { | 7141 | { |
| 7145 | image_error ("Error reading `%s'", img->spec, Qnil); | 7142 | image_error ("Error reading `%s'", img->spec, Qnil); |
| 7146 | fn_DGifCloseFile (gif); | 7143 | fn_DGifCloseFile (gif); |
| 7147 | return 0; | 7144 | return 0; |
| 7148 | } | 7145 | } |
| 7149 | 7146 | ||
| 7150 | image = image_spec_value (img->spec, QCindex, NULL); | 7147 | /* Which sub-image are we to display? */ |
| 7151 | ino = INTEGERP (image) ? XFASTINT (image) : 0; | 7148 | { |
| 7152 | if (ino >= gif->ImageCount) | 7149 | Lisp_Object index = image_spec_value (img->spec, QCindex, NULL); |
| 7153 | { | 7150 | idx = INTEGERP (index) ? XFASTINT (index) : 0; |
| 7154 | image_error ("Invalid image number `%s' in image `%s'", | 7151 | if (idx < 0 || idx >= gif->ImageCount) |
| 7155 | image, img->spec); | ||
| 7156 | fn_DGifCloseFile (gif); | ||
| 7157 | return 0; | ||
| 7158 | } | ||
| 7159 | |||
| 7160 | for (i = 0; i < gif->SavedImages[ino].ExtensionBlockCount; i++) | ||
| 7161 | if ((gif->SavedImages[ino].ExtensionBlocks[i].Function | ||
| 7162 | == GIF_LOCAL_DESCRIPTOR_EXTENSION) | ||
| 7163 | && gif->SavedImages[ino].ExtensionBlocks[i].ByteCount == 4 | ||
| 7164 | /* Transparency enabled? */ | ||
| 7165 | && gif->SavedImages[ino].ExtensionBlocks[i].Bytes[0] & 1) | ||
| 7166 | { | 7152 | { |
| 7167 | transparent_p = 1; | 7153 | image_error ("Invalid image number `%s' in image `%s'", |
| 7168 | transparency_color_index | 7154 | index, img->spec); |
| 7169 | = (unsigned char) gif->SavedImages[ino].ExtensionBlocks[i].Bytes[3]; | 7155 | fn_DGifCloseFile (gif); |
| 7156 | return 0; | ||
| 7170 | } | 7157 | } |
| 7158 | } | ||
| 7171 | 7159 | ||
| 7172 | img->corners[TOP_CORNER] = gif->SavedImages[ino].ImageDesc.Top; | 7160 | img->corners[TOP_CORNER] = gif->SavedImages[idx].ImageDesc.Top; |
| 7173 | img->corners[LEFT_CORNER] = gif->SavedImages[ino].ImageDesc.Left; | 7161 | img->corners[LEFT_CORNER] = gif->SavedImages[idx].ImageDesc.Left; |
| 7174 | image_height = gif->SavedImages[ino].ImageDesc.Height; | 7162 | image_height = gif->SavedImages[idx].ImageDesc.Height; |
| 7175 | img->corners[BOT_CORNER] = img->corners[TOP_CORNER] + image_height; | 7163 | img->corners[BOT_CORNER] = img->corners[TOP_CORNER] + image_height; |
| 7176 | image_width = gif->SavedImages[ino].ImageDesc.Width; | 7164 | image_width = gif->SavedImages[idx].ImageDesc.Width; |
| 7177 | img->corners[RIGHT_CORNER] = img->corners[LEFT_CORNER] + image_width; | 7165 | img->corners[RIGHT_CORNER] = img->corners[LEFT_CORNER] + image_width; |
| 7178 | 7166 | ||
| 7179 | width = img->width = max (gif->SWidth, | 7167 | width = img->width = max (gif->SWidth, |
| @@ -7197,44 +7185,10 @@ gif_load (struct frame *f, struct image *img) | |||
| 7197 | return 0; | 7185 | return 0; |
| 7198 | } | 7186 | } |
| 7199 | 7187 | ||
| 7200 | /* Allocate colors. */ | 7188 | /* Clear the part of the screen image not covered by the image. |
| 7201 | gif_color_map = gif->SavedImages[ino].ImageDesc.ColorMap; | 7189 | Full animated GIF support requires more here (see the gif89 spec, |
| 7202 | if (!gif_color_map) | 7190 | disposal methods). Let's simply assume that the part not covered |
| 7203 | gif_color_map = gif->SColorMap; | 7191 | by a sub-image is in the frame's background color. */ |
| 7204 | init_color_table (); | ||
| 7205 | memset (pixel_colors, 0, sizeof pixel_colors); | ||
| 7206 | |||
| 7207 | if (gif_color_map) | ||
| 7208 | for (i = 0; i < gif_color_map->ColorCount; ++i) | ||
| 7209 | { | ||
| 7210 | if (transparent_p && transparency_color_index == i) | ||
| 7211 | { | ||
| 7212 | Lisp_Object specified_bg | ||
| 7213 | = image_spec_value (img->spec, QCbackground, NULL); | ||
| 7214 | pixel_colors[i] = STRINGP (specified_bg) | ||
| 7215 | ? x_alloc_image_color (f, img, specified_bg, | ||
| 7216 | FRAME_BACKGROUND_PIXEL (f)) | ||
| 7217 | : FRAME_BACKGROUND_PIXEL (f); | ||
| 7218 | } | ||
| 7219 | else | ||
| 7220 | { | ||
| 7221 | int r = gif_color_map->Colors[i].Red << 8; | ||
| 7222 | int g = gif_color_map->Colors[i].Green << 8; | ||
| 7223 | int b = gif_color_map->Colors[i].Blue << 8; | ||
| 7224 | pixel_colors[i] = lookup_rgb_color (f, r, g, b); | ||
| 7225 | } | ||
| 7226 | } | ||
| 7227 | |||
| 7228 | #ifdef COLOR_TABLE_SUPPORT | ||
| 7229 | img->colors = colors_in_color_table (&img->ncolors); | ||
| 7230 | free_color_table (); | ||
| 7231 | #endif /* COLOR_TABLE_SUPPORT */ | ||
| 7232 | |||
| 7233 | /* Clear the part of the screen image that are not covered by | ||
| 7234 | the image from the GIF file. Full animated GIF support | ||
| 7235 | requires more than can be done here (see the gif89 spec, | ||
| 7236 | disposal methods). Let's simply assume that the part | ||
| 7237 | not covered by a sub-image is in the frame's background color. */ | ||
| 7238 | for (y = 0; y < img->corners[TOP_CORNER]; ++y) | 7192 | for (y = 0; y < img->corners[TOP_CORNER]; ++y) |
| 7239 | for (x = 0; x < width; ++x) | 7193 | for (x = 0; x < width; ++x) |
| 7240 | XPutPixel (ximg, x, y, FRAME_BACKGROUND_PIXEL (f)); | 7194 | XPutPixel (ximg, x, y, FRAME_BACKGROUND_PIXEL (f)); |
| @@ -7251,55 +7205,119 @@ gif_load (struct frame *f, struct image *img) | |||
| 7251 | XPutPixel (ximg, x, y, FRAME_BACKGROUND_PIXEL (f)); | 7205 | XPutPixel (ximg, x, y, FRAME_BACKGROUND_PIXEL (f)); |
| 7252 | } | 7206 | } |
| 7253 | 7207 | ||
| 7254 | /* Read the GIF image into the X image. We use a local variable | 7208 | /* Read the GIF image into the X image. */ |
| 7255 | `raster' here because RasterBits below is a char *, and invites | ||
| 7256 | problems with bytes >= 0x80. */ | ||
| 7257 | raster = (unsigned char *) gif->SavedImages[ino].RasterBits; | ||
| 7258 | |||
| 7259 | if (gif->SavedImages[ino].ImageDesc.Interlace) | ||
| 7260 | { | ||
| 7261 | int pass; | ||
| 7262 | int row = interlace_start[0]; | ||
| 7263 | 7209 | ||
| 7264 | pass = 0; | 7210 | /* FIXME: With the current implementation, loading an animated gif |
| 7211 | is quadratic in the number of animation frames, since each frame | ||
| 7212 | is a separate struct image. We must provide a way for a single | ||
| 7213 | gif_load call to construct and save all animation frames. */ | ||
| 7265 | 7214 | ||
| 7266 | for (y = 0; y < image_height; y++) | 7215 | init_color_table (); |
| 7216 | if (STRINGP (specified_bg)) | ||
| 7217 | bgcolor = x_alloc_image_color (f, img, specified_bg, | ||
| 7218 | FRAME_BACKGROUND_PIXEL (f)); | ||
| 7219 | for (j = 0; j <= idx; ++j) | ||
| 7220 | { | ||
| 7221 | /* We use a local variable `raster' here because RasterBits is a | ||
| 7222 | char *, which invites problems with bytes >= 0x80. */ | ||
| 7223 | struct SavedImage *subimage = gif->SavedImages + j; | ||
| 7224 | unsigned char *raster = (unsigned char *) subimage->RasterBits; | ||
| 7225 | int transparency_color_index = -1; | ||
| 7226 | int disposal = 0; | ||
| 7227 | |||
| 7228 | /* Find the Graphic Control Extension block for this sub-image. | ||
| 7229 | Extract the disposal method and transparency color. */ | ||
| 7230 | for (i = 0; i < subimage->ExtensionBlockCount; i++) | ||
| 7267 | { | 7231 | { |
| 7268 | if (row >= image_height) | 7232 | ExtensionBlock *extblock = subimage->ExtensionBlocks + i; |
| 7269 | { | ||
| 7270 | row = interlace_start[++pass]; | ||
| 7271 | while (row >= image_height) | ||
| 7272 | row = interlace_start[++pass]; | ||
| 7273 | } | ||
| 7274 | 7233 | ||
| 7275 | for (x = 0; x < image_width; x++) | 7234 | if ((extblock->Function == GIF_LOCAL_DESCRIPTOR_EXTENSION) |
| 7235 | && extblock->ByteCount == 4 | ||
| 7236 | && extblock->Bytes[0] & 1) | ||
| 7276 | { | 7237 | { |
| 7277 | int c = raster[(y * image_width) + x]; | 7238 | /* From gif89a spec: 1 = "keep in place", 2 = "restore |
| 7278 | XPutPixel (ximg, x + img->corners[LEFT_CORNER], | 7239 | to background". Treat any other value like 2. */ |
| 7279 | row + img->corners[TOP_CORNER], pixel_colors[c]); | 7240 | disposal = (extblock->Bytes[0] >> 2) & 7; |
| 7241 | transparency_color_index = extblock->Bytes[3]; | ||
| 7242 | break; | ||
| 7280 | } | 7243 | } |
| 7281 | |||
| 7282 | row += interlace_increment[pass]; | ||
| 7283 | } | 7244 | } |
| 7284 | } | 7245 | |
| 7285 | else | 7246 | /* We can't "keep in place" the first subimage. */ |
| 7286 | { | 7247 | if (j == 0) |
| 7287 | for (y = 0; y < image_height; ++y) | 7248 | disposal = 2; |
| 7288 | for (x = 0; x < image_width; ++x) | 7249 | |
| 7250 | /* Allocate subimage colors. */ | ||
| 7251 | memset (pixel_colors, 0, sizeof pixel_colors); | ||
| 7252 | gif_color_map = subimage->ImageDesc.ColorMap; | ||
| 7253 | if (!gif_color_map) | ||
| 7254 | gif_color_map = gif->SColorMap; | ||
| 7255 | |||
| 7256 | if (gif_color_map) | ||
| 7257 | for (i = 0; i < gif_color_map->ColorCount; ++i) | ||
| 7289 | { | 7258 | { |
| 7290 | int c = raster[y * image_width + x]; | 7259 | if (transparency_color_index == i) |
| 7291 | XPutPixel (ximg, x + img->corners[LEFT_CORNER], | 7260 | pixel_colors[i] = STRINGP (specified_bg) |
| 7292 | y + img->corners[TOP_CORNER], pixel_colors[c]); | 7261 | ? bgcolor : FRAME_BACKGROUND_PIXEL (f); |
| 7262 | else | ||
| 7263 | { | ||
| 7264 | int r = gif_color_map->Colors[i].Red << 8; | ||
| 7265 | int g = gif_color_map->Colors[i].Green << 8; | ||
| 7266 | int b = gif_color_map->Colors[i].Blue << 8; | ||
| 7267 | pixel_colors[i] = lookup_rgb_color (f, r, g, b); | ||
| 7268 | } | ||
| 7293 | } | 7269 | } |
| 7270 | |||
| 7271 | /* Apply the pixel values. */ | ||
| 7272 | if (gif->SavedImages[j].ImageDesc.Interlace) | ||
| 7273 | { | ||
| 7274 | int row, pass; | ||
| 7275 | |||
| 7276 | for (y = 0, row = interlace_start[0], pass = 0; | ||
| 7277 | y < image_height; | ||
| 7278 | y++, row += interlace_increment[pass]) | ||
| 7279 | { | ||
| 7280 | if (row >= image_height) | ||
| 7281 | { | ||
| 7282 | row = interlace_start[++pass]; | ||
| 7283 | while (row >= image_height) | ||
| 7284 | row = interlace_start[++pass]; | ||
| 7285 | } | ||
| 7286 | |||
| 7287 | for (x = 0; x < image_width; x++) | ||
| 7288 | { | ||
| 7289 | int c = raster[y * image_width + x]; | ||
| 7290 | if (transparency_color_index != c || disposal != 1) | ||
| 7291 | XPutPixel (ximg, x + img->corners[LEFT_CORNER], | ||
| 7292 | row + img->corners[TOP_CORNER], pixel_colors[c]); | ||
| 7293 | } | ||
| 7294 | } | ||
| 7295 | } | ||
| 7296 | else | ||
| 7297 | { | ||
| 7298 | for (y = 0; y < image_height; ++y) | ||
| 7299 | for (x = 0; x < image_width; ++x) | ||
| 7300 | { | ||
| 7301 | int c = raster[y * image_width + x]; | ||
| 7302 | if (transparency_color_index != c || disposal != 1) | ||
| 7303 | XPutPixel (ximg, x + img->corners[LEFT_CORNER], | ||
| 7304 | y + img->corners[TOP_CORNER], pixel_colors[c]); | ||
| 7305 | } | ||
| 7306 | } | ||
| 7294 | } | 7307 | } |
| 7295 | 7308 | ||
| 7309 | #ifdef COLOR_TABLE_SUPPORT | ||
| 7310 | img->colors = colors_in_color_table (&img->ncolors); | ||
| 7311 | free_color_table (); | ||
| 7312 | #endif /* COLOR_TABLE_SUPPORT */ | ||
| 7313 | |||
| 7296 | /* Save GIF image extension data for `image-metadata'. | 7314 | /* Save GIF image extension data for `image-metadata'. |
| 7297 | Format is (count IMAGES extension-data (FUNCTION "BYTES" ...)). */ | 7315 | Format is (count IMAGES extension-data (FUNCTION "BYTES" ...)). */ |
| 7298 | img->data.lisp_val = Qnil; | 7316 | img->data.lisp_val = Qnil; |
| 7299 | if (gif->SavedImages[ino].ExtensionBlockCount > 0) | 7317 | if (gif->SavedImages[idx].ExtensionBlockCount > 0) |
| 7300 | { | 7318 | { |
| 7301 | ExtensionBlock *ext = gif->SavedImages[ino].ExtensionBlocks; | 7319 | ExtensionBlock *ext = gif->SavedImages[idx].ExtensionBlocks; |
| 7302 | for (i = 0; i < gif->SavedImages[ino].ExtensionBlockCount; i++, ext++) | 7320 | for (i = 0; i < gif->SavedImages[idx].ExtensionBlockCount; i++, ext++) |
| 7303 | /* Append (... FUNCTION "BYTES") */ | 7321 | /* Append (... FUNCTION "BYTES") */ |
| 7304 | img->data.lisp_val = Fcons (make_unibyte_string (ext->Bytes, ext->ByteCount), | 7322 | img->data.lisp_val = Fcons (make_unibyte_string (ext->Bytes, ext->ByteCount), |
| 7305 | Fcons (make_number (ext->Function), | 7323 | Fcons (make_number (ext->Function), |