📄 gif.c
字号:
/* * File: gif.c * * Copyright (C) 1997 Raph Levien <raph@acm.org> * Copyright (C) 2000-2002 Jorge Arellano Cid <jcid@dillo.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. *//* * The GIF decoder for dillo. It is responsible for decoding GIF data * and transferring it to the dicache. *//* Notes 13 Oct 1997 --RLL * * Today, just for the hell of it, I implemented a new decoder from * scratch. It's oriented around pushing bytes, while the old decoder * was based around reads which may suspend. There were basically * three motivations. * * 1. To increase the speed. * * 2. To fix some bugs I had seen, most likely due to suspension. * * 3. To make sure that the code had no buffer overruns or the like. * * 4. So that the code could be released under a freer license. * * Let's see how we did on speed. I used a large image for testing * (fvwm95-2.gif). * * The old decoder spent a total of about 1.04 seconds decoding the * image. Another .58 seconds went into Image_line, almost * entirely conversion from colormap to RGB. * * The new decoder spent a total of 0.46 seconds decoding the image. * However, the time for Image_line went up to 1.01 seconds. * Thus, even though the decoder seems to be about twice as fast, * the net gain is pretty minimal. Could this be because of cache * effects? * * Lessons learned: The first, which I keep learning over and over, is * not to try to optimize too much. It doesn't work. Just keep things * simple. * * Second, it seems that the colormap to RGB conversion is really a * significant part of the overall time. It's possible that going * directly to 16 bits would help, but that's optimization again :) *//* todo: * + Make sure to handle error cases gracefully (including aborting the * connection, if necessary). */#include <mgdconfig.h>#ifdef ENABLE_GIF#include <stdio.h> /* for sprintf */#include <string.h> /* for memcpy and memmove */#include <glib.h>#include "msg.h"#include "image.h"#include "web.h"#include "cache.h"#include "dicache.h"#include "prefs.h"#define DEBUG_LEVEL 6#include "debug.h"#define INTERLACE 0x40#define LOCALCOLORMAP 0x80#define LM_to_uint(a,b) ((((guchar)b)<<8)|((guchar)a))#define MAXCOLORMAPSIZE 256#define MAX_LWZ_BITS 12typedef struct _DilloGif { DilloImage *Image; DilloUrl *url; gint version; gint state; size_t Start_Ofs; guint Flags; guchar input_code_size; guchar *linebuf; gint pass; guint y; /* state for lwz_read_byte */ gint code_size; /* The original GifScreen from giftopnm */ guint Top; guint Left; guint Width; guint Height; size_t ColorMap_ofs; guint ColorResolution; guint NumColors; gint Background; guint spill_line_index;#ifdef ENABLE_ANIMATION guint AspectRatio;#endif /* Gif89 extensions */ gint transparent;#ifdef ENABLE_ANIMATION /* None are used: */ gint delayTime; gint inputFlag; gint disposal; guint time_unit;#endif /* state for the new push-oriented decoder */ gint packet_size; /* The amount of the data block left to process */ guint window; gint bits_in_window; guint last_code; /* Last "compressed" code in the look up table */ guint line_index; guchar **spill_lines; gint num_spill_lines_max; gint length[(1 << MAX_LWZ_BITS) + 1]; gint code_and_byte[(1 << MAX_LWZ_BITS) + 1];#ifdef ENABLE_ANIMATION AnimationFrame* new_frame;#endif} DilloGif;/* Some invariants: * * last_code <= code_mask * * code_and_byte is stored packed: (code << 8) | byte *//* * Forward declarations */static void Gif_write(DilloGif *gif, void *Buf, guint BufSize);static void Gif_close(DilloGif *gif, CacheClient_t *Client);static size_t Gif_process_bytes(DilloGif *gif, const guchar *buf, gint bufsize, void *Buf);static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, gint version);static void Gif_callback(int Op, CacheClient_t *Client);/* exported function */DwWidget *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call, void **Data);/* * MIME handler for "image/gif" type * (Sets Gif_callback as cache-client) */DwWidget *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call, void **Data){ DilloWeb *web = Ptr; DICacheEntry *DicEntry; if ( !web->Image ) web->Image = a_Image_new(0, 0, NULL, prefs.bg_color); /* todo: get the backgound color from the parent widget -- Livio. */ /* Add an extra reference to the Image (for dicache usage) */ a_Image_ref(web->Image); DicEntry = a_Dicache_get_entry(web->url); if ( !DicEntry ) { /* Let's create an entry for this image... */ DicEntry = a_Dicache_add_entry(web->url); /* ... and let the decoder feed it! */ *Data = Gif_new(web->Image, DicEntry->url, DicEntry->version); *Call = (CA_Callback_t) Gif_callback; } else { /* Let's feed our client from the dicache */ a_Dicache_ref(DicEntry->url, DicEntry->version); *Data = web->Image; *Call = (CA_Callback_t) a_Dicache_callback; } return DW_WIDGET (web->Image->dw);}/* * Create a new gif structure for decoding a gif into a RGB buffer */static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, gint version){ DilloGif *gif = g_malloc(sizeof(DilloGif)); gif->Image = Image; gif->url = url; gif->version = version; gif->Flags = 0; gif->state = 0; gif->Start_Ofs = 0; gif->linebuf = NULL; gif->Background = -1; gif->transparent = -1; gif->num_spill_lines_max = 0; gif->spill_lines = NULL; gif->window = 0; gif->packet_size = 0; gif->ColorMap_ofs = 0; #ifdef ENABLE_ANIMATION gif->delayTime = -1; gif->inputFlag = -1; gif->disposal = -1; gif->time_unit = 10; gif->new_frame = NULL;#endif return gif;}/* * This function is a cache client, it receives data from the cache * and dispatches it to the appropriate gif-processing functions */static void Gif_callback(int Op, CacheClient_t *Client){ if ( Op ) Gif_close(Client->CbData, Client); else Gif_write(Client->CbData, Client->Buf, Client->BufSize);}#ifdef ENABLE_ANIMATIONstatic void restore_bk_color (DwImage* dw, AnimationFrame* frame){ gint w, h, i, pitch = dw->width * 3; guchar* dst_row = dw->buffer + pitch * frame->off_y + 3 * frame->off_x; guchar* pixel; if (dw->transparent < 0) return; DEBUG_MSG (5, "restore the bk color: %d, %d, %d, %d\n", frame->off_x, frame->off_y, frame->width, frame->height); i = dw->transparent * 3; for (h = 0; h < frame->height; h++) { pixel = dst_row; for (w = 0; w < frame->width; w++) { pixel [0] = dw->cmap [i]; pixel [1] = dw->cmap [i + 1]; pixel [2] = dw->cmap [i + 2]; pixel += 3; } dst_row += pitch; }}static void fill_frame (DwImage* dw, AnimationFrame* frame){ gint w, h, dst_pitch; guchar *src_row, *dst_row, *dst_pixel; if (frame == NULL || frame->filled == 0) return; DEBUG_MSG (5, "fill frame: %d, %d, %d, %d\n", frame->off_x, frame->off_y, frame->width, frame->height); dst_pitch = dw->width * 3; src_row = frame->bits; dst_row = dw->buffer + dst_pitch * frame->off_y + frame->off_x * 3; for (h = 0; h < frame->height; h++) { dst_pixel = dst_row; for (w = 0; w < frame->width; w++) { if (src_row [w] != dw->transparent) { gint i = src_row[w] * 3; dst_pixel [0] = dw->cmap [i]; dst_pixel [1] = dw->cmap [i + 1]; dst_pixel [2] = dw->cmap [i + 2]; } dst_pixel += 3; } src_row += frame->width; dst_row += dst_pitch; }}static void anim_treat_frame_disposal (DwImage* dw, AnimationFrame* frame){ int disposal = -1; if (frame) disposal = frame->disposal; switch(disposal) { case 2: restore_bk_color (dw, frame); break; case 3: fill_frame (dw, frame->prev); break; default: /* no nothing */ break; }}static gint Gif_play_frames (DwImage* dw){ DwWidget *widget; gint delay_time = -1; if (dw->current_frame) delay_time = dw->current_frame->delay_time; dw->elapsed_10ms++; if (delay_time > 0 && dw->elapsed_10ms >= delay_time) { if (dw->done_frame) anim_treat_frame_disposal (dw, dw->done_frame); if (!dw->current_frame->next) dw->current_frame = dw->frames; else dw->current_frame = dw->current_frame->next; fill_frame (dw, dw->current_frame); dw->done_frame = dw->current_frame; dw->elapsed_10ms = 0; widget = DW_WIDGET(dw); p_Dw_widget_queue_draw_area (widget, 0, 0 + p_Dw_style_box_offset_y(widget->style), dw->width, dw->height); } return 1;}#endif/* * Receive and process new chunks of GIF image data */static void Gif_write(DilloGif *gif, void *Buf, guint BufSize){ guchar *buf; gint bufsize, bytes_consumed; /* Sanity checks */ if (!Buf || !gif->Image || BufSize == 0) return; DEBUG_MSG(5, "Gif_write: %u bytes\n", BufSize);#ifdef ENABLE_ANIMATION do {#endif buf = ((guchar *) Buf) + gif->Start_Ofs; bufsize = BufSize - gif->Start_Ofs; /* Process the bytes in the input buffer. */ bytes_consumed = Gif_process_bytes(gif, buf, bufsize, Buf); if (bytes_consumed < 1)#ifdef ENABLE_ANIMATION break;#else return;#endif gif->Start_Ofs += bytes_consumed;#ifdef ENABLE_ANIMATION } while (TRUE); DEBUG_MSG(5, "Gif info: frame = %d\n", gif->Image->dw->nr_frames); if (!gif->Image->dw->timer && gif->Image->dw->frames && gif->Image->dw->frames->next) { DEBUG_MSG(5, "Add a timer to play the animation: %d\n", gif->Image->dw->nr_frames); gif->Image->dw->current_frame = gif->Image->dw->frames; gif->Image->dw->timer = g_timeout_add_full (G_PRIORITY_DEFAULT+1, 5, (GSourceFunc)Gif_play_frames, gif->Image->dw, NULL); }#endif DEBUG_MSG(5, "exit Gif_write, bufsize=%ld\n", (glong)bufsize);}/* * Finish the decoding process (and free the memory) */static void Gif_close(DilloGif *gif, CacheClient_t *Client){ gint i; DEBUG_MSG(5, "destroy gif %p\n", gif);#ifdef ENABLE_ANIMATION if (gif->Image->dw->nr_frames > 1) { /* force to reload the Gif89a animation file */ a_Dicache_invalidate_entry(gif->url); }#endif a_Dicache_close(gif->url, gif->version, Client); g_free(gif->linebuf); if (gif->spill_lines != NULL) { for (i = 0; i < gif->num_spill_lines_max; i++) g_free(gif->spill_lines[i]); g_free(gif->spill_lines); } g_free(gif);}/* --- GIF Extensions ----------------------------------------------------- *//* * This reads a sequence of GIF data blocks.. and ignores them! * Buf points to the first data block. *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -