/*
 * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef BMPImageReader_h
#define BMPImageReader_h

#include <stdint.h>
#include "ImageDecoder.h"

namespace WebCore {

    // This class decodes a BMP image.  It is used in the BMP and ICO decoders,
    // which wrap it in the appropriate code to read file headers, etc.
    class BMPImageReader {
    public:
        // Read a value from |data[offset]|, converting from little to native
        // endianness.
        static inline uint16_t readUint16(SharedBuffer* data, int offset)
        {
            uint16_t result;
            memcpy(&result, &data->data()[offset], 2);
        #if PLATFORM(BIG_ENDIAN)
            result = ((result & 0xff) << 8) | ((result & 0xff00) >> 8);
        #endif
            return result;
        }

        static inline uint32_t readUint32(SharedBuffer* data, int offset)
        {
            uint32_t result;
            memcpy(&result, &data->data()[offset], 4);
        #if PLATFORM(BIG_ENDIAN)
            result = ((result & 0xff) << 24) | ((result & 0xff00) << 8) |
                ((result & 0xff0000) >> 8) | ((result & 0xff000000) >> 24);
        #endif
            return result;
        }

        // |parent| is the decoder that owns us.
        // |startOffset| points to the start of the BMP within the file.
        // |buffer| points at an empty RGBA32Buffer that we'll initialize and
        // fill with decoded data.
        BMPImageReader(ImageDecoder* parent,
                       size_t decodedAndHeaderOffset,
                       size_t imgDataOffset,
                       bool usesAndMask);

        void setBuffer(RGBA32Buffer* buffer) { m_buffer = buffer; }
        void setData(SharedBuffer* data) { m_data = data; }

        // Does the actual decoding.  If |onlySize| is true, decoding only
        // progresses as far as necessary to get the image size.  Returns
        // whether decoding succeeded.
        bool decodeBMP(bool onlySize);

    private:
        // The various BMP compression types.  We don't currently decode all
        // these.
        enum CompressionType {
            // Universal types
            RGB = 0,
            RLE8 = 1,
            RLE4 = 2,
            // Windows V3+ only
            BITFIELDS = 3,
            JPEG = 4,
            PNG = 5,
            // OS/2 2.x-only
            HUFFMAN1D,  // Stored in file as 3
            RLE24,      // Stored in file as 4
        };
        enum AndMaskState {
            None,
            NotYetDecoded,
            Decoding,
        };

        // These are based on the Windows BITMAPINFOHEADER and RGBTRIPLE
        // structs, but with unnecessary entries removed.
        struct BitmapInfoHeader {
            uint32_t biSize;
            int32_t biWidth;
            int32_t biHeight;
            uint16_t biBitCount;
            CompressionType biCompression;
            uint32_t biClrUsed;
        };
        struct RGBTriple {
            uint8_t rgbBlue;
            uint8_t rgbGreen;
            uint8_t rgbRed;
        };

        inline uint16_t readUint16(int offset) const
        {
            return readUint16(m_data.get(), m_decodedOffset + offset);
        }

        inline uint32_t readUint32(int offset) const
        {
            return readUint32(m_data.get(), m_decodedOffset + offset);
        }

        // Determines the size of the BMP info header.  Returns true if the size
        // is valid.
        bool readInfoHeaderSize();

        // Processes the BMP info header.  Returns true if the info header could
        // be decoded.
        bool processInfoHeader();

        // Helper function for processInfoHeader() which does the actual reading
        // of header values from the byte stream.  Returns false on error.
        bool readInfoHeader();

        // Returns true if this is a Windows V4+ BMP.
        inline bool isWindowsV4Plus() const
        {
            // Windows V4 info header is 108 bytes.  V5 is 124 bytes.
            return (m_infoHeader.biSize == 108) || (m_infoHeader.biSize == 124);
        }

        // Returns false if consistency errors are found in the info header.
        bool isInfoHeaderValid() const;

        // For BI_BITFIELDS images, initializes the m_bitMasks[] and
        // m_bitOffsets[] arrays.  processInfoHeader() will initialize these for
        // other compression types where needed.
        bool processBitmasks();

        // For paletted images, allocates and initializes the m_colorTable[]
        // array.
        bool processColorTable();

        // Processes an RLE-encoded image.  Returns true if the entire image was
        // decoded.
        bool processRLEData();

        // Processes a set of non-RLE-compressed pixels.  Two cases:
        //   * inRLE = true: the data is inside an RLE-encoded bitmap.  Tries to
        //     process |numPixels| pixels on the current row; returns true on
        //     success.
        //   * inRLE = false: the data is inside a non-RLE-encoded bitmap.
        //     |numPixels| is ignored.  Expects |m_coord| to point at the
        //     beginning of the next row to be decoded.  Tries to process as
        //     many complete rows as possible.  Returns true if the whole image
        //     was decoded.
        bool processNonRLEData(bool inRLE, int numPixels);

        // Returns true if the current y-coordinate plus |numRows| would be past
        // the end of the image.  Here "plus" means "toward the end of the
        // image", so downwards for m_isTopDown images and upwards otherwise.
        inline bool pastEndOfImage(int numRows)
        {
            return m_isTopDown
                ? ((m_coord.y() + numRows) >= m_parent->size().height())
                : ((m_coord.y() - numRows) < 0);
        }

        // Returns the pixel data for the current X coordinate in a uint32_t.
        // Assumes m_decodedOffset has been set to the beginning of the current
        // row.
        // NOTE: Only as many bytes of the return value as are needed to hold
        // the pixel data will actually be set.
        inline uint32_t readCurrentPixel(int bytesPerPixel) const
        {
            const int offset = m_coord.x() * bytesPerPixel;
            switch (bytesPerPixel) {
            case 2:
                return readUint16(offset);

            case 3: {
                // It doesn't matter that we never set the most significant byte
                // of the return value here in little-endian mode, the caller
                // won't read it.
                uint32_t pixel;
                memcpy(&pixel, &m_data->data()[m_decodedOffset + offset], 3);
        #if PLATFORM(BIG_ENDIAN)
                pixel = ((pixel & 0xff00) << 8) | ((pixel & 0xff0000) >> 8) |
                    ((pixel & 0xff000000) >> 24);
        #endif
                return pixel;
            }

            case 4:
                return readUint32(offset);

            default:
                ASSERT_NOT_REACHED();
                return 0;
            }
        }

        // Returns the value of the desired component (0, 1, 2, 3 == R, G, B, A)
        // in the given pixel data.
        inline unsigned getComponent(uint32_t pixel, int component) const
        {
            return ((pixel & m_bitMasks[component]) >>
                m_bitShiftsRight[component]) << m_bitShiftsLeft[component];
        }

        inline unsigned getAlpha(uint32_t pixel) const
        {
            // For images without alpha, return alpha of 0xff.
            if (m_bitMasks[3] == 0)
                return 0xff;

            return getComponent(pixel, 3);
        }

        // Sets the current pixel to the color given by |colorIndex|.  This also
        // increments the relevant local variables to move the current pixel
        // right by one.
        inline void setI(size_t colorIndex)
        {
            setRGBA(m_colorTable[colorIndex].rgbRed,
                    m_colorTable[colorIndex].rgbGreen,
                    m_colorTable[colorIndex].rgbBlue, 0xff);
        }

        // Like setI(), but with the individual component values specified.
        inline void setRGBA(unsigned red,
                            unsigned green,
                            unsigned blue,
                            unsigned alpha)
        {
            m_buffer->setRGBA(m_coord.x(), m_coord.y(), red, green, blue,
                              alpha);
            m_coord.move(1, 0);
        }

        // Fills pixels from the current X-coordinate up to, but not including,
        // |endCoord| with the color given by the individual components.  This
        // also increments the relevant local variables to move the current
        // pixel right to |endCoord|.
        inline void fillRGBA(int endCoord,
                             unsigned red,
                             unsigned green,
                             unsigned blue,
                             unsigned alpha)
        {
            while (m_coord.x() < endCoord)
                setRGBA(red, green, blue, alpha);
        }

        // Resets the relevant local variables to start drawing at the left edge
        // of the "next" row, where "next" is above or below the current row
        // depending on the value of |m_isTopDown|.
        void moveBufferToNextRow();

        // Sets the "decode failure" flag and clears any local storage.  For
        // caller convenience (since so many callers want to return false after
        // calling this), returns false to enable easy tailcalling.
        bool setFailed();

        // The decoder that owns us.
        ImageDecoder* m_parent;

        // The destination for the pixel data.
        RGBA32Buffer* m_buffer;

        // The file to decode.
        RefPtr<SharedBuffer> m_data;

        // An index into |m_data| representing how much we've already decoded.
        size_t m_decodedOffset;

        // The file offset at which the BMP info header starts.
        size_t m_headerOffset;

        // The file offset at which the actual image bits start.  When decoding
        // ICO files, this is set to 0, since it's not stored anywhere in a
        // header; the reader functions expect the image data to start
        // immediately after the header and (if necessary) color table.
        size_t m_imgDataOffset;

        // The BMP info header.
        BitmapInfoHeader m_infoHeader;

        // True if this is an OS/2 1.x (aka Windows 2.x) BMP.  The struct
        // layouts for this type of BMP are slightly different from the later,
        // more common formats.
        bool m_isOS21x;

        // True if this is an OS/2 2.x BMP.  The meanings of compression types 3
        // and 4 for this type of BMP differ from Windows V3+ BMPs.
        //
        // This will be falsely negative in some cases, but only ones where the
        // way we misinterpret the data is irrelevant.
        bool m_isOS22x;

        // True if the BMP is not vertically flipped, that is, the first line of
        // raster data in the file is the top line of the image.
        bool m_isTopDown;

        // These flags get set to false as we finish each processing stage.
        bool m_needToProcessBitmasks;
        bool m_needToProcessColorTable;

        // Masks/offsets for the color values for non-palette formats.  These
        // are bitwise, with array entries 0, 1, 2, 3 corresponding to R, G, B,
        // A.
        //
        // The right/left shift values are meant to be applied after the masks.
        // We need to right shift to compensate for the bitfields' offsets into
        // the 32 bits of pixel data, and left shift to scale the color values
        // up for fields with less than 8 bits of precision.  Sadly, we can't
        // just combine these into one shift value because the net shift amount
        // could go either direction.  (If only "<< -x" were equivalent to
        // ">> x"...)
        uint32_t m_bitMasks[4];
        int m_bitShiftsRight[4];
        int m_bitShiftsLeft[4];

        // The color palette, for paletted formats.
        size_t m_tableSizeInBytes;
        Vector<RGBTriple> m_colorTable;

        // The coordinate to which we've decoded the image.
        IntPoint m_coord;

        // Variables that track whether we've seen pixels with alpha values != 0
        // and == 0, respectively.  See comments in processNonRLEData() on how
        // these are used.
        bool m_seenNonZeroAlphaPixel;
        bool m_seenZeroAlphaPixel;

        // ICOs store a 1bpp "mask" immediately after the main bitmap image data
        // (and, confusingly, add its height to the biHeight value in the info
        // header, thus doubling it).  This variable tracks whether we have such
        // a mask and if we've started decoding it yet.
        AndMaskState m_andMaskState;
    };

} // namespace WebCore

#endif
