Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(532)

Unified Diff: third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp

Issue 1567053002: Animated PNG implementation Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Moved some code to PNGImageReader.cpp and PNGImageReader.h Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
diff --git a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
index 177b58af86dbbebe6f631714da4501214ac11b31..4f9d48a70837fc2e82494d9aeb984307415b8a38 100644
--- a/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
+++ b/third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.cpp
@@ -38,11 +38,18 @@
#include "platform/image-decoders/png/PNGImageReader.h"
+#include "platform/RuntimeEnabledFeatures.h"
+#include "platform/image-decoders/FastSharedBufferReader.h"
+
#include "platform/image-decoders/SegmentReader.h"
#include "png.h"
#include "wtf/PtrUtil.h"
+#include "zlib.h"
#include <memory>
+#define get16(p) ((p)[0] << 8 | (p)[1])
+#define get32(p) ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])
+
namespace {
inline blink::PNGImageDecoder* imageDecoder(png_structp png) {
@@ -75,13 +82,22 @@ namespace blink {
PNGImageReader::PNGImageReader(PNGImageDecoder* decoder, size_t readOffset)
: m_decoder(decoder),
m_readOffset(readOffset),
- m_currentBufferSize(0),
- m_decodingSizeOnly(false),
+ m_parseOffset(readOffset),
+ m_decodeOffset(readOffset),
+ m_infoSize(0),
+ m_isAnimated(false),
+ m_isParsed(false),
+ m_width(0),
+ m_height(0),
+ m_repetitionCount(cAnimationNone),
+ m_posterFrame(0),
+ m_visibleFrames(1),
m_hasAlpha(false) {
m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
m_info = png_create_info_struct(m_png);
png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
pngRowAvailable, pngComplete);
+ memset(&m_currentFrame, 0, sizeof(m_currentFrame));
}
PNGImageReader::~PNGImageReader() {
@@ -91,26 +107,208 @@ PNGImageReader::~PNGImageReader() {
m_readOffset = 0;
}
-bool PNGImageReader::decode(const SegmentReader& data, bool sizeOnly) {
- m_decodingSizeOnly = sizeOnly;
+// PNG signature and IHDR size
+static const size_t headerSize = 33;
+// fcTL size
+static const size_t frameControlSize = 34;
- // We need to do the setjmp here. Otherwise bad things will happen.
- if (setjmp(JMPBUF(m_png)))
- return m_decoder->setFailed();
-
- const char* segment;
- while (size_t segmentLength = data.getSomeData(segment, m_readOffset)) {
- m_readOffset += segmentLength;
- m_currentBufferSize = m_readOffset;
- png_process_data(m_png, m_info,
- reinterpret_cast<png_bytep>(const_cast<char*>(segment)),
- segmentLength);
- if (sizeOnly ? m_decoder->isDecodedSizeAvailable()
- : m_decoder->frameIsCompleteAtIndex(0))
+bool PNGImageReader::parse(SegmentReader& data) {
+ const unsigned long maxPNGSize = 1000000UL;
+ size_t inputSize = data.size();
+ const png_byte* chunk;
+ FastSharedBufferReader reader(&data);
+ char readBuffer[frameControlSize];
+
+ if (m_parseOffset == m_readOffset) {
+ if (m_readOffset + headerSize > inputSize)
+ return false;
+
+ chunk = reinterpret_cast<const png_byte*>(reader.getConsecutiveData(
+ m_readOffset + 8, headerSize - 8, readBuffer));
+ const png_byte* chunkId = chunk + 4;
+ png_uint_32 chunkSize = get32(chunk);
+ if (memcmp(chunkId, "IHDR", 4) || chunkSize != 13)
+ return false;
+ m_currentFrame.width = m_width = get32(chunk + 8);
+ m_currentFrame.height = m_height = get32(chunk + 12);
+ if (m_width > maxPNGSize || m_height > maxPNGSize)
+ return false;
+ png_byte bitDepth = chunk[16];
+ if (bitDepth != 1 && bitDepth != 2 && bitDepth != 4 && bitDepth != 8 &&
+ bitDepth != 16)
+ return false;
+ png_byte colorType = chunk[17];
+ if (colorType == 1 || colorType == 5 || colorType > 6)
+ return false;
+ png_uint_32 chunkCrc = get32(chunk + 21);
+ if (crc32(crc32(0, Z_NULL, 0), chunkId, 17) != chunkCrc)
+ return false;
+ m_infoSize = m_parseOffset = m_readOffset + headerSize;
+ }
+
+ while (!m_isParsed) {
+ if (m_parseOffset + 8 > inputSize)
+ return false;
+
+ chunk = reinterpret_cast<const png_byte*>(
+ reader.getConsecutiveData(m_parseOffset, 8, readBuffer));
+ const png_byte* chunkId = chunk + 4;
+ png_uint_32 chunkSize = get32(chunk);
+
+ if (!memcmp(chunkId, "IDAT", 4) && !m_currentFrame.start) {
+ m_currentFrame.start = m_parseOffset;
+ if (!m_decoder->setSize(m_width, m_height))
+ return false;
+ png_destroy_read_struct(&m_png, &m_info, 0);
+ m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
+ m_info = png_create_info_struct(m_png);
+ png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
+ pngRowAvailable, pngComplete);
+
+ if (setjmp(JMPBUF(m_png)))
+ return false;
+
+ processData(data, 0, m_readOffset, m_parseOffset + 8);
+ }
+
+ size_t chunkEnd = m_parseOffset + chunkSize + 12;
+ if (chunkEnd > inputSize)
+ return false;
+
+ if (!memcmp(chunkId, "IEND", 4) && !chunkSize) {
+ if (!m_currentFrame.start)
+ return false;
+ m_isParsed = true;
+ m_currentFrame.finish = chunkEnd;
+ m_frames.append(m_currentFrame);
return true;
+ }
+
+ if (RuntimeEnabledFeatures::animatedPNGEnabled()) {
+ if (!memcmp(chunkId, "fdAT", 4) && m_isAnimated &&
+ !m_currentFrame.start) {
+ m_currentFrame.start = m_parseOffset;
+ } else if (!memcmp(chunkId, "acTL", 4) && chunkSize == 8 &&
+ !m_isAnimated && !m_currentFrame.start) {
+ chunk = reinterpret_cast<const png_byte*>(
+ reader.getConsecutiveData(m_parseOffset, 16, readBuffer));
+ m_isAnimated = true;
+ m_posterFrame = 1;
+ m_visibleFrames = 0;
+ m_repetitionCount = static_cast<int>(get32(chunk + 12)) - 1;
+ } else if (!memcmp(chunkId, "fcTL", 4) && chunkSize == 26 &&
+ (m_isAnimated || !m_currentFrame.start)) {
+ if (m_currentFrame.start) {
+ m_currentFrame.finish = chunkEnd;
+ m_frames.append(m_currentFrame);
+ m_currentFrame.start = 0;
+ } else if (m_frames.isEmpty()) {
+ m_posterFrame = 0;
+ }
+
+ chunk = reinterpret_cast<const png_byte*>(reader.getConsecutiveData(
+ m_parseOffset, frameControlSize, readBuffer));
+ m_currentFrame.width = get32(chunk + 12);
+ m_currentFrame.height = get32(chunk + 16);
+ m_currentFrame.xOffset = get32(chunk + 20);
+ m_currentFrame.yOffset = get32(chunk + 24);
+ unsigned numerator = get16(chunk + 28);
+ unsigned denominator = get16(chunk + 30);
+ m_currentFrame.duration = !denominator
+ ? numerator * 10
+ : (int)(numerator * 1000.0 / denominator);
+ m_currentFrame.dispose = chunk[32];
+ m_currentFrame.blend = chunk[33];
+
+ if (m_currentFrame.width > maxPNGSize ||
+ m_currentFrame.height > maxPNGSize ||
+ m_currentFrame.xOffset > maxPNGSize ||
+ m_currentFrame.yOffset > maxPNGSize ||
+ m_currentFrame.xOffset + m_currentFrame.width > m_width ||
+ m_currentFrame.yOffset + m_currentFrame.height > m_height ||
+ m_currentFrame.dispose > 2 || m_currentFrame.blend > 1)
+ return false;
+
+ if (!m_visibleFrames) {
+ m_currentFrame.blend = 0;
+ if (m_currentFrame.dispose == 2)
+ m_currentFrame.dispose = 1;
+ }
+ m_visibleFrames++;
+ } else if (!m_currentFrame.start && m_frames.isEmpty()) {
+ m_infoSize = chunkEnd;
+ }
+ }
+
+ m_parseOffset = chunkEnd;
+ }
+ return true;
+}
+
+bool PNGImageReader::decode(SegmentReader& data, size_t index) {
+ static png_byte dataIEND[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130};
+
+ index += m_posterFrame;
+ if (index && index >= m_frames.size())
+ return true;
+
+ if (index || m_decodeOffset == m_readOffset) {
+ png_destroy_read_struct(&m_png, &m_info, 0);
+ m_png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, pngFailed, 0);
+ m_info = png_create_info_struct(m_png);
+ png_set_progressive_read_fn(m_png, m_decoder, pngHeaderAvailable,
+ pngRowAvailable, pngComplete);
}
- return false;
+ if (setjmp(JMPBUF(m_png)))
+ return false;
+
+ if (!index) {
+ size_t endOffset = !m_frames.isEmpty() ? m_frames[0].finish : 0;
+ processData(data, index, m_decodeOffset, endOffset);
+ if (m_decodeOffset == endOffset)
+ png_process_data(m_png, m_info, dataIEND, 12);
+ } else {
+ const png_byte* chunk;
+ FastSharedBufferReader reader(&data);
+ char readBuffer[headerSize];
+
+ m_decodeOffset = m_readOffset;
+ size_t offset = m_frames[index].start;
+ size_t endOffset = m_frames[index].finish;
+ if (data.size() < endOffset)
+ return true;
+
+ png_set_crc_action(m_png, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
+ if (m_frames[index].width != m_width ||
+ m_frames[index].height != m_height) {
+ unsigned char header[headerSize];
+ chunk = reinterpret_cast<const png_byte*>(
+ reader.getConsecutiveData(m_readOffset, headerSize, readBuffer));
+ memcpy(&header[0], chunk, headerSize);
+ png_save_uint_32(&header[16], m_frames[index].width);
+ png_save_uint_32(&header[20], m_frames[index].height);
+ png_process_data(m_png, m_info, header, headerSize);
+ processData(data, index, m_readOffset + headerSize, m_infoSize);
+ } else {
+ processData(data, index, m_readOffset, m_infoSize);
+ }
+
+ png_byte dataIDAT[8] = {0, 0, 0, 0, 73, 68, 65, 84};
+ while (offset < endOffset) {
+ chunk = reinterpret_cast<const png_byte*>(
+ reader.getConsecutiveData(offset, 8, readBuffer));
+ png_uint_32 chunkSize = get32(chunk);
+ if (!memcmp(chunk + 4, "fdAT", 4)) {
+ png_save_uint_32(&dataIDAT[0], chunkSize - 4);
+ png_process_data(m_png, m_info, dataIDAT, 8);
+ processData(data, index, offset + 12, offset + 12 + chunkSize);
+ }
+ offset += chunkSize + 12;
+ }
+ png_process_data(m_png, m_info, dataIEND, 12);
+ }
+ return true;
}
} // namespace blink
« no previous file with comments | « third_party/WebKit/Source/platform/image-decoders/png/PNGImageReader.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698