gifdecod.cpp

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

CPP
951
字号
            ab_prefix[ab_free] = lastcode;
            ab_tail[ab_free]   = code;
            ab_free++;

            if ((ab_free > ab_max) && (ab_bits < 12))
            {
                ab_bits++;
                ab_max = (1 << ab_bits) - 1;
            }
        }

        /* dump stack data to the image buffer */
        while (pos >= 0)
        {
            (img->p)[x + (y * (img->w))] = (char) stack[pos];
            pos--;

            if (++x >= (img->w))
            {
                x = 0;

                if (interl)
                {
                    /* support for interlaced images */
                    switch (pass)
                    {
                        case 1: y += 8; break;
                        case 2: y += 8; break;
                        case 3: y += 4; break;
                        case 4: y += 2; break;
                    }

                    /* loop until a valid y coordinate has been
                    found, Or if the maximum number of passes has
                    been reached, exit the loop, and stop image
                    decoding (At this point the image is successfully
                    decoded).
                    If we don't loop, but merely set y to some other
                    value, that new value might still be invalid depending
                    on the height of the image. This would cause out of
                    bounds writing.
                    */
                    while (y >= (img->h))
                    {
                        switch (++pass)
                        {
                            case 2: y = 4; break;
                            case 3: y = 2; break;
                            case 4: y = 1; break;

                            default:
                                /*
                                It's possible we arrive here. For example this
                                happens when the image is interlaced, and the
                                height is 1. Looking at the above cases, the
                                lowest possible y is 1. While the only valid
                                one would be 0 for an image of height 1. So
                                'eventually' the loop will arrive here.
                                This case makes sure this while loop is
                                exited, as well as the 2 other ones.
                                */

                                // Set y to a valid coordinate so the local
                                // while loop will be exited. (y = 0 always
                                // is >= img->h since if img->h == 0 the
                                // image is never decoded)
                                y = 0;

                                // This will exit the other outer while loop
                                pos = -1;

                                // This will halt image decoding.
                                code = ab_fin;

                                break;
                        }
                    }
                }
                else
                {
                    /* non-interlaced */
                    y++;
/*
Normally image decoding is finished when an End of Information code is
encountered (code == ab_fin) however some broken encoders write wrong
"block byte counts" (The first byte value after the "code size" byte),
being one value too high. It might very well be possible other variants
of this problem occur as well. The only sensible solution seems to
be to check for clipping.
Example of wrong encoding:
(1 * 1 B/W image, raster data stream follows in hex bytes)

02  << B/W images have a code size of 2
02  << Block byte count
44  << LZW packed
00  << Zero byte count (terminates data stream)

Because the block byte count is 2, the zero byte count is used in the
decoding process, and decoding is continued after this byte. (While it
should signal an end of image)

It should be:
02
02
44
01  << When decoded this correctly includes the End of Information code
00

Or (Worse solution):
02
01
44
00
(The 44 doesn't include an End of Information code, but at least the
decoder correctly skips to 00 now after decoding, and signals this
as an End of Information itself)
*/
                    if (y >= img->h)
                    {
                        code = ab_fin;
                        break;
                    }
                }
            }
        }

        pos = 0;
        lastcode = readcode;
    }
    while (code != ab_fin);

    delete [] ab_prefix ;
    delete [] ab_tail ;
    delete [] stack ;

    return wxGIF_OK;
}


// CanRead:
//  Returns true if the file looks like a valid GIF, false otherwise.
//
bool wxGIFDecoder::CanRead()
{
    unsigned char buf[3];

    if ( !m_f->Read(buf, WXSIZEOF(buf)) )
        return false;

    m_f->SeekI(-(wxFileOffset)WXSIZEOF(buf), wxFromCurrent);

    return memcmp(buf, "GIF", WXSIZEOF(buf)) == 0;
}


