gifdecod.cpp

来自「A*算法 A*算法 A*算法 A*算法A*算法A*算法」· C++ 代码 · 共 951 行 · 第 1/2 页

CPP
951
字号
/////////////////////////////////////////////////////////////////////////////
// Name:        gifdecod.cpp
// Purpose:     wxGIFDecoder, GIF reader for wxImage and wxAnimation
// Author:      Guillermo Rodriguez Garcia <guille@iies.es>
// Version:     3.04
// RCS-ID:      $Id: gifdecod.cpp,v 1.37.2.1 2006/01/23 07:18:36 vell Exp $
// Copyright:   (c) Guillermo Rodriguez Garcia
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "gifdecod.h"
#endif

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#  pragma hdrstop
#endif

#ifndef WX_PRECOMP
#  include "wx/defs.h"
#  include "wx/palette.h"
#endif

#if wxUSE_STREAMS && wxUSE_GIF

#include <stdlib.h>
#include <string.h>
#include "wx/gifdecod.h"


//---------------------------------------------------------------------------
// GIFImage constructor
//---------------------------------------------------------------------------
GIFImage::GIFImage()
{
    w = 0;
    h = 0;
    left = 0;
    top = 0;
    transparent = 0;
    disposal = 0;
    delay = -1;
    p = (unsigned char *) NULL;
    pal = (unsigned char *) NULL;
    next = (GIFImage *) NULL;
    prev = (GIFImage *) NULL;
}

//---------------------------------------------------------------------------
// wxGIFDecoder constructor and destructor
//---------------------------------------------------------------------------

wxGIFDecoder::wxGIFDecoder(wxInputStream *s, bool anim)
{
    m_f    = s;
    m_anim = anim;

    m_background = -1;
    m_screenw = 0;
    m_screenh = 0;

    m_pimage  = NULL;
    m_pfirst  = NULL;
    m_plast   = NULL;
    m_image   = 0;
    m_nimages = 0;
}

wxGIFDecoder::~wxGIFDecoder()
{
    Destroy();
}

void wxGIFDecoder::Destroy()
{
    GIFImage *pimg, *paux;

    pimg = m_pfirst;

    while (pimg != NULL)
    {
        paux = pimg->next;
        free(pimg->p);
        free(pimg->pal);
        delete pimg;
        pimg = paux;
    }

    m_pimage  = NULL;
    m_pfirst  = NULL;
    m_plast   = NULL;
    m_image   = 0;
    m_nimages = 0;
}


//---------------------------------------------------------------------------
// Convert this image to a wxImage object
//---------------------------------------------------------------------------

// This function was designed by Vaclav Slavik

bool wxGIFDecoder::ConvertToImage(wxImage *image) const
{
    unsigned char *src, *dst, *pal;
    unsigned long i;
    int      transparent;

    /* just in case... */
    image->Destroy();

    /* create the image */
    image->Create(GetWidth(), GetHeight());

    if (!image->Ok())
        return false;

    pal = GetPalette();
    src = GetData();
    dst = image->GetData();
    transparent = GetTransparentColour();

    /* set transparent colour mask */
    if (transparent != -1)
    {
        for (i = 0; i < 256; i++)
        {
            if ((pal[3 * i + 0] == 255) &&
                (pal[3 * i + 1] == 0) &&
                (pal[3 * i + 2] == 255))
            {
                pal[3 * i + 2] = 254;
            }
        }

        pal[3 * transparent + 0] = 255,
        pal[3 * transparent + 1] = 0,
        pal[3 * transparent + 2] = 255;

        image->SetMaskColour(255, 0, 255);
    }
    else
        image->SetMask(false);

#if wxUSE_PALETTE
    if (pal)
    {
        unsigned char r[256];
        unsigned char g[256];
        unsigned char b[256];

        for (i = 0; i < 256; i++)
        {
            r[i] = pal[3*i + 0];
            g[i] = pal[3*i + 1];
            b[i] = pal[3*i + 2];
        }

        image->SetPalette(wxPalette(256, r, g, b));
    }
#endif // wxUSE_PALETTE

    /* copy image data */
    for (i = 0; i < (GetWidth() * GetHeight()); i++, src++)
    {
        *(dst++) = pal[3 * (*src) + 0];
        *(dst++) = pal[3 * (*src) + 1];
        *(dst++) = pal[3 * (*src) + 2];
    }

    return true;
}


//---------------------------------------------------------------------------
// Data accessors
//---------------------------------------------------------------------------

// Get data for current frame

int wxGIFDecoder::GetFrameIndex() const         { return m_image; }
unsigned char* wxGIFDecoder::GetData() const    { return (m_pimage->p); }
unsigned char* wxGIFDecoder::GetPalette() const { return (m_pimage->pal); }
unsigned int wxGIFDecoder::GetWidth() const     { return (m_pimage->w); }
unsigned int wxGIFDecoder::GetHeight() const    { return (m_pimage->h); }
unsigned int wxGIFDecoder::GetTop() const       { return (m_pimage->top); }
unsigned int wxGIFDecoder::GetLeft() const      { return (m_pimage->left); }
int wxGIFDecoder::GetTransparentColour() const  { return (m_pimage->transparent); }
int wxGIFDecoder::GetDisposalMethod() const     { return (m_pimage->disposal); }
long wxGIFDecoder::GetDelay() const             { return (m_pimage->delay); }

// Get global data

unsigned int wxGIFDecoder::GetLogicalScreenWidth() const    { return m_screenw; }
unsigned int wxGIFDecoder::GetLogicalScreenHeight() const   { return m_screenh; }
int wxGIFDecoder::GetBackgroundColour() const   { return m_background; }
int wxGIFDecoder::GetNumberOfFrames() const     { return m_nimages; }
bool wxGIFDecoder::IsAnimation() const          { return (m_nimages > 1); }


//---------------------------------------------------------------------------
// Functions to move through the animation
//---------------------------------------------------------------------------

bool wxGIFDecoder::GoFirstFrame()
{
    if (!IsAnimation())
        return false;

    m_image = 1;
    m_pimage = m_pfirst;
    return true;
}

bool wxGIFDecoder::GoLastFrame()
{
    if (!IsAnimation())
        return false;

    m_image = m_nimages;
    m_pimage = m_plast;
    return true;
}

bool wxGIFDecoder::GoNextFrame(bool cyclic)
{
    if (!IsAnimation())
        return false;

    if ((m_image < m_nimages) || (cyclic))
    {
        m_pimage = m_pimage->next;
        m_image++;

        if (!m_pimage)
        {
            m_image = 1;
            m_pimage = m_pfirst;
        }

        return true;
    }
    else
        return false;
}

bool wxGIFDecoder::GoPrevFrame(bool cyclic)
{
    if (!IsAnimation())
        return false;

    if ((m_image > 1) || (cyclic))
    {
        m_pimage = m_pimage->prev;
        m_image--;

        if (!m_pimage)
        {
            m_image = m_nimages;
            m_pimage = m_plast;
        }

        return true;
    }
    else
        return false;
}

bool wxGIFDecoder::GoFrame(int which)
{
    if (!IsAnimation())
        return false;

    if ((which >= 1) && (which <= m_nimages))
    {
        m_image = 1;
        m_pimage = m_pfirst;

        while (m_image < which)
        {
            m_image++;
            m_pimage = m_pimage->next;
        }

        return true;
    }
    else
        return false;
}


//---------------------------------------------------------------------------
// GIF reading and decoding
//---------------------------------------------------------------------------

// getcode:
//  Reads the next code from the file stream, with size 'bits'
//
int wxGIFDecoder::getcode(int bits, int ab_fin)
{
    unsigned int mask;          /* bit mask */
    unsigned int code;          /* code (result) */


    /* get remaining bits from last byte read */
    mask = (1 << bits) - 1;
    code = (m_lastbyte >> (8 - m_restbits)) & mask;

    /* keep reading new bytes while needed */
    while (bits > m_restbits)
    {
        /* if no bytes left in this block, read the next block */
        if (m_restbyte == 0)
        {
            m_restbyte = (unsigned char)m_f->GetC();

            /* Some encoders are a bit broken: instead of issuing
             * an end-of-image symbol (ab_fin) they come up with
             * a zero-length subblock!! We catch this here so
             * that the decoder sees an ab_fin code.
             */
            if (m_restbyte == 0)
            {
                code = ab_fin;
                break;
            }

            /* prefetch data */
            m_f->Read((void *) m_buffer, m_restbyte);
            if (m_f->LastRead() != m_restbyte)
            {
                code = ab_fin;
                return code;
            }
            m_bufp = m_buffer;
        }

        /* read next byte and isolate the bits we need */
        m_lastbyte = (unsigned char) (*m_bufp++);
        mask       = (1 << (bits - m_restbits)) - 1;
        code       = code + ((m_lastbyte & mask) << m_restbits);
        m_restbyte--;

        /* adjust total number of bits extracted from the buffer */
        m_restbits = m_restbits + 8;
    }

    /* find number of bits remaining for next code */
    m_restbits = (m_restbits - bits);

    return code;
}


// dgif:
//  GIF decoding function. The initial code size (aka root size)
//  is 'bits'. Supports interlaced images (interl == 1).
//  Returns wxGIF_OK (== 0) on success, or an error code if something
// fails (see header file for details)
int wxGIFDecoder::dgif(GIFImage *img, int interl, int bits)
{
    static const int allocSize = 4096 + 1;
    int *ab_prefix = new int[allocSize]; /* alphabet (prefixes) */
    if (ab_prefix == NULL)
    {
        return wxGIF_MEMERR;
    }

    int *ab_tail = new int[allocSize];   /* alphabet (tails) */
    if (ab_tail == NULL)
    {
        delete[] ab_prefix;
        return wxGIF_MEMERR;
    }

    int *stack = new int[allocSize];     /* decompression stack */
    if (stack == NULL)
    {
        delete[] ab_prefix;
        delete[] ab_tail;
        return wxGIF_MEMERR;
    }

    int ab_clr;                     /* clear code */
    int ab_fin;                     /* end of info code */
    int ab_bits;                    /* actual symbol width, in bits */
    int ab_free;                    /* first free position in alphabet */
    int ab_max;                     /* last possible character in alphabet */
    int pass;                       /* pass number in interlaced images */
    int pos;                        /* index into decompresion stack */
    unsigned int x, y;              /* position in image buffer */

    int code, readcode, lastcode, abcabca;

    /* these won't change */
    ab_clr = (1 << bits);
    ab_fin = (1 << bits) + 1;

    /* these will change through the decompression proccess */
    ab_bits  = bits + 1;
    ab_free  = (1 << bits) + 2;
    ab_max   = (1 << ab_bits) - 1;
    lastcode = -1;
    abcabca  = -1;
    pass     = 1;
    pos = x = y = 0;

    /* reset decoder vars */
    m_restbits = 0;
    m_restbyte = 0;
    m_lastbyte = 0;

    do
    {
        /* get next code */
        readcode = code = getcode(ab_bits, ab_fin);

        /* end of image? */
        if (code == ab_fin) break;

        /* reset alphabet? */
        if (code == ab_clr)
        {
            /* reset main variables */
            ab_bits  = bits + 1;
            ab_free  = (1 << bits) + 2;
            ab_max   = (1 << ab_bits) - 1;
            lastcode = -1;
            abcabca  = -1;

            /* skip to next code */
            continue;
        }

        /* unknown code: special case (like in ABCABCA) */
        if (code >= ab_free)
        {
            code = lastcode;            /* take last string */
            stack[pos++] = abcabca;     /* add first character */
        }

        /* build the string for this code in the stack */
        while (code > ab_clr)
        {
            stack[pos++] = ab_tail[code];
            code         = ab_prefix[code];

            // Don't overflow. This shouldn't happen with normal
            // GIF files, the allocSize of 4096+1 is enough. This
            // will only happen with badly formed GIFs.
            if (pos >= allocSize)
            {
                delete[] ab_prefix;
                delete[] ab_tail;
                delete[] stack;
                return wxGIF_INVFORMAT;
            }
        }

        if (pos >= allocSize)
        {
            delete[] ab_prefix;
            delete[] ab_tail;
            delete[] stack;
            return wxGIF_INVFORMAT;
        }

        stack[pos] = code;              /* push last code into the stack */
        abcabca    = code;              /* save for special case */

        /* make new entry in alphabet (only if NOT just cleared) */
        if (lastcode != -1)
        {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?