Libav 0.7.1
|
00001 /* 00002 * GIF decoder 00003 * Copyright (c) 2003 Fabrice Bellard 00004 * Copyright (c) 2006 Baptiste Coudurier 00005 * 00006 * This file is part of Libav. 00007 * 00008 * Libav is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * Libav is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with Libav; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 //#define DEBUG 00024 00025 #include "libavutil/imgutils.h" 00026 #include "avcodec.h" 00027 #include "bytestream.h" 00028 #include "lzw.h" 00029 00030 #define GCE_DISPOSAL_NONE 0 00031 #define GCE_DISPOSAL_INPLACE 1 00032 #define GCE_DISPOSAL_BACKGROUND 2 00033 #define GCE_DISPOSAL_RESTORE 3 00034 00035 typedef struct GifState { 00036 AVFrame picture; 00037 int screen_width; 00038 int screen_height; 00039 int bits_per_pixel; 00040 int background_color_index; 00041 int transparent_color_index; 00042 int color_resolution; 00043 uint32_t *image_palette; 00044 00045 /* after the frame is displayed, the disposal method is used */ 00046 int gce_disposal; 00047 /* delay during which the frame is shown */ 00048 int gce_delay; 00049 00050 /* LZW compatible decoder */ 00051 const uint8_t *bytestream; 00052 const uint8_t *bytestream_end; 00053 LZWState *lzw; 00054 00055 /* aux buffers */ 00056 uint8_t global_palette[256 * 3]; 00057 uint8_t local_palette[256 * 3]; 00058 00059 AVCodecContext* avctx; 00060 } GifState; 00061 00062 static const uint8_t gif87a_sig[6] = "GIF87a"; 00063 static const uint8_t gif89a_sig[6] = "GIF89a"; 00064 00065 static int gif_read_image(GifState *s) 00066 { 00067 int left, top, width, height, bits_per_pixel, code_size, flags; 00068 int is_interleaved, has_local_palette, y, pass, y1, linesize, n, i; 00069 uint8_t *ptr, *spal, *palette, *ptr1; 00070 00071 left = bytestream_get_le16(&s->bytestream); 00072 top = bytestream_get_le16(&s->bytestream); 00073 width = bytestream_get_le16(&s->bytestream); 00074 height = bytestream_get_le16(&s->bytestream); 00075 flags = bytestream_get_byte(&s->bytestream); 00076 is_interleaved = flags & 0x40; 00077 has_local_palette = flags & 0x80; 00078 bits_per_pixel = (flags & 0x07) + 1; 00079 00080 av_dlog(s->avctx, "gif: image x=%d y=%d w=%d h=%d\n", left, top, width, height); 00081 00082 if (has_local_palette) { 00083 bytestream_get_buffer(&s->bytestream, s->local_palette, 3 * (1 << bits_per_pixel)); 00084 palette = s->local_palette; 00085 } else { 00086 palette = s->global_palette; 00087 bits_per_pixel = s->bits_per_pixel; 00088 } 00089 00090 /* verify that all the image is inside the screen dimensions */ 00091 if (left + width > s->screen_width || 00092 top + height > s->screen_height) 00093 return AVERROR(EINVAL); 00094 00095 /* build the palette */ 00096 n = (1 << bits_per_pixel); 00097 spal = palette; 00098 for(i = 0; i < n; i++) { 00099 s->image_palette[i] = (0xff << 24) | AV_RB24(spal); 00100 spal += 3; 00101 } 00102 for(; i < 256; i++) 00103 s->image_palette[i] = (0xff << 24); 00104 /* handle transparency */ 00105 if (s->transparent_color_index >= 0) 00106 s->image_palette[s->transparent_color_index] = 0; 00107 00108 /* now get the image data */ 00109 code_size = bytestream_get_byte(&s->bytestream); 00110 ff_lzw_decode_init(s->lzw, code_size, s->bytestream, 00111 s->bytestream_end - s->bytestream, FF_LZW_GIF); 00112 00113 /* read all the image */ 00114 linesize = s->picture.linesize[0]; 00115 ptr1 = s->picture.data[0] + top * linesize + left; 00116 ptr = ptr1; 00117 pass = 0; 00118 y1 = 0; 00119 for (y = 0; y < height; y++) { 00120 ff_lzw_decode(s->lzw, ptr, width); 00121 if (is_interleaved) { 00122 switch(pass) { 00123 default: 00124 case 0: 00125 case 1: 00126 y1 += 8; 00127 ptr += linesize * 8; 00128 if (y1 >= height) { 00129 y1 = pass ? 2 : 4; 00130 ptr = ptr1 + linesize * y1; 00131 pass++; 00132 } 00133 break; 00134 case 2: 00135 y1 += 4; 00136 ptr += linesize * 4; 00137 if (y1 >= height) { 00138 y1 = 1; 00139 ptr = ptr1 + linesize; 00140 pass++; 00141 } 00142 break; 00143 case 3: 00144 y1 += 2; 00145 ptr += linesize * 2; 00146 break; 00147 } 00148 } else { 00149 ptr += linesize; 00150 } 00151 } 00152 /* read the garbage data until end marker is found */ 00153 ff_lzw_decode_tail(s->lzw); 00154 s->bytestream = ff_lzw_cur_ptr(s->lzw); 00155 return 0; 00156 } 00157 00158 static int gif_read_extension(GifState *s) 00159 { 00160 int ext_code, ext_len, i, gce_flags, gce_transparent_index; 00161 00162 /* extension */ 00163 ext_code = bytestream_get_byte(&s->bytestream); 00164 ext_len = bytestream_get_byte(&s->bytestream); 00165 00166 av_dlog(s->avctx, "gif: ext_code=0x%x len=%d\n", ext_code, ext_len); 00167 00168 switch(ext_code) { 00169 case 0xf9: 00170 if (ext_len != 4) 00171 goto discard_ext; 00172 s->transparent_color_index = -1; 00173 gce_flags = bytestream_get_byte(&s->bytestream); 00174 s->gce_delay = bytestream_get_le16(&s->bytestream); 00175 gce_transparent_index = bytestream_get_byte(&s->bytestream); 00176 if (gce_flags & 0x01) 00177 s->transparent_color_index = gce_transparent_index; 00178 else 00179 s->transparent_color_index = -1; 00180 s->gce_disposal = (gce_flags >> 2) & 0x7; 00181 00182 av_dlog(s->avctx, "gif: gce_flags=%x delay=%d tcolor=%d disposal=%d\n", 00183 gce_flags, s->gce_delay, 00184 s->transparent_color_index, s->gce_disposal); 00185 00186 ext_len = bytestream_get_byte(&s->bytestream); 00187 break; 00188 } 00189 00190 /* NOTE: many extension blocks can come after */ 00191 discard_ext: 00192 while (ext_len != 0) { 00193 for (i = 0; i < ext_len; i++) 00194 bytestream_get_byte(&s->bytestream); 00195 ext_len = bytestream_get_byte(&s->bytestream); 00196 00197 av_dlog(s->avctx, "gif: ext_len1=%d\n", ext_len); 00198 } 00199 return 0; 00200 } 00201 00202 static int gif_read_header1(GifState *s) 00203 { 00204 uint8_t sig[6]; 00205 int v, n; 00206 int has_global_palette; 00207 00208 if (s->bytestream_end < s->bytestream + 13) 00209 return -1; 00210 00211 /* read gif signature */ 00212 bytestream_get_buffer(&s->bytestream, sig, 6); 00213 if (memcmp(sig, gif87a_sig, 6) != 0 && 00214 memcmp(sig, gif89a_sig, 6) != 0) 00215 return -1; 00216 00217 /* read screen header */ 00218 s->transparent_color_index = -1; 00219 s->screen_width = bytestream_get_le16(&s->bytestream); 00220 s->screen_height = bytestream_get_le16(&s->bytestream); 00221 if( (unsigned)s->screen_width > 32767 00222 || (unsigned)s->screen_height > 32767){ 00223 av_log(NULL, AV_LOG_ERROR, "picture size too large\n"); 00224 return -1; 00225 } 00226 00227 v = bytestream_get_byte(&s->bytestream); 00228 s->color_resolution = ((v & 0x70) >> 4) + 1; 00229 has_global_palette = (v & 0x80); 00230 s->bits_per_pixel = (v & 0x07) + 1; 00231 s->background_color_index = bytestream_get_byte(&s->bytestream); 00232 bytestream_get_byte(&s->bytestream); /* ignored */ 00233 00234 av_dlog(s->avctx, "gif: screen_w=%d screen_h=%d bpp=%d global_palette=%d\n", 00235 s->screen_width, s->screen_height, s->bits_per_pixel, 00236 has_global_palette); 00237 00238 if (has_global_palette) { 00239 n = 1 << s->bits_per_pixel; 00240 if (s->bytestream_end < s->bytestream + n * 3) 00241 return -1; 00242 bytestream_get_buffer(&s->bytestream, s->global_palette, n * 3); 00243 } 00244 return 0; 00245 } 00246 00247 static int gif_parse_next_image(GifState *s) 00248 { 00249 while (s->bytestream < s->bytestream_end) { 00250 int code = bytestream_get_byte(&s->bytestream); 00251 00252 av_dlog(s->avctx, "gif: code=%02x '%c'\n", code, code); 00253 00254 switch (code) { 00255 case ',': 00256 return gif_read_image(s); 00257 case '!': 00258 if (gif_read_extension(s) < 0) 00259 return -1; 00260 break; 00261 case ';': 00262 /* end of image */ 00263 default: 00264 /* error or erroneous EOF */ 00265 return -1; 00266 } 00267 } 00268 return -1; 00269 } 00270 00271 static av_cold int gif_decode_init(AVCodecContext *avctx) 00272 { 00273 GifState *s = avctx->priv_data; 00274 00275 s->avctx = avctx; 00276 00277 avcodec_get_frame_defaults(&s->picture); 00278 avctx->coded_frame= &s->picture; 00279 s->picture.data[0] = NULL; 00280 ff_lzw_decode_open(&s->lzw); 00281 return 0; 00282 } 00283 00284 static int gif_decode_frame(AVCodecContext *avctx, void *data, int *data_size, AVPacket *avpkt) 00285 { 00286 const uint8_t *buf = avpkt->data; 00287 int buf_size = avpkt->size; 00288 GifState *s = avctx->priv_data; 00289 AVFrame *picture = data; 00290 int ret; 00291 00292 s->bytestream = buf; 00293 s->bytestream_end = buf + buf_size; 00294 if (gif_read_header1(s) < 0) 00295 return -1; 00296 00297 avctx->pix_fmt = PIX_FMT_PAL8; 00298 if (av_image_check_size(s->screen_width, s->screen_height, 0, avctx)) 00299 return -1; 00300 avcodec_set_dimensions(avctx, s->screen_width, s->screen_height); 00301 00302 if (s->picture.data[0]) 00303 avctx->release_buffer(avctx, &s->picture); 00304 if (avctx->get_buffer(avctx, &s->picture) < 0) { 00305 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); 00306 return -1; 00307 } 00308 s->image_palette = (uint32_t *)s->picture.data[1]; 00309 ret = gif_parse_next_image(s); 00310 if (ret < 0) 00311 return ret; 00312 00313 *picture = s->picture; 00314 *data_size = sizeof(AVPicture); 00315 return s->bytestream - buf; 00316 } 00317 00318 static av_cold int gif_decode_close(AVCodecContext *avctx) 00319 { 00320 GifState *s = avctx->priv_data; 00321 00322 ff_lzw_decode_close(&s->lzw); 00323 if(s->picture.data[0]) 00324 avctx->release_buffer(avctx, &s->picture); 00325 return 0; 00326 } 00327 00328 AVCodec ff_gif_decoder = { 00329 "gif", 00330 AVMEDIA_TYPE_VIDEO, 00331 CODEC_ID_GIF, 00332 sizeof(GifState), 00333 gif_decode_init, 00334 NULL, 00335 gif_decode_close, 00336 gif_decode_frame, 00337 CODEC_CAP_DR1, 00338 .long_name = NULL_IF_CONFIG_SMALL("GIF (Graphics Interchange Format)"), 00339 };