// ReadGIF:
//  Reads and decodes one or more GIF images, depending on whether
//  animated GIF support is enabled. Can read GIFs with any bit
//  size (color depth), but the output images are always expanded
//  to 8 bits per pixel. Also, the image palettes always contain
//  256 colors, although some of them may be unused. Returns wxGIF_OK
//  (== 0) on success, or an error code if something fails (see
//  header file for details)
//
int wxGIFDecoder::ReadGIF()
{
    unsigned int ncolors;
    int           bits, interl, transparent, disposal, i;
    long          size;
    long          delay;
    unsigned char type = 0;
    unsigned char pal[768];
    unsigned char buf[16];
    GIFImage      **ppimg;
    GIFImage      *pimg, *pprev;

    /* check GIF signature */
    if (!CanRead())
        return wxGIF_INVFORMAT;

    /* check for animated GIF support (ver. >= 89a) */

    static const size_t headerSize = (3 + 3);
    m_f->Read(buf, headerSize);
    if (m_f->LastRead() != headerSize)
    {
        return wxGIF_INVFORMAT;
    }

    if (memcmp(buf + 3, "89a", 3) < 0)
    {
        m_anim = false;
    }

    /* read logical screen descriptor block (LSDB) */
    static const size_t lsdbSize = (2 + 2 + 1 + 1 + 1);
    m_f->Read(buf, lsdbSize);
    if (m_f->LastRead() != lsdbSize)
    {
        return wxGIF_INVFORMAT;
    }

    m_screenw = buf[0] + 256 * buf[1];
    m_screenh = buf[2] + 256 * buf[3];

    if ((m_screenw == 0) || (m_screenh == 0))
    {
        return wxGIF_INVFORMAT;
    }

    /* load global color map if available */
    if ((buf[4] & 0x80) == 0x80)
    {
        m_background = buf[5];

        ncolors = 2 << (buf[4] & 0x07);
        size_t numBytes = 3 * ncolors;
        m_f->Read(pal, numBytes);
        if (m_f->LastRead() != numBytes)
        {
            return wxGIF_INVFORMAT;
        }
    }

    /* transparent colour, disposal method and delay default to unused */
    transparent = -1;
    disposal = -1;
    delay = -1;

    /* read images */
    ppimg = &m_pfirst;
    pprev = NULL;
    pimg  = NULL;

    bool done = false;

    while (!done)
    {
        type = (unsigned char)m_f->GetC();

        /*
        If the end of file has been reached (or an error) and a ";"
        (0x3B) hasn't been encountered yet, exit the loop. (Without this
        check the while loop would loop endlessly.) Later on, in the next while
        loop, the file will be treated as being truncated (But still
        be decoded as far as possible). returning wxGIF_TRUNCATED is not
        possible here since some init code is done after this loop.
        */
        if (m_f->Eof())// || !m_f->IsOk())
        {
            /*
            type is set to some bogus value, so there's no
            need to continue evaluating it.
            */
            break; // Alternative : "return wxGIF_INVFORMAT;"
        }

        /* end of data? */
        if (type == 0x3B)
        {
            done = true;
        }
        else
        /* extension block? */
        if (type == 0x21)
        {
            if (((unsigned char)m_f->GetC()) == 0xF9)
            /* graphics control extension, parse it */
            {
                static const size_t gceSize = 6;
                m_f->Read(buf, gceSize);
                if (m_f->LastRead() != gceSize)
                {
                    Destroy();
                    return wxGIF_INVFORMAT;
                }

                /* read delay and convert from 1/100 of a second to ms */
                delay = 10 * (buf[2] + 256 * buf[3]);

                /* read transparent colour index, if used */
                if (buf[1] & 0x01)
                    transparent = buf[4];

                /* read disposal method */
                disposal = ((buf[1] & 0x1C) >> 2) - 1;
            }
            else
            /* other extension, skip */
            {
                while ((i = (unsigned char)m_f->GetC()) != 0)
                {
                    m_f->SeekI(i, wxFromCurrent);
                    if (m_f->Eof())
                    {
                        done = true;
                        break;
                    }
                }
            }
        }
        else
        /* image descriptor block? */
        if (type == 0x2C)
        {
            /* allocate memory for IMAGEN struct */
            pimg = (*ppimg) = new GIFImage();

            if (pimg == NULL)
            {
                Destroy();
                return wxGIF_MEMERR;
            }

            /* fill in the data */
            static const size_t idbSize = (2 + 2 + 2 + 2 + 1);
            m_f->Read(buf, idbSize);
            if (m_f->LastRead() != idbSize)
            {
                Destroy();
                return wxGIF_INVFORMAT;
            }

            pimg->left = buf[0] + 256 * buf[1];
            pimg->top = buf[2] + 256 * buf[3];
/*
            pimg->left = buf[4] + 256 * buf[5];
            pimg->top = buf[4] + 256 * buf[5];
*/
            pimg->w = buf[4] + 256 * buf[5];
            pimg->h = buf[6] + 256 * buf[7];

            if ((pimg->w == 0) || (pimg->w > m_screenw) || (pimg->h == 0) || (pimg->h > m_screenh))
            {
                Destroy();
                return wxGIF_INVFORMAT;
            }

            interl = ((buf[8] & 0x40)? 1 : 0);
            size = pimg->w * pimg->h;

            pimg->transparent = transparent;
            pimg->disposal = disposal;
            pimg->delay = delay;
            pimg->next = NULL;
            pimg->prev = pprev;
            pprev = pimg;
            ppimg = &pimg->next;

            /* allocate memory for image and palette */
            pimg->p   = (unsigned char *) malloc((size_t)size);
            pimg->pal = (unsigned char *) malloc(768);

            if ((!pimg->p) || (!pimg->pal))
            {
                Destroy();
                return wxGIF_MEMERR;
            }

            /* load local color map if available, else use global map */
            if ((buf[8] & 0x80) == 0x80)
            {
                ncolors = 2 << (buf[8] & 0x07);
                size_t numBytes = 3 * ncolors;
                m_f->Read(pimg->pal, numBytes);
                if (m_f->LastRead() != numBytes)
                {
                    Destroy();
                    return wxGIF_INVFORMAT;
                }
            }
            else
            {
                memcpy(pimg->pal, pal, 768);
            }

            /* get initial code size from first byte in raster data */
            bits = (unsigned char)m_f->GetC();
            if (bits == 0)
            {
                Destroy();
                return wxGIF_INVFORMAT;
            }

            /* decode image */
            int result = dgif(pimg, interl, bits);
            if (result != wxGIF_OK)
            {
                Destroy();
                return result;
            }
            m_nimages++;

            /* if this is not an animated GIF, exit after first image */
            if (!m_anim)
                done = true;
        }
    }

    if (m_nimages <= 0)
    {
        Destroy();
        return wxGIF_INVFORMAT;
    }

    /* setup image pointers */
    m_image = 1;
    m_plast = pimg;
    m_pimage = m_pfirst;

    /* try to read to the end of the stream */
    while (type != 0x3B)
    {
        if (!m_f->IsOk())
            return wxGIF_TRUNCATED;

        type = (unsigned char)m_f->GetC();

        if (type == 0x21)
        {
            /* extension type */
            (void) m_f->GetC();

            /* skip all data */
            while ((i = (unsigned char)m_f->GetC()) != 0)
            {
                m_f->SeekI(i, wxFromCurrent);
            }
        }
        else if (type == 0x2C)
        {
            /* image descriptor block */
            static const size_t idbSize = (2 + 2 + 2 + 2 + 1);
            m_f->Read(buf, idbSize);
            if (m_f->LastRead() != idbSize)
            {
                Destroy();
                return wxGIF_INVFORMAT;
            }

            /* local color map */
            if ((buf[8] & 0x80) == 0x80)
            {
                ncolors = 2 << (buf[8] & 0x07);
                wxFileOffset pos = m_f->TellI();
                wxFileOffset numBytes = 3 * ncolors;
                m_f->SeekI(numBytes, wxFromCurrent);
                if (m_f->TellI() != (pos + numBytes))
                {
                    Destroy();
                    return wxGIF_INVFORMAT;
                }
            }

            /* initial code size */
            (void) m_f->GetC();

            /* skip all data */
            while ((i = (unsigned char)m_f->GetC()) != 0)
            {
                m_f->SeekI(i, wxFromCurrent);
            }
        }
        else if ((type != 0x3B) && (type != 00)) /* testing */
        {
            /* images are OK, but couldn't read to the end of the stream */
            return wxGIF_TRUNCATED;
        }
    }

    return wxGIF_OK;
}

#endif // wxUSE_STREAMS && wxUSE_GIF

⌨️ 快捷键说明

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