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

Side by Side Diff: third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp

Issue 2565323003: Move gif image decoder to SkCodec (Closed)
Patch Set: Removing impossible branch and old comment. Created 4 years 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 unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. 2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
12 * 12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */ 24 */
25 25
26 #include "platform/image-decoders/gif/GIFImageDecoder.h" 26 #include "platform/image-decoders/gif/GIFImageDecoder.h"
27 27
28 #include "platform/image-decoders/gif/GIFImageReader.h" 28 #include "base/logging.h"
29 #include "third_party/skia/include/core/SkImageInfo.h"
29 #include "wtf/NotFound.h" 30 #include "wtf/NotFound.h"
30 #include "wtf/PtrUtil.h" 31 #include "wtf/PtrUtil.h"
31 #include <limits> 32 #include <limits>
32 33
33 namespace blink { 34 namespace blink {
34 35
35 GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption, 36 GIFImageDecoder::GIFImageDecoder(AlphaOption alphaOption,
36 ColorSpaceOption colorOptions, 37 ColorSpaceOption colorOptions,
37 sk_sp<SkColorSpace> targetColorSpace, 38 sk_sp<SkColorSpace> targetColorSpace,
38 size_t maxDecodedBytes) 39 size_t maxDecodedBytes)
39 : ImageDecoder(alphaOption, 40 : ImageDecoder(alphaOption,
40 colorOptions, 41 colorOptions,
41 std::move(targetColorSpace), 42 std::move(targetColorSpace),
42 maxDecodedBytes), 43 maxDecodedBytes),
43 m_repetitionCount(cAnimationLoopOnce) {} 44 m_codec(),
45 m_segmentStream(nullptr) {}
44 46
45 GIFImageDecoder::~GIFImageDecoder() {} 47 GIFImageDecoder::~GIFImageDecoder() {
48 if (!m_codec) {
49 // if we did not create m_codec and thus did not pass ownership to it
50 if (m_segmentStream) {
51 delete m_segmentStream;
52 }
53 }
54 }
46 55
47 void GIFImageDecoder::onSetData(SegmentReader* data) { 56 void GIFImageDecoder::onSetData(SegmentReader* data) {
48 if (m_reader) 57 // Add the segment to our stream
49 m_reader->setData(data); 58 if (!m_segmentStream) {
59 m_segmentStream = new SegmentStream();
60 }
61
62 m_segmentStream->setReader(data, isAllDataReceived());
63
64 // If we don't have a SkCodec yet, create one from the stream
65 if (!m_codec) {
66 SkCodec* newCodec = SkCodec::NewFromStream(m_segmentStream);
67 if (newCodec) {
68 m_codec.reset(newCodec);
69 }
70 }
50 } 71 }
51 72
52 int GIFImageDecoder::repetitionCount() const { 73 int GIFImageDecoder::repetitionCount() const {
74 CHECK(m_codec);
75
53 // This value can arrive at any point in the image data stream. Most GIFs 76 // This value can arrive at any point in the image data stream. Most GIFs
54 // in the wild declare it near the beginning of the file, so it usually is 77 // in the wild declare it near the beginning of the file, so it usually is
55 // set by the time we've decoded the size, but (depending on the GIF and the 78 // set by the time we've decoded the size, but (depending on the GIF and the
56 // packets sent back by the webserver) not always. If the reader hasn't 79 // packets sent back by the webserver) not always.
57 // seen a loop count yet, it will return cLoopCountNotSeen, in which case we
58 // should default to looping once (the initial value for
59 // |m_repetitionCount|).
60 // 80 //
61 // There are some additional wrinkles here. First, ImageSource::clear() 81 // SkCodec will parse forward in the file if the repetition count has not been
62 // may destroy the reader, making the result from the reader _less_ 82 // seen yet.
63 // authoritative on future calls if the recreated reader hasn't seen the 83
64 // loop count. We don't need to special-case this because in this case the 84 int repetitionCount = m_codec->getRepetitionCount();
65 // new reader will once again return cLoopCountNotSeen, and we won't 85 switch (repetitionCount) {
66 // overwrite the cached correct value. 86 case 0:
67 // 87 return cAnimationNone;
68 // Second, a GIF might never set a loop count at all, in which case we 88 case SkCodec::kRepetitionCountInfinite:
69 // should continue to treat it as a "loop once" animation. We don't need 89 return cAnimationLoopInfinite;
70 // special code here either, because in this case we'll never change 90 default:
71 // |m_repetitionCount| from its default value. 91 return repetitionCount;
72 // 92 }
73 // Third, we use the same GIFImageReader for counting frames and we might
74 // see the loop count and then encounter a decoding error which happens
75 // later in the stream. It is also possible that no frames are in the
76 // stream. In these cases we should just loop once.
77 if (isAllDataReceived() && parseCompleted() && m_reader->imagesCount() == 1)
78 m_repetitionCount = cAnimationNone;
79 else if (failed() || (m_reader && (!m_reader->imagesCount())))
80 m_repetitionCount = cAnimationLoopOnce;
81 else if (m_reader && m_reader->loopCount() != cLoopCountNotSeen)
82 m_repetitionCount = m_reader->loopCount();
83 return m_repetitionCount;
84 } 93 }
85 94
86 bool GIFImageDecoder::frameIsCompleteAtIndex(size_t index) const { 95 bool GIFImageDecoder::frameIsCompleteAtIndex(size_t index) const {
87 return m_reader && (index < m_reader->imagesCount()) && 96 CHECK(m_codec);
88 m_reader->frameContext(index)->isComplete(); 97
98 std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo();
99
100 // If https://skia-review.googlesource.com/c/5635/ lands
101 if (index == 0) {
102 return (frameInfos.size() > 1 || isAllDataReceived());
103 // Either We have found a second frame, which means frame 0 is complete,
104 // or we have all the data, the image could be a single-frame and complete.
105 }
106
107 return frameInfos.size() > index;
108
109 // If https://skia-review.googlesource.com/c/5703/ lands:
110 // if (frameInfos.size() < index) {
111 // // index out of bounds as far as we have parsed
112 // return false;
113 //}
114 //
115 // return frameInfos[index].fFullyReceived;
89 } 116 }
90 117
91 float GIFImageDecoder::frameDurationAtIndex(size_t index) const { 118 float GIFImageDecoder::frameDurationAtIndex(size_t index) const {
92 return (m_reader && (index < m_reader->imagesCount()) && 119 CHECK(m_codec);
93 m_reader->frameContext(index)->isHeaderDefined()) 120
94 ? m_reader->frameContext(index)->delayTime() 121 std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo();
95 : 0; 122
123 if (frameInfos.size() < index) {
124 // index out of bounds as far as we have parsed
125 return 0;
126 }
127
128 return frameInfos[index].fDuration;
96 } 129 }
97 130
98 bool GIFImageDecoder::setFailed() { 131 void GIFImageDecoder::decodeSize() {
99 m_reader.reset(); 132 CHECK(m_codec);
100 return ImageDecoder::setFailed(); 133
134 SkImageInfo imageInfo = m_codec->getInfo();
135 setSize(imageInfo.width(), imageInfo.height());
101 } 136 }
102 137
103 bool GIFImageDecoder::haveDecodedRow(size_t frameIndex, 138 size_t GIFImageDecoder::decodeFrameCount() {
104 GIFRow::const_iterator rowBegin, 139 std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo();
105 size_t width, 140 return frameInfos.size();
106 size_t rowNumber, 141 }
107 unsigned repeatCount,
108 bool writeTransparentPixels) {
109 const GIFFrameContext* frameContext = m_reader->frameContext(frameIndex);
110 // The pixel data and coordinates supplied to us are relative to the frame's
111 // origin within the entire image size, i.e.
112 // (frameContext->xOffset, frameContext->yOffset). There is no guarantee
113 // that width == (size().width() - frameContext->xOffset), so
114 // we must ensure we don't run off the end of either the source data or the
115 // row's X-coordinates.
116 const int xBegin = frameContext->xOffset();
117 const int yBegin = frameContext->yOffset() + rowNumber;
118 const int xEnd = std::min(static_cast<int>(frameContext->xOffset() + width),
119 size().width());
120 const int yEnd = std::min(
121 static_cast<int>(frameContext->yOffset() + rowNumber + repeatCount),
122 size().height());
123 if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) ||
124 (yEnd <= yBegin))
125 return true;
126 142
127 const GIFColorMap::Table& colorTable = 143 void GIFImageDecoder::initializeNewFrame(size_t index) {
128 frameContext->localColorMap().isDefined() 144 CHECK(m_codec);
129 ? frameContext->localColorMap().getTable()
130 : m_reader->globalColorMap().getTable();
131 145
132 if (colorTable.isEmpty()) 146 ImageFrame& frame = m_frameBufferCache[index];
133 return true; 147 std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo();
134 148
135 GIFColorMap::Table::const_iterator colorTableIter = colorTable.begin(); 149 frame.setOriginalFrameRect(IntRect(IntPoint(), size()));
150 frame.setDuration(frameInfos[index].fDuration);
151 frame.setDisposalMethod(ImageFrame::DisposeKeep);
152 size_t requiredPreviousFrame = frameInfos[index].fRequiredFrame;
153 if (requiredPreviousFrame == SkCodec::kNone) {
154 requiredPreviousFrame = WTF::kNotFound;
155 }
156 frame.setRequiredPreviousFrameIndex(requiredPreviousFrame);
157 }
136 158
137 // Initialize the frame if necessary. 159 void GIFImageDecoder::decode(size_t index) {
138 ImageFrame& buffer = m_frameBufferCache[frameIndex]; 160 CHECK(m_codec);
139 if (!initFrameBuffer(frameIndex))
140 return false;
141 161
142 const size_t transparentPixel = frameContext->transparentPixel(); 162 if (m_frameBufferCache.size() < index) {
scroggo_chromium 2016/12/14 17:49:23 <=
cblume 2016/12/16 02:57:10 Done.
143 GIFRow::const_iterator rowEnd = rowBegin + (xEnd - xBegin); 163 // index out of bounds as far as we have parsed
144 ImageFrame::PixelData* currentAddress = buffer.getAddr(xBegin, yBegin); 164 return;
165 }
145 166
146 // We may or may not need to write transparent pixels to the buffer. 167 updateAggressivePurging(index);
147 // If we're compositing against a previous image, it's wrong, and if 168
148 // we're writing atop a cleared, fully transparent buffer, it's 169 SkImageInfo imageInfo = m_codec->getInfo().makeColorType(kN32_SkColorType);
149 // unnecessary; but if we're decoding an interlaced gif and 170 // Index8 has special behavior when getting the pixels, which we do not yet
scroggo_chromium 2016/12/14 17:49:23 This comment can be removed.
cblume 2016/12/16 02:57:10 Done.
150 // displaying it "Haeberli"-style, we must write these for passes 171 // handle
151 // beyond the first, or the initial passes will "show through" the 172
152 // later ones. 173 SkCodec::Options getPixelsOptions;
153 // 174 getPixelsOptions.fFrameIndex = index;
154 // The loops below are almost identical. One writes a transparent pixel 175 getPixelsOptions.fHasPriorFrame = false;
155 // and one doesn't based on the value of |writeTransparentPixels|. 176
156 // The condition check is taken out of the loop to enhance performance. 177 ImageFrame& frame = m_frameBufferCache[index];
157 // This optimization reduces decoding time by about 15% for a 3MB image. 178 if (frame.getStatus() != ImageFrame::FrameComplete) {
158 if (writeTransparentPixels) { 179 size_t requiredPreviousFrameIndex = frame.requiredPreviousFrameIndex();
159 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) { 180 if (requiredPreviousFrameIndex != WTF::kNotFound) {
160 const size_t sourceValue = *rowBegin; 181 getPixelsOptions.fHasPriorFrame = true;
161 if ((sourceValue != transparentPixel) &&
162 (sourceValue < colorTable.size())) {
163 *currentAddress = colorTableIter[sourceValue];
164 } else {
165 *currentAddress = 0;
166 m_currentBufferSawAlpha = true;
167 }
168 }
169 } else {
170 for (; rowBegin != rowEnd; ++rowBegin, ++currentAddress) {
171 const size_t sourceValue = *rowBegin;
172 if ((sourceValue != transparentPixel) &&
173 (sourceValue < colorTable.size()))
174 *currentAddress = colorTableIter[sourceValue];
175 else
176 m_currentBufferSawAlpha = true;
177 } 182 }
178 } 183 }
179 184
180 // Tell the frame to copy the row data if need be. 185 SkCodec::Result getPixelsResult =
181 if (repeatCount > 1) 186 m_codec->getPixels(imageInfo, frame.bitmap().getPixels(),
182 buffer.copyRowNTimes(xBegin, xEnd, yBegin, yEnd); 187 frame.bitmap().rowBytes(),
183 188 &getPixelsOptions, nullptr, nullptr);
184 buffer.setPixelsChanged(true); 189 if (getPixelsResult != SkCodec::kSuccess &&
185 return true; 190 getPixelsResult != SkCodec::kIncompleteInput) {
186 } 191 setFailed();
187 192 return;
188 bool GIFImageDecoder::parseCompleted() const {
189 return m_reader && m_reader->parseCompleted();
190 }
191
192 bool GIFImageDecoder::frameComplete(size_t frameIndex) {
193 // Initialize the frame if necessary. Some GIFs insert do-nothing frames,
194 // in which case we never reach haveDecodedRow() before getting here.
195 ImageFrame& buffer = m_frameBufferCache[frameIndex];
196 if (!initFrameBuffer(frameIndex))
197 return false; // initFrameBuffer() has already called setFailed().
198
199 buffer.setStatus(ImageFrame::FrameComplete);
200
201 if (!m_currentBufferSawAlpha) {
202 // The whole frame was non-transparent, so it's possible that the entire
203 // resulting buffer was non-transparent, and we can setHasAlpha(false).
204 if (buffer.originalFrameRect().contains(IntRect(IntPoint(), size()))) {
205 buffer.setHasAlpha(false);
206 buffer.setRequiredPreviousFrameIndex(kNotFound);
207 } else if (buffer.requiredPreviousFrameIndex() != kNotFound) {
208 // Tricky case. This frame does not have alpha only if everywhere
209 // outside its rect doesn't have alpha. To know whether this is
210 // true, we check the start state of the frame -- if it doesn't have
211 // alpha, we're safe.
212 const ImageFrame* prevBuffer =
213 &m_frameBufferCache[buffer.requiredPreviousFrameIndex()];
214 ASSERT(prevBuffer->getDisposalMethod() !=
215 ImageFrame::DisposeOverwritePrevious);
216
217 // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then
218 // we can say we have no alpha if that frame had no alpha. But
219 // since in initFrameBuffer() we already copied that frame's alpha
220 // state into the current frame's, we need do nothing at all here.
221 //
222 // The only remaining case is a DisposeOverwriteBgcolor frame. If
223 // it had no alpha, and its rect is contained in the current frame's
224 // rect, we know the current frame has no alpha.
225 if ((prevBuffer->getDisposalMethod() ==
226 ImageFrame::DisposeOverwriteBgcolor) &&
227 !prevBuffer->hasAlpha() &&
228 buffer.originalFrameRect().contains(prevBuffer->originalFrameRect()))
229 buffer.setHasAlpha(false);
230 }
231 } 193 }
232 194
233 return true; 195 if (!postDecodeProcessing(index)) {
234 } 196 return;
197 }
235 198
236 void GIFImageDecoder::clearFrameBuffer(size_t frameIndex) { 199 // If we do not end up landing https://skia-review.googlesource.com/c/5635/
237 if (m_reader && 200 // ...
238 m_frameBufferCache[frameIndex].getStatus() == ImageFrame::FramePartial) {
239 // Reset the state of the partial frame in the reader so that the frame
240 // can be decoded again when requested.
241 m_reader->clearDecodeState(frameIndex);
242 }
243 ImageDecoder::clearFrameBuffer(frameIndex);
244 }
245
246 size_t GIFImageDecoder::decodeFrameCount() {
247 parse(GIFFrameCountQuery);
248 // If decoding fails, |m_reader| will have been destroyed. Instead of
249 // returning 0 in this case, return the existing number of frames. This way
250 // if we get halfway through the image before decoding fails, we won't
251 // suddenly start reporting that the image has zero frames.
252 return failed() ? m_frameBufferCache.size() : m_reader->imagesCount();
253 }
254
255 void GIFImageDecoder::initializeNewFrame(size_t index) {
256 ImageFrame* buffer = &m_frameBufferCache[index];
257 const GIFFrameContext* frameContext = m_reader->frameContext(index);
258 buffer->setOriginalFrameRect(
259 intersection(frameContext->frameRect(), IntRect(IntPoint(), size())));
260 buffer->setDuration(frameContext->delayTime());
261 buffer->setDisposalMethod(frameContext->getDisposalMethod());
262 buffer->setRequiredPreviousFrameIndex(
263 findRequiredPreviousFrame(index, false));
264 }
265
266 void GIFImageDecoder::decode(size_t index) {
267 parse(GIFFrameCountQuery);
268
269 if (failed())
270 return;
271
272 updateAggressivePurging(index);
273
274 Vector<size_t> framesToDecode = findFramesToDecode(index);
275 for (auto i = framesToDecode.rbegin(); i != framesToDecode.rend(); ++i) {
276 if (!m_reader->decode(*i)) {
277 setFailed();
278 return;
279 }
280
281 // If this returns false, we need more data to continue decoding.
282 if (!postDecodeProcessing(*i))
283 break;
284 }
285 201
286 // It is also a fatal error if all data is received and we have decoded all 202 // It is also a fatal error if all data is received and we have decoded all
287 // frames available but the file is truncated. 203 // frames available but the file is truncated.
288 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived() && 204 if (index >= m_frameBufferCache.size() - 1 && isAllDataReceived()) {
289 m_reader && !m_reader->parseCompleted())
290 setFailed(); 205 setFailed();
291 }
292
293 void GIFImageDecoder::parse(GIFParseQuery query) {
294 if (failed())
295 return;
296
297 if (!m_reader) {
298 m_reader = makeUnique<GIFImageReader>(this);
299 m_reader->setData(m_data);
300 } 206 }
301
302 if (!m_reader->parse(query))
303 setFailed();
304 }
305
306 void GIFImageDecoder::onInitFrameBuffer(size_t frameIndex) {
307 m_currentBufferSawAlpha = false;
308 } 207 }
309 208
310 bool GIFImageDecoder::canReusePreviousFrameBuffer(size_t frameIndex) const { 209 bool GIFImageDecoder::canReusePreviousFrameBuffer(size_t frameIndex) const {
311 DCHECK(frameIndex < m_frameBufferCache.size()); 210 DCHECK(frameIndex < m_frameBufferCache.size());
312 return m_frameBufferCache[frameIndex].getDisposalMethod() != 211 return m_frameBufferCache[frameIndex].getDisposalMethod() !=
313 ImageFrame::DisposeOverwritePrevious; 212 ImageFrame::DisposeOverwritePrevious;
314 } 213 }
315 214
316 } // namespace blink 215 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698