📄 bmpimagereader.cpp
字号:
if ((m_decodedOffset > data->size()) || ((data->size() - m_decodedOffset) < m_tableSizeInBytes)) return false; m_colorTable.resize(m_infoHeader.biClrUsed); for (size_t i = 0; i < m_infoHeader.biClrUsed; ++i) { m_colorTable[i].rgbBlue = data->data()[m_decodedOffset++]; m_colorTable[i].rgbGreen = data->data()[m_decodedOffset++]; m_colorTable[i].rgbRed = data->data()[m_decodedOffset++]; // Skip padding byte (not present on OS/2 1.x). if (!m_isOS21x) ++m_decodedOffset; } // We've now decoded all the non-image data we care about. Skip anything // else before the actual raster data. if (m_imgDataOffset) m_decodedOffset = m_imgDataOffset; m_needToProcessColorTable = false; return true;}bool BMPImageReader::processRLEData(SharedBuffer* data){ if (m_decodedOffset > 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 ((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 = data->data()[m_decodedOffset]; const uint8_t code = data->data()[m_decodedOffset + 1]; if (((count != 0) || (code != 1)) && pastEndOfImage(0)) { m_failed = true; return false; } // Decode. if (count == 0) { switch (code) { case 0: // Magic token: EOL // Skip any remaining pixels in this row. if (m_coord.x() < size().width()) m_frameBufferCache.first().setHasAlpha(true); moveBufferToNextRow(); m_decodedOffset += 2; break; case 1: // Magic token: EOF // Skip any remaining pixels in the image. if ((m_coord.x() < size().width()) || (m_isTopDown ? (m_coord.y() < (size().height() - 1)) : (m_coord.y() > 0))) m_frameBufferCache.first().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 ((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 = data->data()[m_decodedOffset + 2]; const uint8_t dy = data->data()[m_decodedOffset + 3]; if ((dx != 0) || (dy != 0)) m_frameBufferCache.first().setHasAlpha(true); if (((m_coord.x() + dx) > size().width()) || pastEndOfImage(dy)) { m_failed = true; return false; } // 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(data, 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, size().width()); if (m_infoHeader.biCompression == RLE24) { // Bail if there isn't enough data. if ((data->size() - m_decodedOffset) < 4) return false; // One BGR triple that we copy |count| times. fillRGBA(endX, data->data()[m_decodedOffset + 3], 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)) { m_failed = true; return false; } for (int which = 0; m_coord.x() < endX; ) { setI(colorIndexes[which]); which = !which; } m_decodedOffset += 2; } } }}bool BMPImageReader::processNonRLEData(SharedBuffer* data, bool inRLE, int numPixels){ if (m_decodedOffset > data->size()) return false; if (!inRLE) numPixels = size().width(); // Fail if we're being asked to decode more pixels than remain in the row. const int endX = m_coord.x() + numPixels; if (endX > size().width()) { m_failed = true; return false; } // Determine how many bytes of data the requested number of pixels // requires. const size_t pixelsPerByte = 8 / m_infoHeader.biBitCount; const size_t bytesPerPixel = m_infoHeader.biBitCount / 8; const size_t unpaddedNumBytes = (m_infoHeader.biBitCount < 16) ? ((numPixels + pixelsPerByte - 1) / pixelsPerByte) : (numPixels * bytesPerPixel); // RLE runs are zero-padded at the end to a multiple of 16 bits. Non-RLE // data is in rows and is zero-padded to a multiple of 32 bits. const size_t alignBits = inRLE ? 1 : 3; const size_t paddedNumBytes = (unpaddedNumBytes + alignBits) & ~alignBits; // Decode as many rows as we can. (For RLE, where we only want to decode // one row, we've already checked that this condition is true.) while (!pastEndOfImage(0)) { // Bail if we don't have enough data for the desired number of pixels. if ((data->size() - m_decodedOffset) < paddedNumBytes) return false; if (m_infoHeader.biBitCount < 16) { // Paletted data. Pixels are stored little-endian within bytes. // Decode pixels one byte at a time, left to right (so, starting at // the most significant bits in the byte). const uint8_t mask = (1 << m_infoHeader.biBitCount) - 1; for (size_t byte = 0; byte < unpaddedNumBytes; ++byte) { uint8_t pixelData = data->data()[m_decodedOffset + byte]; for (size_t pixel = 0; (pixel < pixelsPerByte) && (m_coord.x() < endX); ++pixel) { const size_t colorIndex = (pixelData >> (8 - m_infoHeader.biBitCount)) & mask; if (m_andMaskState == Decoding) { // There's no way to accurately represent an AND + XOR // operation as an RGBA image, so where the AND values // are 1, we simply set the framebuffer pixels to fully // transparent, on the assumption that most ICOs on the // web will not be doing a lot of inverting. if (colorIndex) { setRGBA(0, 0, 0, 0); m_frameBufferCache.first().setHasAlpha(true); } else m_coord.move(1, 0); } else { if (colorIndex >= m_infoHeader.biClrUsed) { m_failed = true; return false; } setI(colorIndex); } pixelData <<= m_infoHeader.biBitCount; } } } else { // RGB data. Decode pixels one at a time, left to right. while (m_coord.x() < endX) { const uint32_t pixel = readCurrentPixel(data, bytesPerPixel); // Some BMPs specify an alpha channel but don't actually use it // (it contains all 0s). To avoid displaying these images as // fully-transparent, decode as if images are fully opaque // until we actually see a non-zero alpha value; at that point, // reset any previously-decoded pixels to fully transparent and // continue decoding based on the real alpha channel values. // As an optimization, avoid setting "hasAlpha" to true for // images where all alpha values are 255; opaque images are // faster to draw. int alpha = getAlpha(pixel); if (!m_seenNonZeroAlphaPixel && (alpha == 0)) { m_seenZeroAlphaPixel = true; alpha = 255; } else { m_seenNonZeroAlphaPixel = true; if (m_seenZeroAlphaPixel) { // The eraseARGB() call here also sets "hasAlpha" true. m_frameBufferCache.first().bitmap().eraseARGB(0, 0, 0, 0); m_seenZeroAlphaPixel = false; } else if (alpha != 255) m_frameBufferCache.first().setHasAlpha(true); } setRGBA(getComponent(pixel, 0), getComponent(pixel, 1), getComponent(pixel, 2), alpha); } } // Success, keep going. m_decodedOffset += paddedNumBytes; if (inRLE) return true; moveBufferToNextRow(); } // Finished decoding whole image. return true;}void BMPImageReader::moveBufferToNextRow(){ m_coord.move(-m_coord.x(), m_isTopDown ? 1 : -1);}} // namespace WebCore
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -