Exemple #1
0
bool BMPImageReader::decodePixelData(bool nonRLE) {
  const IntPoint coord(m_coord);
  const ProcessingResult result =
      nonRLE ? processNonRLEData(false, 0) : processRLEData();
  if (m_coord != coord)
    m_buffer->setPixelsChanged(true);
  return (result == Failure) ? m_parent->setFailed() : (result == Success);
}
bool BMPImageReader::processRLEData()
{
    if (m_decodedOffset > m_data->size())
        return false;

    // RLE decoding is poorly specified.  Two main problems:
    // (1) Are EOL markers necessary?  What happens when we have too many
    //     pixels for one row?
    //     http://www.fileformat.info/format/bmp/egff.htm says extra pixels
    //     should wrap to the next line.  Real BMPs I've encountered seem to
    //     instead expect extra pixels to be ignored until the EOL marker is
    //     seen, although this has only happened in a few cases and I suspect
    //     those BMPs may be invalid.  So we only change lines on EOL (or Delta
    //     with dy > 0), and fail in most cases when pixels extend past the end
    //     of the line.
    // (2) When Delta, EOL, or EOF are seen, what happens to the "skipped"
    //     pixels?
    //     http://www.daubnet.com/formats/BMP.html says these should be filled
    //     with color 0.  However, the "do nothing" and "don't care" comments
    //     of other references suggest leaving these alone, i.e. letting them
    //     be transparent to the background behind the image.  This seems to
    //     match how MSPAINT treats BMPs, so we do that.  Note that when we
    //     actually skip pixels for a case like this, we need to note on the
    //     framebuffer that we have alpha.

    // Impossible to decode row-at-a-time, so just do things as a stream of
    // bytes.
    while (true) {
        // Every entry takes at least two bytes; bail if there isn't enough
        // data.
        if ((m_data->size() - m_decodedOffset) < 2)
            return false;

        // For every entry except EOF, we'd better not have reached the end of
        // the image.
        const uint8_t count = m_data->data()[m_decodedOffset];
        const uint8_t code = m_data->data()[m_decodedOffset + 1];
        if ((count || (code != 1)) && pastEndOfImage(0))
            return setFailed();

        // Decode.
        if (count == 0) {
            switch (code) {
            case 0:  // Magic token: EOL
                // Skip any remaining pixels in this row.
                if (m_coord.x() < m_parent->size().width())
                    m_buffer->setHasAlpha(true);
                moveBufferToNextRow();

                m_decodedOffset += 2;
                break;

            case 1:  // Magic token: EOF
                // Skip any remaining pixels in the image.
                if ((m_coord.x() < m_parent->size().width()) || (m_isTopDown ? (m_coord.y() < (m_parent->size().height() - 1)) : (m_coord.y() > 0)))
                    m_buffer->setHasAlpha(true);
                return true;

            case 2: {  // Magic token: Delta
                // The next two bytes specify dx and dy.  Bail if there isn't
                // enough data.
                if ((m_data->size() - m_decodedOffset) < 4)
                    return false;

                // Fail if this takes us past the end of the desired row or
                // past the end of the image.
                const uint8_t dx = m_data->data()[m_decodedOffset + 2];
                const uint8_t dy = m_data->data()[m_decodedOffset + 3];
                if (dx || dy)
                    m_buffer->setHasAlpha(true);
                if (((m_coord.x() + dx) > m_parent->size().width()) || pastEndOfImage(dy))
                    return setFailed();

                // Skip intervening pixels.
                m_coord.move(dx, m_isTopDown ? dy : -dy);

                m_decodedOffset += 4;
                break;
            }

            default:  // Absolute mode
                // |code| pixels specified as in BI_RGB, zero-padded at the end
                // to a multiple of 16 bits.
                // Because processNonRLEData() expects m_decodedOffset to
                // point to the beginning of the pixel data, bump it past
                // the escape bytes and then reset if decoding failed.
                m_decodedOffset += 2;
                if (!processNonRLEData(true, code)) {
                    m_decodedOffset -= 2;
                    return false;
                }
                break;
            }
        } else {  // Encoded mode
            // The following color data is repeated for |count| total pixels.
            // Strangely, some BMPs seem to specify excessively large counts
            // here; ignore pixels past the end of the row.
            const int endX = std::min(m_coord.x() + count, m_parent->size().width());

            if (m_infoHeader.biCompression == RLE24) {
                // Bail if there isn't enough data.
                if ((m_data->size() - m_decodedOffset) < 4)
                    return false;

                // One BGR triple that we copy |count| times.
                fillRGBA(endX, m_data->data()[m_decodedOffset + 3], m_data->data()[m_decodedOffset + 2], code, 0xff);
                m_decodedOffset += 4;
            } else {
                // RLE8 has one color index that gets repeated; RLE4 has two
                // color indexes in the upper and lower 4 bits of the byte,
                // which are alternated.
                size_t colorIndexes[2] = {code, code};
                if (m_infoHeader.biCompression == RLE4) {
                    colorIndexes[0] = (colorIndexes[0] >> 4) & 0xf;
                    colorIndexes[1] &= 0xf;
                }
                if ((colorIndexes[0] >= m_infoHeader.biClrUsed) || (colorIndexes[1] >= m_infoHeader.biClrUsed))
                    return setFailed();
                for (int which = 0; m_coord.x() < endX; ) {
                    setI(colorIndexes[which]);
                    which = !which;
                }

                m_decodedOffset += 2;
            }
        }
    }