|
|
DescriptionUse SkCodec internally in GIFImageDecoder
Previously, GIFImageDecoder used GIFImageReader internally. SkCodec uses a
modified version of that class (SkGifImageReader; adapted in
crrev.com/2045293002). SkCodec provides the following benefits:
- SIMD optimized code for writing pixels
- an API that allows the client to handle caching
- flexibility regarding the required frame to use
- the ability to decode scaled versions of images
- subset decoding (i.e. issue 468914)
(not fully implemented for GIF)
- the ability to decode to half-width float format
In addition, this patch enables sharing code between Android, Skia, and
Chromium. This means that new features/bug fixes in Android benefit Chromium
and vice versa.
For larger images (above ~60x60), the SIMD optimizations show a much bigger
benefit (up to 24% in one case). For most images, decoding speed is about the
same. Images with many frames that contain tiny update regions are a hair
slower. The mean decode time across all tested images showed an improvement. Raw
performance numbers can be found here:
https://docs.google.com/spreadsheets/d/1JqCPdYmbasOwKRdvuG6ZI4gwq9dPvnJA9oDquh7SxOQ/edit?usp=sharing
GIFImageDecoder still handles the cached frames currently, but this change will
allow future changes in Blink to make wiser caching Decisions (such as keeping
all frames of a 3-frame animation if those frames are small). Full road map:
https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqUJ8AMppouPQ_DLrvpk/edit?usp=sharing
This results in some behavior changes:
- SkCodec does not check the alpha of each pixel during decode (for speed and
simplicity).
As a result, GIFImageDecoder no longer corrects opacity or the required frame
after decoding a frame. No performance penalty has been observed for
incorrectly leaving a frame marked as having transparency.
- SkCodec guesses transparency based on the presence of a
transparent index (in addition to being subset) and uses this to
potentially determine an earlier required frame.
BUG=715812
Review-Url: https://codereview.chromium.org/2565323003
Cr-Commit-Position: refs/heads/master@{#495230}
Committed: https://chromium.googlesource.com/chromium/src/+/4fed3346549a90c0de40c02f6388e19e8151e92a
Patch Set 1 #
Total comments: 46
Patch Set 2 : Addressing code review comments. #Patch Set 3 : Removing impossible branch and old comment. #
Total comments: 14
Patch Set 4 : Add incremental decode, address code review comments. #
Total comments: 22
Patch Set 5 : Address code review comments. #Patch Set 6 : Adding color correction info. #Patch Set 7 : Fix formatting #Patch Set 8 : Handle incomplete frame info, look into reusing the existing frame buffer. #Patch Set 9 : Updating Skia build files #
Total comments: 1
Patch Set 10 : Fix BUILD.gn -- now builds #
Total comments: 8
Patch Set 11 : Add the ability to reuse previous buffers rather than copy them. #
Total comments: 4
Patch Set 12 : Fix failing CHECK()s. #Patch Set 13 : Rebase #
Total comments: 2
Patch Set 14 : Refactoring #Patch Set 15 : Fixing build. #
Total comments: 2
Patch Set 16 : Peek now loops over a corrected offset. #Patch Set 17 : Adding move() to SkStream subclass. #Patch Set 18 : Fixing cast to size_t #Patch Set 19 : fix peek() reading the wrong size -- now passing GIFImageDecoderTest.progressiveDecode #
Total comments: 1
Patch Set 20 : Fixing the alpha, not changing frame between init and decode. #
Total comments: 5
Patch Set 21 : Remove invalid disposal method test #
Total comments: 1
Patch Set 22 : Handling invalid data #Patch Set 23 : Don't initialize the frame until we know the frame exists. #Patch Set 24 : Set the frame status after marking that pixels have changed #Patch Set 25 : Lock / unlock pixels when copying and decoding. #Patch Set 26 : Now parsing what we can -- don't need to trigger parse #Patch Set 27 : Override frameStatusSufficientForSuccessors since we now need the completed frame #Patch Set 28 : Only call setSizeAndColorSpace when we won't be copying from a previous frame #Patch Set 29 : Remove lockPixels() / unlockPixels() #Patch Set 30 : Only set frame to partial for frame 0 #
Total comments: 3
Patch Set 31 : Removing temporary test rigging #Patch Set 32 : Rebasing #Patch Set 33 : Accidentally left in some relics from printf-style debugging #Patch Set 34 : Allocate frames as needed instead of in advance #Patch Set 35 : Fixing build. Cleaning up frame allocation #Patch Set 36 : Fixing GIFImageDecoderTest.verifyRepetitionCount test to reflect new behavior #Patch Set 37 : Fix GIFImageDecoderTest.randomFrameDecode -- possibly fixing animated images but I cannot see from … #Patch Set 38 : Rebasing -- include orders are now changed #
Total comments: 42
Patch Set 39 : Rebasing #Patch Set 40 : Addressing code review comments #Patch Set 41 : Fixing setFailed() on incomplete frame 0 #
Total comments: 12
Patch Set 42 : Fixing code review comments #Patch Set 43 : Fix dereferencing nullptr errors #
Total comments: 26
Patch Set 44 : Fixing code review comments (DCHECK, remove dead branch, clarify comments) #
Total comments: 5
Patch Set 45 : Add setSizeAndColorSpaceWithoutZeroFilling. Check incomplete frames #Patch Set 46 : Fix DeferredImageDecoderTestWoPlatform.mixImagesGif by tracking onSetData(nullptr) #
Total comments: 10
Patch Set 47 : Rebasing #Patch Set 48 : Store frame info. Try to not keep track of whether the SegmentStream was cleared #Patch Set 49 : Rebasing #Patch Set 50 : Fixing build issue #Patch Set 51 : Rebasing #Patch Set 52 : Use new allocatePixelData() #Patch Set 53 : Adding isCleared() back #Patch Set 54 : Zero fill pixel data when allocating #
Total comments: 6
Patch Set 55 : Fix zero init -- it is an enum, not a bool #Patch Set 56 : Fix include order #Patch Set 57 : Fix potential memory leak #
Total comments: 1
Patch Set 58 : Move SegmentStream out of GIFImageDecoder #Patch Set 59 : Add SegmentStream to BUILD.gn #Patch Set 60 : Fixing unknown identifier #Patch Set 61 : Fix build, mixImagesGif test, and verifyRepetitionCount test #
Total comments: 3
Patch Set 62 : Adding SegmentStreamTest.cpp, more null checks, and updating m_hasReadAllContents more. #
Total comments: 1
Patch Set 63 : Rebase #Patch Set 64 : Fix Windows build #Patch Set 65 : Rebase with new Blink naming scheme #Patch Set 66 : Rebase with new Blink naming scheme #Patch Set 67 : Rebase #Patch Set 68 : Adding per-frame alpha #Patch Set 69 : Fix GIFImageDecoderTest.externalAllocator and DeferredImageDecoderTest.frameOpacity #Patch Set 70 : Update naming of Size() #Patch Set 71 : Setting has alpha on a partial decode. #
Total comments: 63
Patch Set 72 : General code clean-up #Patch Set 73 : Rebasing #Patch Set 74 : Fixing Windows build #Patch Set 75 : Fix gif fill color not being transparent #Patch Set 76 : Fix DeferredImageDecoderTestWoPlatform.mixImagesGif #
Total comments: 12
Patch Set 77 : Fixing dependent frames not setting status to allocated. #Patch Set 78 : Code review comment changes #Patch Set 79 : Only delete the SegmentStream pointer if the SkCodec wasn't created. #Patch Set 80 : Fixing GIFImageDecoderTest.verifyRepetitionCount in the case where there is no animation. #Patch Set 81 : Handling 1-frame images returning no animation. #
Total comments: 5
Patch Set 82 : Code review comments #
Total comments: 22
Patch Set 83 : More code review comments #
Total comments: 35
Patch Set 84 : Cleaning up SegmentStream tests #
Total comments: 2
Patch Set 85 : Rebasing #Patch Set 86 : Make SegmentStream movable, clean naming #
Total comments: 1
Patch Set 87 : Fixing missing array include, cleaning up the tests #Patch Set 88 : Using a long instead of a size_t for the offset #Patch Set 89 : Fixing wrong literal being used #Patch Set 90 : Rebase & format #Patch Set 91 : Add libjpeg-turbo to list of Skia library configs #
Total comments: 2
Patch Set 92 : Rebase #Patch Set 93 : Use Chromium's libjpeg-turb and not Skia's #Patch Set 94 : Fix build after rebase #Patch Set 95 : See build errors for libjpeg_turbo on iOS #Patch Set 96 : See the linker errors when iOS doesn't have libjpeg_turbo #Patch Set 97 : Only include SkCodec for non-iOS targets #
Total comments: 22
Patch Set 98 : Simplify frame allocation code inside ImageFrame::CopyBitmapData #Patch Set 99 : Use SkCodec::getFrameInfo(index) instead of copying a vector #Patch Set 100 : Create SkImageInfo using desired color space #Patch Set 101 : Call SetFailed() after zeroing extra lines on partially-decoded first frame #Patch Set 102 : Use ImageDecoder::Size() instead of ImageFrame::OriginalFrameRect() for clarity #Patch Set 103 : Only zero fill the first frame when the frame is incomplete #Patch Set 104 : Simplify setting alpha back to its pre-zeroed value #Patch Set 105 : Rebase #Patch Set 106 : Add SkBmpBaseCodec.cpp to the build #Patch Set 107 : Rebase #Patch Set 108 : Fill image with alpha after an error is encountered #
Total comments: 11
Patch Set 109 : Provide previous frame #Patch Set 110 : Remove dead code #Patch Set 111 : Use disposal method for frame reuse #Patch Set 112 : Fix build #
Total comments: 5
Patch Set 113 : Allow frames after a DisposePrevious to be reused #Patch Set 114 : Only zero-fill the first frame #Patch Set 115 : Mark error-in-input frames as empty, so they are not displayed #
Total comments: 1
Patch Set 116 : Always zero-initialize every frame completely #
Total comments: 4
Patch Set 117 : Rebase #Patch Set 118 : Only zero-fill independent frames. Don't call SetPixelsChanged() until after decode #Patch Set 119 : Preserve frame alpha beyond zero filling #Patch Set 120 : Fix gif alpha errors #Patch Set 121 : Correct misleading comment #Patch Set 122 : Initialize new frames with empty status. Sufficient frames should not include the allocated state #Patch Set 123 : Override ClearCacheExceptFrame to handle SkCodec-specific behavior #Patch Set 124 : Decode previous frame if incomplete. Make sure we don't search past kNotFound #Patch Set 125 : Support SkCodec::NewFromStream finding an error in the input #
Total comments: 61
Patch Set 126 : Add default initialization to SegmentStream's members. Explicitly default the default ctor. Add mis… #Patch Set 127 : Remove unnecessary peek_position variable #Patch Set 128 : SegmentStream::move now DCHECKs against negative offsets #Patch Set 129 : Simplify & clarify cache clearing #Patch Set 130 : Remove duplicated variable assignments #Patch Set 131 : Clarify code with mo'bettah comments #
Total comments: 40
Patch Set 132 : Rebase #Patch Set 133 : Fix rebase mistake #Patch Set 134 : Improve comments #Patch Set 135 : Remove redundant parameter names from header. Utilize blink namespace #Patch Set 136 : Improve readability #Patch Set 137 : Only fill in frame info if we could get it #Patch Set 138 : Clean decode-needed-frame code #Patch Set 139 : Rebase #Patch Set 140 : Remove SegmentStream test to check negative move clamps to 0 #
Total comments: 7
Patch Set 141 : Remove unused constant #Patch Set 142 : DCHECK the result of codec_->getFrameInfo() #Patch Set 143 : Do not specify WTF:: for kNotFound #Patch Set 144 : Remove useless check for complete frames prior to decoding them #
Total comments: 2
Patch Set 145 : Remove extra calls to SetHasAlpha() #
Total comments: 9
Patch Set 146 : Check if the stream has been cleared before any SkCodec call which might advance. Remove dtor which… #
Total comments: 8
Patch Set 147 : Cache last-seen repetition count for use when buffer cleared #Patch Set 148 : Remove redundant check for IsCleared() #Patch Set 149 : Keep a cache of the max fully received frame index #
Total comments: 3
Patch Set 150 : Fix broken if branch #Patch Set 151 : Rebase #Patch Set 152 : Updated API call to reflect API change #Patch Set 153 : Updated API call to reflect API change #Patch Set 154 : Fix invalid disposal method test #Patch Set 155 : Correct comments. Better variable names. Better DCHECK. #
Total comments: 20
Patch Set 156 : Using override & default-in-cpp for destructors #Patch Set 157 : Fix invalid DCHECK #Patch Set 158 : Move max complete index caching into DecodeFrameCount() #Patch Set 159 : Only check this and the last frame for completion #Patch Set 160 : Don't use deprecated PassRefPtr #
Total comments: 6
Patch Set 161 : Rebase #Patch Set 162 : Simplify code by removing cached values. #
Total comments: 4
Patch Set 163 : Cache repetition count. Use SkCodec's cache of fully received. #
Total comments: 5
Patch Set 164 : Rename variable. Return last-known frame count upon error / clearing #
Total comments: 8
Patch Set 165 : Forward declare SegmentReader in SegmentStream.h #Patch Set 166 : Don't use deprecated PassRefPtr #Patch Set 167 : Enforce all calls to GetViableReferenceFrameIndex() to require a valid previous frame #Patch Set 168 : Replace SegmentStream::SetPositionState() #
Total comments: 11
Patch Set 169 : Rebase #Patch Set 170 : Compare against previous frame, not status #Patch Set 171 : Use =default for SegmentStream's move ctor & assignment #Patch Set 172 : Use =default for SegmentStream's move ctor & assignment #Patch Set 173 : Remove superfluous variable #Patch Set 174 : Use DCHECK for IsCleared() inside SegmentStream #Patch Set 175 : Fix typo #Patch Set 176 : Explicitly specify move ctor / assignment until required patch lands. #Dependent Patchsets: Messages
Total messages: 601 (433 generated)
cblume@chromium.org changed reviewers: + scroggo@chromium.org, scroggo@google.com
PTAL This is not ready to submit. There are comments in the code that point this out. In fact, it fails some presubmit tests about being able to include the Skia files and base/logging.h . I need to make sure to re-run the presubmit checks before finishing.
https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:45: m_segmentStream(new SegmentStream{}) {} Why not make m_segmentStream a full member? Oh wait, m_codec is going to want to take ownership of it... I think you need to do the following: - Store a raw pointer to m_segmentStream - In the destructor, delete m_segmentStream if !m_codec A related issue - SkCodec::NewFromStream will delete m_segmentStream on failure, so you'll need to recreate it if that call fails. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:88: return (frameInfoes.size() >= 1 || m_segmentStream->isAtEnd); frameInfos* m_segmentStream->isAtEnd()* - but doesn't the GIFImageDecoder know if all the data was received? isAllDataReceived()? And I think you want frameInfos.size() > 1, not >=. > means we've seen the second frame, so the first frame must be complete. Back to our discussion on those CLs - we'll be wrong between receiving the start and the end of the second frame; during that time we'll incorrectly report that frame 0 is incomplete. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:132: if (m_frameBufferCache.size() < index) { I think you want <=, here and elsewhere. (And the comment below may not be necessary. Maybe it's just obvious to me, and not to someone else.) Should this ever happen in this case, though? In the old code we do not make this check. I think this only gets called when m_frameBufferCache is big enough. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:138: std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); Since we call this so often, I think we should consider caching it. (One option I like is to make SkCodec store it and return a reference. It knows whether it needed to change anything, so when it parses it can only fill in the new ones. Then you don't have to worry about it here.) https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:143: // TODO: SkCodec doesn't expose the disposal method. These might just be awkward during the transition. I'm not convinced you need to know the disposal method or original frame rect, since that will be handled internally by SkCodec. But maybe I'm missing something? https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:163: // imageInfo's color type may be influenced by the untrusted image file. We report index8 if the GIF can support it (for the first frame). A GIF frame only uses 256 colors directly, but if it blends with a prior frame, it may use more. Since we only consider the first frame to potentially use index8, we do not need to worry about prior frames, but we do need to worry about transparency. If the frame is subset, and it does *not* have a transparent pixel, we don't support index8, since we don't have anything to fill the background with. Here's the Skia code: https://skia.googlesource.com/skia/+/36aa176d0/third_party/gif/SkGifImageRead... (We could take this even further - if there is no transparent pixel, but the color table is less than 256 colors, we could add an extra color to be transparent. But we have not pursued that.) All of this is just to say that it's fine for the codec to report index8. As a client, you just need to request the color type that you want. In this case, you want kN32: SkImageInfo imageInfo = m_codec->getInfo()->makeColorType(kN32_SkColorType); https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:174: // Said another way, is there a way to know if we already populated this Check frame.getStatus()? https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:178: if (requiredPreviousFrameIndex == WTF::kNotFound) { I think you mean != WTF::kNotFound? https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: frame.copyBitmapData(requiredPreviousFrame); Doesn't this happen in initFrameBuffer? https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: SkCodec::Result getPixelsResult = You want to switch from getPixels() to startIncrementalDecode() and incrementalDecode(). Those let you continue decoding where you left off. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:188: if (getPixelResult != kSuccess) { kIncomplete is also okay, so long as it's the latest frame. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:29: #include "codec/SkCodec.h" I thought these were typically included as #include "third_party/skia/include/codec/SkCodec.h" https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:68: class SegmentStream : public SkStream { Add a constructor to initialize m_isDone and m_position? https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:76: m_position += len; I think this should only advance to the end of the reader. e.g. const size_t bytesSkipped = std::min(len, m_reader->size()); m_position += bytesSkipped; return bytesSkipped; (Currently SkGifCodec does not do any skipping, but I have an in progress CL that could. Looking back at it, I call SkStream::move() instead, but it could easily call read(nullptr, len).) https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:99: bool isAtEnd() const override { return m_isDone; } What does isDone mean? The fact that it is passed in with the SegmentReader makes it sound like it tells us whether allDataIsReceived. But isAtEnd() means we've actually read all the way to the end, and the next read will fail because there's no more data. (As it happens, SkCodec never checks isAtEnd.) https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:102: std::unique_ptr<SegmentReader> m_reader; SegmentReader is a ref counted class. Don't you want to make this a RefPtr?
https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:45: m_segmentStream(new SegmentStream{}) {} On 2016/12/13 16:57:36, scroggo_chromium wrote: > Why not make m_segmentStream a full member? > > Oh wait, m_codec is going to want to take ownership of it... > > I think you need to do the following: > > - Store a raw pointer to m_segmentStream > - In the destructor, delete m_segmentStream if !m_codec > > A related issue - SkCodec::NewFromStream will delete m_segmentStream on failure, > so you'll need to recreate it if that call fails. Your idea about a raw pointer makes sense to me. If SkCodec::NewFromStream deletes the memory on failure, we lose any data passed into it previously. This means the only safe way for me to create SkCodec would be to first make a deep copy of my SegmentStream and its SegmentReader. Would it be better for SkCodec::NewFromStream to not delete the memory on error? It seems much easier from my end. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:88: return (frameInfoes.size() >= 1 || m_segmentStream->isAtEnd); On 2016/12/13 16:57:36, scroggo_chromium wrote: > frameInfos* > > m_segmentStream->isAtEnd()* - but doesn't the GIFImageDecoder know if all the > data was received? isAllDataReceived()? > > And I think you want frameInfos.size() > 1, not >=. > means we've seen the > second frame, so the first frame must be complete. > > Back to our discussion on those CLs - we'll be wrong between receiving the start > and the end of the second frame; during that time we'll incorrectly report that > frame 0 is incomplete. Done. Yeah, we'll be wrong until the end of the second frame. If we land the CL where SkCodec exposes whether the frame is complete or not, I can report this without error (the commented out section below). If we instead land the CL where SkCodec always reports that frame 0 is complete, I don't know a way to not report incorrectly. Do you have an idea? https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:132: if (m_frameBufferCache.size() < index) { On 2016/12/13 16:57:36, scroggo_chromium wrote: > I think you want <=, here and elsewhere. (And the comment below may not be > necessary. Maybe it's just obvious to me, and not to someone else.) > > Should this ever happen in this case, though? In the old code we do not make > this check. I think this only gets called when m_frameBufferCache is big enough. Done. I think you are right that it is not needed. I guess I was being paranoid. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:138: std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); On 2016/12/13 16:57:36, scroggo_chromium wrote: > Since we call this so often, I think we should consider caching it. (One option > I like is to make SkCodec store it and return a reference. It knows whether it > needed to change anything, so when it parses it can only fill in the new ones. > Then you don't have to worry about it here.) I like the idea of SkCodec having a method that returns a const reference. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:143: // TODO: SkCodec doesn't expose the disposal method. On 2016/12/13 16:57:36, scroggo_chromium wrote: > These might just be awkward during the transition. I'm not convinced you need to > know the disposal method or original frame rect, since that will be handled > internally by SkCodec. But maybe I'm missing something? I agree. I'm hoping this stuff isn't handled by the level above which calls into the decoder. My fear is that since these are here maybe they are. I suppose even if it is, I can present each frame as being completely independent and taking the whole area and having whatever disposal method. Then I can let SkCodec handle the reality of it. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:163: // imageInfo's color type may be influenced by the untrusted image file. On 2016/12/13 16:57:36, scroggo_chromium wrote: > We report index8 if the GIF can support it (for the first frame). A GIF frame > only uses 256 colors directly, but if it blends with a prior frame, it may use > more. Since we only consider the first frame to potentially use index8, we do > not need to worry about prior frames, but we do need to worry about > transparency. If the frame is subset, and it does *not* have a transparent > pixel, we don't support index8, since we don't have anything to fill the > background with. > > Here's the Skia code: > https://skia.googlesource.com/skia/+/36aa176d0/third_party/gif/SkGifImageRead... > > (We could take this even further - if there is no transparent pixel, but the > color table is less than 256 colors, we could add an extra color to be > transparent. But we have not pursued that.) > > All of this is just to say that it's fine for the codec to report index8. As a > client, you just need to request the color type that you want. In this case, you > want kN32: > > SkImageInfo imageInfo = m_codec->getInfo()->makeColorType(kN32_SkColorType); Done. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:174: // Said another way, is there a way to know if we already populated this On 2016/12/13 16:57:36, scroggo_chromium wrote: > Check frame.getStatus()? Hrmmm I seem to be able to check for empty, incomplete, and complete. I can imagine a frame has been completely downloaded but not decoded. Or we decoded it the first pass, purged the cache, and need to decode it again even though it is complete. So maybe I should be checking for empty? But seeing as how this deals with completeness, it feels less like a check of the cache. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:178: if (requiredPreviousFrameIndex == WTF::kNotFound) { On 2016/12/13 16:57:36, scroggo_chromium wrote: > I think you mean != WTF::kNotFound? Done. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: frame.copyBitmapData(requiredPreviousFrame); On 2016/12/13 16:57:36, scroggo_chromium wrote: > Doesn't this happen in initFrameBuffer? > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... Done. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: SkCodec::Result getPixelsResult = On 2016/12/13 16:57:36, scroggo_chromium wrote: > You want to switch from getPixels() to startIncrementalDecode() and > incrementalDecode(). Those let you continue decoding where you left off. That makes sense. I'll have to research the behavior of Blink calling into the ImageDecoder to see if it attempts an incremental code on the first frame but doesn't attempt any decodes on other frames until they are complete. Reading the comments in SkCodec I had a question: https://cs.chromium.org/chromium/src/third_party/skia/include/codec/SkCodec.h... Does it only decode complete rows? Can't have a partial row? If it can decode a partial row then I think I need to initialize the SkBitmap I'm using with either the background color or full alpha or something. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:188: if (getPixelResult != kSuccess) { On 2016/12/13 16:57:36, scroggo_chromium wrote: > kIncomplete is also okay, so long as it's the latest frame. Done. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:29: #include "codec/SkCodec.h" On 2016/12/13 16:57:37, scroggo_chromium wrote: > I thought these were typically included as > > #include "third_party/skia/include/codec/SkCodec.h" Done. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:68: class SegmentStream : public SkStream { On 2016/12/13 16:57:37, scroggo_chromium wrote: > Add a constructor to initialize m_isDone and m_position? Done. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:76: m_position += len; On 2016/12/13 16:57:37, scroggo_chromium wrote: > I think this should only advance to the end of the reader. e.g. > > const size_t bytesSkipped = std::min(len, m_reader->size()); > m_position += bytesSkipped; > return bytesSkipped; > > (Currently SkGifCodec does not do any skipping, but I have an in progress CL > that could. Looking back at it, I call SkStream::move() instead, but it could > easily call read(nullptr, len).) I agree. In fact, I should probably clamp len to always be <= m_reader->size() - m_position (not just when skipping). I can also use this to set m_isDone, which should reflect the read hitting the end (not just the end of the buffer coming in). https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:90: } I'm not sure we need to check if getSomeData() returned more than we asked for. It seems like the contract is if we ask for more than is available it returns 0. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:99: bool isAtEnd() const override { return m_isDone; } On 2016/12/13 16:57:37, scroggo_chromium wrote: > What does isDone mean? The fact that it is passed in with the SegmentReader > makes it sound like it tells us whether allDataIsReceived. But isAtEnd() means > we've actually read all the way to the end, and the next read will fail because > there's no more data. > > (As it happens, SkCodec never checks isAtEnd.) Thank you for catching this. I've fixed it to correctly reflect whether the data has all been read. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:102: std::unique_ptr<SegmentReader> m_reader; On 2016/12/13 16:57:37, scroggo_chromium wrote: > SegmentReader is a ref counted class. Don't you want to make this a RefPtr? Done.
cblume@chromium.org changed reviewers: + senorblanco@chromium.org
+senorblanco@ to review BUILD.gn I need to add Skia as a dependency in BUILD.gn. I am getting undefined references: ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:66: error: undefined reference to 'SkCodec::NewFromStream(SkStream*, SkPngChunkReader*)' ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:190: error: undefined reference to 'SkCodec::getPixels(SkImageInfo const&, void*, unsigned long, SkCodec::Options const*, unsigned int*, int*)' I am not familiar enough with gn files. It seems to already have Skia as a dependency.
https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:45: m_segmentStream(new SegmentStream{}) {} On 2016/12/14 09:16:56, cblume wrote: > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > Why not make m_segmentStream a full member? > > > > Oh wait, m_codec is going to want to take ownership of it... > > > > I think you need to do the following: > > > > - Store a raw pointer to m_segmentStream > > - In the destructor, delete m_segmentStream if !m_codec > > > > A related issue - SkCodec::NewFromStream will delete m_segmentStream on > failure, > > so you'll need to recreate it if that call fails. > > Your idea about a raw pointer makes sense to me. > > If SkCodec::NewFromStream deletes the memory on failure, we lose any data passed > into it previously. NewFromStream deletes the object - not the memory. The memory still lives inside the SegmentReader. > This means the only safe way for me to create SkCodec would > be to first make a deep copy of my SegmentStream and its SegmentReader. I do not see how that is necessary. The SegmentReader will stick around even if the SegmentStream is deleted. I think you just need to do the following: if (!m_segmentStream) { m_segmentStream = new SegmentStream(); } m_segmentStream->setReader(data, isAllDataReceived()); if (!m_codec) { m_codec.reset(SkCodec::NewFromStream(m_segmentStream)); if (!m_codec) { // NewFromStream deleted the stream m_segmentStream = nullptr; } } > > Would it be better for SkCodec::NewFromStream to not delete the memory on error? > It seems much easier from my end. Here's some history: SkStream used to be ref-counted. Many classes in Skia were ref-counted, but we have been removing it from classes where we don't think it makes sense. In the case of SkStream, which is a stateful object, if multiple objects have a ref, and are presumably reading from it, each object's reads are going to interfere with the other. So we removed ref-counting from SkStream. With SkCodec (and SkImageGenerator before it, which was a model for SkCodec), we decided to delete the SkStream on failure because on success, the SkCodec is expected to take ownership of the SkStream. Also "taking ownership" in the case of failure prevents the client from having to do the following: SkStream* stream = new SkStream(...); std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream)); if (!codec) { delete stream; } Prior to integration with Chromium, we were not considering a stream that might need to also receive more data, so there was no need to have a second owner. But you're correct, this does make things trickier on your end. I think the ultimate solution is to use a different abstraction of the input. I have not figured out exactly what that looks like though... https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:88: return (frameInfoes.size() >= 1 || m_segmentStream->isAtEnd); On 2016/12/14 09:16:56, cblume wrote: > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > frameInfos* > > > > m_segmentStream->isAtEnd()* - but doesn't the GIFImageDecoder know if all the > > data was received? isAllDataReceived()? > > > > And I think you want frameInfos.size() > 1, not >=. > means we've seen the > > second frame, so the first frame must be complete. > > > > Back to our discussion on those CLs - we'll be wrong between receiving the > start > > and the end of the second frame; during that time we'll incorrectly report > that > > frame 0 is incomplete. > > Done. > > Yeah, we'll be wrong until the end of the second frame. > If we land the CL where SkCodec exposes whether the frame is complete or not, I > can report this without error (the commented out section below). > If we instead land the CL where SkCodec always reports that frame 0 is complete, > I don't know a way to not report incorrectly. Do you have an idea? My hope was that we didn't actually need to know whether the first frame is complete - at least without decoding. I brought up the reasons we want to know whether the frame is complete here: https://skia-review.googlesource.com/c/5635/#message-78219d862632f346d7a9ab90... In most of those cases, I think we could actually go based on whether the frame is decoded (rather than whether the frame was received), but that would require some refactoring. I think https://skia-review.googlesource.com/c/5703/ is the way to go here. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:174: // Said another way, is there a way to know if we already populated this On 2016/12/14 09:16:56, cblume wrote: > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > Check frame.getStatus()? > > Hrmmm I seem to be able to check for empty, incomplete, and complete. > I can imagine a frame has been completely downloaded but not decoded. Or we > decoded it the first pass, purged the cache, and need to decode it again even > though it is complete. > > So maybe I should be checking for empty? If i was purged, m_frameBufferCache[i] should be FrameEmpty. If we've started, it should be FramePartial. (We currently set it to FramePartial in initFrameBuffer [1], but you probably want to skip that method entirely.) [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > But seeing as how this deals with completeness, it feels less like a check of > the cache. I don't understand. If the cache (m_frameBufferCache[i]?) is FrameComplete, isn't that how you know that it is complete? https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: frame.copyBitmapData(requiredPreviousFrame); On 2016/12/14 09:16:56, cblume wrote: > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > Doesn't this happen in initFrameBuffer? > > > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > Done. D'oh, and then I told you not to call that method... https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: SkCodec::Result getPixelsResult = On 2016/12/14 09:16:56, cblume wrote: > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > You want to switch from getPixels() to startIncrementalDecode() and > > incrementalDecode(). Those let you continue decoding where you left off. > > That makes sense. > > I'll have to research the behavior of Blink calling into the ImageDecoder to see > if it attempts an incremental code on the first frame but doesn't attempt any > decodes on other frames until they are complete. As I understand it, Blink waits until a frame after the first is complete [1]. But it is fine to call SkCodec::incrementalDecode() on a complete image. [1] https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/So... > > Reading the comments in SkCodec I had a question: > https://cs.chromium.org/chromium/src/third_party/skia/include/codec/SkCodec.h... > Does it only decode complete rows? Can't have a partial row? It only decodes complete rows. > > If it can decode a partial row then I think I need to initialize the SkBitmap > I'm using with either the background color or full alpha or something. Chrome already fills the bitmap with transparent - again currently in initFrameBuffer, though I think you'll want to handle it yourself. That said, the out parameter from incrementalDecode() tells you how many rows were decoded, so you could wait until you call the method, and only fill the remaining rows if the return value is kIncompleteInput (and it's only necessary on the first pass). https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:90: } On 2016/12/14 09:16:57, cblume wrote: > I'm not sure we need to check if getSomeData() returned more than we asked for. > It seems like the contract is if we ask for more than is available it returns 0. getSomeData returning more than we asked for is the opposite of asking for more than is available. SegmentReader is based on SharedBuffer, and the SharedBuffer implementation forwards this call directly to its SharedBuffer. Both classes are designed to have blocks of data that can be peered into. So the size returned corresponds to how much data is contiguous starting from the position you asked for. Which made me look at your call to getSomeData - you want to pass m_position as the second parameter. Once you do that, you'll see that the SegmentReader does not even know how many bytes you wanted. So you definitely need to make this check. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:162: if (m_frameBufferCache.size() < index) { <= https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:170: // Index8 has special behavior when getting the pixels, which we do not yet This comment can be removed. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:79: m_position = 0; Why does this reset the position? You'll be receiving a new SegmentReader which has the full data from the start, so you want to keep reading from the same position. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:80: m_hasReadAllContents = false; This assumes you will never be given a SegmentReader of the same size you've seen before. That may be true, but I don't think it's guaranteed. ImageFrameGenerator::decode pulls an ImageDecoder out of its cache and sets the data. I could imagine that we are re-decoding an earlier frame after receiving all the data, in which case we might already be at the end. [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:91: intptr_t destAsInt = reinterpret_cast<intptr_t>(dst); I haven't seen this pattern before, and do not see it anywhere in chromium. (The only instance I see of "intptr_t" is here [1].) According to [2], intptr_t may not be defined anyway. [1] https://cs.chromium.org/chromium/src/sandbox/win/src/internal_types.h?dr=CSs&... [2] http://www.cplusplus.com/reference/cstdint/ https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:94: size_t bytesRead = m_reader->getSomeData(segment, len); This came up while I was reviewing the first patch set, but I wanted to note it here - you want to use m_position here.
cblume@chromium.org changed reviewers: + fmalita@chromium.org - senorblanco@chromium.org
-Stephen +Florin
On 2016/12/14 17:11:09, cblume wrote: > +senorblanco@ to review BUILD.gn > > I need to add Skia as a dependency in BUILD.gn. I am getting undefined > references: > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:66: > error: undefined reference to 'SkCodec::NewFromStream(SkStream*, > SkPngChunkReader*)' > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:190: > error: undefined reference to 'SkCodec::getPixels(SkImageInfo const&, void*, > unsigned long, SkCodec::Options const*, unsigned int*, int*)' It looks like GIFImageDecoder.cpp needs to #include "third_party/skia/include/codec/SkCodec.h", no? > I am not familiar enough with gn files. It seems to already have Skia as a > dependency.
On 2016/12/14 18:03:55, Stephen White wrote: > On 2016/12/14 17:11:09, cblume wrote: > > +senorblanco@ to review BUILD.gn > > > > I need to add Skia as a dependency in BUILD.gn. I am getting undefined > > references: > > > > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:66: > > error: undefined reference to 'SkCodec::NewFromStream(SkStream*, > > SkPngChunkReader*)' > > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:190: > > error: undefined reference to 'SkCodec::getPixels(SkImageInfo const&, void*, > > unsigned long, SkCodec::Options const*, unsigned int*, int*)' > > It looks like GIFImageDecoder.cpp needs to #include > "third_party/skia/include/codec/SkCodec.h", no? Oh wait, it's in GIFImageDecoder.h. Not sure what's going on, then. > > I am not familiar enough with gn files. It seems to already have Skia as a > > dependency.
On 2016/12/14 17:11:09, cblume wrote: > +senorblanco@ to review BUILD.gn > > I need to add Skia as a dependency in BUILD.gn. I am getting undefined > references: > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:66: > error: undefined reference to 'SkCodec::NewFromStream(SkStream*, > SkPngChunkReader*)' > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:190: > error: undefined reference to 'SkCodec::getPixels(SkImageInfo const&, void*, > unsigned long, SkCodec::Options const*, unsigned int*, int*)' > > I am not familiar enough with gn files. It seems to already have Skia as a > dependency. I think SkCodec needs to be SK_API-annotated to be used externally in a component build.
BUILD.gn lgtm
On 2016/12/14 18:16:15, f(malita) wrote: > On 2016/12/14 17:11:09, cblume wrote: > > +senorblanco@ to review BUILD.gn > > > > I need to add Skia as a dependency in BUILD.gn. I am getting undefined > > references: > > > > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:66: > > error: undefined reference to 'SkCodec::NewFromStream(SkStream*, > > SkPngChunkReader*)' > > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:190: > > error: undefined reference to 'SkCodec::getPixels(SkImageInfo const&, void*, > > unsigned long, SkCodec::Options const*, unsigned int*, int*)' > > > > I am not familiar enough with gn files. It seems to already have Skia as a > > dependency. > > I think SkCodec needs to be SK_API-annotated to be used externally in a > component build. Not sure if it's related, but there's also nothing in src/skia/BUILD.gn under "skia_config" for include/codec, while there is for include/core, include/effects, etc.
On 2016/12/14 18:19:48, Stephen White wrote: > On 2016/12/14 18:16:15, f(malita) wrote: > > On 2016/12/14 17:11:09, cblume wrote: > > > +senorblanco@ to review BUILD.gn > > > > > > I need to add Skia as a dependency in BUILD.gn. I am getting undefined > > > references: > > > > > > > > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:66: > > > error: undefined reference to 'SkCodec::NewFromStream(SkStream*, > > > SkPngChunkReader*)' > > > > > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:190: > > > error: undefined reference to 'SkCodec::getPixels(SkImageInfo const&, void*, > > > unsigned long, SkCodec::Options const*, unsigned int*, int*)' > > > > > > I am not familiar enough with gn files. It seems to already have Skia as a > > > dependency. > > > > I think SkCodec needs to be SK_API-annotated to be used externally in a > > component build. > > Not sure if it's related, but there's also nothing in src/skia/BUILD.gn under > "skia_config" for include/codec, while there is for include/core, > include/effects, > etc. SkCodec currently isn't included at all. undefined reference is a linking error - we're not compiling SkCodec.cpp
On 2016/12/14 18:31:41, scroggo_chromium wrote: > On 2016/12/14 18:19:48, Stephen White wrote: > > On 2016/12/14 18:16:15, f(malita) wrote: > > > On 2016/12/14 17:11:09, cblume wrote: > > > > +senorblanco@ to review BUILD.gn > > > > > > > > I need to add Skia as a dependency in BUILD.gn. I am getting undefined > > > > references: > > > > > > > > > > > > > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:66: > > > > error: undefined reference to 'SkCodec::NewFromStream(SkStream*, > > > > SkPngChunkReader*)' > > > > > > > > > > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:190: > > > > error: undefined reference to 'SkCodec::getPixels(SkImageInfo const&, > void*, > > > > unsigned long, SkCodec::Options const*, unsigned int*, int*)' > > > > > > > > I am not familiar enough with gn files. It seems to already have Skia as a > > > > dependency. > > > > > > I think SkCodec needs to be SK_API-annotated to be used externally in a > > > component build. > > > > Not sure if it's related, but there's also nothing in src/skia/BUILD.gn under > > "skia_config" for include/codec, while there is for include/core, > > include/effects, > > etc. > > SkCodec currently isn't included at all. undefined reference is a linking error > - we're not compiling SkCodec.cpp Sounds like we should add a gn/codec.gni to Skia, and define skia_codec_sources similar to the other components? But maybe in the meantime Chris can just copy the sources list to skia/BUILD.gn.
https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:45: m_segmentStream(new SegmentStream{}) {} On 2016/12/14 17:49:23, scroggo_chromium wrote: > On 2016/12/14 09:16:56, cblume wrote: > > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > > Why not make m_segmentStream a full member? > > > > > > Oh wait, m_codec is going to want to take ownership of it... > > > > > > I think you need to do the following: > > > > > > - Store a raw pointer to m_segmentStream > > > - In the destructor, delete m_segmentStream if !m_codec > > > > > > A related issue - SkCodec::NewFromStream will delete m_segmentStream on > > failure, > > > so you'll need to recreate it if that call fails. > > > > Your idea about a raw pointer makes sense to me. > > > > If SkCodec::NewFromStream deletes the memory on failure, we lose any data > passed > > into it previously. > > NewFromStream deletes the object - not the memory. The memory still lives inside > the SegmentReader. > > > This means the only safe way for me to create SkCodec would > > be to first make a deep copy of my SegmentStream and its SegmentReader. > > I do not see how that is necessary. The SegmentReader will stick around even if > the SegmentStream is deleted. I think you just need to do the following: > > if (!m_segmentStream) { > m_segmentStream = new SegmentStream(); > } > m_segmentStream->setReader(data, isAllDataReceived()); > if (!m_codec) { > m_codec.reset(SkCodec::NewFromStream(m_segmentStream)); > if (!m_codec) { > // NewFromStream deleted the stream > m_segmentStream = nullptr; > } > } > > > > > Would it be better for SkCodec::NewFromStream to not delete the memory on > error? > > It seems much easier from my end. > > Here's some history: > > SkStream used to be ref-counted. Many classes in Skia were ref-counted, but we > have been removing it from classes where we don't think it makes sense. In the > case of SkStream, which is a stateful object, if multiple objects have a ref, > and are presumably reading from it, each object's reads are going to interfere > with the other. So we removed ref-counting from SkStream. > > With SkCodec (and SkImageGenerator before it, which was a model for SkCodec), we > decided to delete the SkStream on failure because on success, the SkCodec is > expected to take ownership of the SkStream. Also "taking ownership" in the case > of failure prevents the client from having to do the following: > > SkStream* stream = new SkStream(...); > std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream)); > if (!codec) { > delete stream; > } > > Prior to integration with Chromium, we were not considering a stream that might > need to also receive more data, so there was no need to have a second owner. But > you're correct, this does make things trickier on your end. > > I think the ultimate solution is to use a different abstraction of the input. I > have not figured out exactly what that looks like though... Done. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:88: return (frameInfoes.size() >= 1 || m_segmentStream->isAtEnd); On 2016/12/14 17:49:23, scroggo_chromium wrote: > On 2016/12/14 09:16:56, cblume wrote: > > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > > frameInfos* > > > > > > m_segmentStream->isAtEnd()* - but doesn't the GIFImageDecoder know if all > the > > > data was received? isAllDataReceived()? > > > > > > And I think you want frameInfos.size() > 1, not >=. > means we've seen the > > > second frame, so the first frame must be complete. > > > > > > Back to our discussion on those CLs - we'll be wrong between receiving the > > start > > > and the end of the second frame; during that time we'll incorrectly report > > that > > > frame 0 is incomplete. > > > > Done. > > > > Yeah, we'll be wrong until the end of the second frame. > > If we land the CL where SkCodec exposes whether the frame is complete or not, > I > > can report this without error (the commented out section below). > > If we instead land the CL where SkCodec always reports that frame 0 is > complete, > > I don't know a way to not report incorrectly. Do you have an idea? > > My hope was that we didn't actually need to know whether the first frame is > complete - at least without decoding. I brought up the reasons we want to know > whether the frame is complete here: > > https://skia-review.googlesource.com/c/5635/#message-78219d862632f346d7a9ab90... > > In most of those cases, I think we could actually go based on whether the frame > is decoded (rather than whether the frame was received), but that would require > some refactoring. I think https://skia-review.googlesource.com/c/5703/ is the > way to go here. Done. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:174: // Said another way, is there a way to know if we already populated this On 2016/12/14 17:49:23, scroggo_chromium wrote: > On 2016/12/14 09:16:56, cblume wrote: > > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > > Check frame.getStatus()? > > > > Hrmmm I seem to be able to check for empty, incomplete, and complete. > > I can imagine a frame has been completely downloaded but not decoded. Or we > > decoded it the first pass, purged the cache, and need to decode it again even > > though it is complete. > > > > So maybe I should be checking for empty? > > If i was purged, m_frameBufferCache[i] should be FrameEmpty. If we've started, > it should be FramePartial. (We currently set it to FramePartial in > initFrameBuffer [1], but you probably want to skip that method entirely.) > > [1] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > > But seeing as how this deals with completeness, it feels less like a check of > > the cache. > > I don't understand. If the cache (m_frameBufferCache[i]?) is FrameComplete, > isn't that how you know that it is complete? > I guess we have two types of "complete": completely received and completely decoded. For example, on the second pass through an animation the image is completely downloaded but if we purged the cache then it is not completely decoded. I guess I was just mixing the two up since we also have functions like frameCompleteAtIndex(). You are right that this "complete" relates to the decode and it is what I need. Maybe later I will rename those things to be more clear. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: frame.copyBitmapData(requiredPreviousFrame); On 2016/12/14 17:49:23, scroggo_chromium wrote: > On 2016/12/14 09:16:56, cblume wrote: > > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > > Doesn't this happen in initFrameBuffer? > > > > > > > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > Done. > > D'oh, and then I told you not to call that method... I took another look again at initFrameBuffer. It copies the bitmap data when we reply on the previous frame, fills with BG color if that is the disposal method, sets the rect, and sets the frame to partially complete. Skia handles the disposal method and rect business. I think all I need to do is copy the previous buffer (put copyBitmapData back in) and add partial/complete flags. Another thing initFrameBuffer() does is use some default color space for frames with no prior dependency. I hadn't been paying attention to color space at all. Maybe I should add something for that? https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: SkCodec::Result getPixelsResult = On 2016/12/14 17:49:23, scroggo_chromium wrote: > On 2016/12/14 09:16:56, cblume wrote: > > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > > You want to switch from getPixels() to startIncrementalDecode() and > > > incrementalDecode(). Those let you continue decoding where you left off. > > > > That makes sense. > > > > I'll have to research the behavior of Blink calling into the ImageDecoder to > see > > if it attempts an incremental code on the first frame but doesn't attempt any > > decodes on other frames until they are complete. > > As I understand it, Blink waits until a frame after the first is complete [1]. > But it is fine to call SkCodec::incrementalDecode() on a complete image. > > [1] > https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/So... > > > > > Reading the comments in SkCodec I had a question: > > > https://cs.chromium.org/chromium/src/third_party/skia/include/codec/SkCodec.h... > > Does it only decode complete rows? Can't have a partial row? > > It only decodes complete rows. > > > > > If it can decode a partial row then I think I need to initialize the SkBitmap > > I'm using with either the background color or full alpha or something. > > Chrome already fills the bitmap with transparent - again currently in > initFrameBuffer, though I think you'll want to handle it yourself. That said, > the out parameter from incrementalDecode() tells you how many rows were decoded, > so you could wait until you call the method, and only fill the remaining rows if > the return value is kIncompleteInput (and it's only necessary on the first > pass). > Done. https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:90: } On 2016/12/14 17:49:23, scroggo_chromium wrote: > On 2016/12/14 09:16:57, cblume wrote: > > I'm not sure we need to check if getSomeData() returned more than we asked > for. > > It seems like the contract is if we ask for more than is available it returns > 0. > > getSomeData returning more than we asked for is the opposite of asking for more > than is available. SegmentReader is based on SharedBuffer, and the SharedBuffer > implementation forwards this call directly to its SharedBuffer. Both classes are > designed to have blocks of data that can be peered into. So the size returned > corresponds to how much data is contiguous starting from the position you asked > for. > > Which made me look at your call to getSomeData - you want to pass m_position as > the second parameter. Once you do that, you'll see that the SegmentReader does > not even know how many bytes you wanted. So you definitely need to make this > check. Done. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:162: if (m_frameBufferCache.size() < index) { On 2016/12/14 17:49:23, scroggo_chromium wrote: > <= Done. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:170: // Index8 has special behavior when getting the pixels, which we do not yet On 2016/12/14 17:49:23, scroggo_chromium wrote: > This comment can be removed. Done. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:79: m_position = 0; On 2016/12/14 17:49:23, scroggo_chromium wrote: > Why does this reset the position? You'll be receiving a new SegmentReader which > has the full data from the start, so you want to keep reading from the same > position. Yeah, that was a mistake. I removed it. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:80: m_hasReadAllContents = false; On 2016/12/14 17:49:23, scroggo_chromium wrote: > This assumes you will never be given a SegmentReader of the same size you've > seen before. That may be true, but I don't think it's guaranteed. > ImageFrameGenerator::decode pulls an ImageDecoder out of its cache and sets the > data. I could imagine that we are re-decoding an earlier frame after receiving > all the data, in which case we might already be at the end. > > [1] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... We might be thinking of different things. Perhaps I have the wrong mental model here. I intended to treat this differently from isAllDataReceived(). I was intending this to be used to reflect how much of the received data has actually been read (decoded). In that case, would a new image decoder with reused data still need to start from the beginning while decoding? I imagine so. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:91: intptr_t destAsInt = reinterpret_cast<intptr_t>(dst); On 2016/12/14 17:49:23, scroggo_chromium wrote: > I haven't seen this pattern before, and do not see it anywhere in chromium. (The > only instance I see of "intptr_t" is here [1].) > > According to [2], intptr_t may not be defined anyway. > > [1] > https://cs.chromium.org/chromium/src/sandbox/win/src/internal_types.h?dr=CSs&... > [2] http://www.cplusplus.com/reference/cstdint/ Hrmmm you are right, it is optional. We had used the unsigned version before: https://cs.chromium.org/chromium/src/third_party/skia/src/image/SkImage_Gpu.c... It is a way to avoid pointer aliasing and being able to do math on the pointer. But I can use char* instead. C/C++ have special rules that char* does not alias and I can do pointer arithmetic on it. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:94: size_t bytesRead = m_reader->getSomeData(segment, len); On 2016/12/14 17:49:23, scroggo_chromium wrote: > This came up while I was reviewing the first patch set, but I wanted to note it > here - you want to use m_position here. Done.
https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: frame.copyBitmapData(requiredPreviousFrame); On 2016/12/16 02:57:10, cblume wrote: > On 2016/12/14 17:49:23, scroggo_chromium wrote: > > On 2016/12/14 09:16:56, cblume wrote: > > > On 2016/12/13 16:57:36, scroggo_chromium wrote: > > > > Doesn't this happen in initFrameBuffer? > > > > > > > > > > > > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > > > Done. > > > > D'oh, and then I told you not to call that method... > > I took another look again at initFrameBuffer. > It copies the bitmap data when we reply on the previous frame, fills with BG > color if that is the disposal method, sets the rect, and sets the frame to > partially complete. > > Skia handles the disposal method and rect business. I think all I need to do is > copy the previous buffer (put copyBitmapData back in) and add partial/complete > flags. > > Another thing initFrameBuffer() does is use some default color space for frames > with no prior dependency. I hadn't been paying attention to color space at all. > Maybe I should add something for that? I'd sync up with msarett@. https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:80: m_hasReadAllContents = false; On 2016/12/16 02:57:10, cblume wrote: > On 2016/12/14 17:49:23, scroggo_chromium wrote: > > This assumes you will never be given a SegmentReader of the same size you've > > seen before. That may be true, but I don't think it's guaranteed. > > ImageFrameGenerator::decode pulls an ImageDecoder out of its cache and sets > the > > data. I could imagine that we are re-decoding an earlier frame after receiving > > all the data, in which case we might already be at the end. > > > > [1] > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > > We might be thinking of different things. Perhaps I have the wrong mental model > here. > > I intended to treat this differently from isAllDataReceived(). > I was intending this to be used to reflect how much of the received data has > actually been read (decoded). > > In that case, would a new image decoder with reused data still need to start > from the beginning while decoding? I imagine so. Yes, but that would be determined by m_position, which should be zero in that case. I think rather than setting this to false you should check: m_hasReadAllContents = reader->size() == m_position https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/BUILD.gn:355: "WebFrameScheduler.h", nit: I know you didn't change this line, but you rebased and made changes in the same upload, which makes it harder for me to see the changes between revisions. If you have to rebase, please try to do an upload that just shows the rebase, so I can focus on the actual changes that you made. It's even better if you can avoid rebasing when you can, which saves work for both of us. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:53: // Add the segment to our stream I find this comment misleading. Our stream just wraps the SegmentReader; this doesn't add individual segments to it. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:98: if (frameInfos.size() < index) { <= https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: options.fHasPriorFrame = true; This isn't necessarily true. copyBitmapData returns a boolean. You need to check whether it (or takeBitmapDataIfWritable, which should be checked first) succeeds. If so, you have the prior frame. If not, this frame will still be empty. You also might want to decode prior frames manually. If you do not provide the prior frame, SkCodec will decode it first, but will not store it for later. If you want to cache a frame that index depends on, you need to decode it yourself. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: if (startIncrementalDecodeResult == SkCodec::kSuccess) { nit: This empty block looks funny to me. Why not make this a switch statement, or convert the second else to } else if (result != success) { ? https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: // call startIncrementalDecode again? Yes. I only recently (https://skia-review.googlesource.com/5758) made a change where it would ever return incomplete, and I can see how it is a little different from returning incomplete from incrementalDecode(). The intent is to say "we could not start, because there is not enough data", so yes, another call to start is necessary. So the line below should probably leave the frame FrameEmpty. In the case of the first frame, we'll need to make sure it is initialized. For other frames, this should never happen, because we wait until they're fully received to decode... But this code doesn't know that, so we may need to take it into account. It might be awkward here, since the frame might not be truly empty (if its required frame was copied into it), but we use empty to signal to attempt to start again.
https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/40001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:80: m_hasReadAllContents = false; On 2016/12/16 15:03:48, scroggo_chromium wrote: > On 2016/12/16 02:57:10, cblume wrote: > > On 2016/12/14 17:49:23, scroggo_chromium wrote: > > > This assumes you will never be given a SegmentReader of the same size you've > > > seen before. That may be true, but I don't think it's guaranteed. > > > ImageFrameGenerator::decode pulls an ImageDecoder out of its cache and sets > > the > > > data. I could imagine that we are re-decoding an earlier frame after > receiving > > > all the data, in which case we might already be at the end. > > > > > > [1] > > > > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > > > > We might be thinking of different things. Perhaps I have the wrong mental > model > > here. > > > > I intended to treat this differently from isAllDataReceived(). > > I was intending this to be used to reflect how much of the received data has > > actually been read (decoded). > > > > In that case, would a new image decoder with reused data still need to start > > from the beginning while decoding? I imagine so. > > Yes, but that would be determined by m_position, which should be zero in that > case. I think rather than setting this to false you should check: > > m_hasReadAllContents = reader->size() == m_position Done. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/BUILD.gn:355: "WebFrameScheduler.h", On 2016/12/16 15:03:48, scroggo_chromium wrote: > nit: I know you didn't change this line, but you rebased and made changes in the > same upload, which makes it harder for me to see the changes between revisions. > If you have to rebase, please try to do an upload that just shows the rebase, so > I can focus on the actual changes that you made. > > It's even better if you can avoid rebasing when you can, which saves work for > both of us. Makes sense to rebase in a separate patch. I'm a little fearful to avoid rebasing because it would make landing the final patch harder. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:53: // Add the segment to our stream On 2016/12/16 15:03:48, scroggo_chromium wrote: > I find this comment misleading. Our stream just wraps the SegmentReader; this > doesn't add individual segments to it. You're right. Aside from being unclear, it also isn't useful. I'll remove it. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:98: if (frameInfos.size() < index) { On 2016/12/16 15:03:48, scroggo_chromium wrote: > <= Done. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: options.fHasPriorFrame = true; On 2016/12/16 15:03:48, scroggo_chromium wrote: > This isn't necessarily true. copyBitmapData returns a boolean. You need to check > whether it (or takeBitmapDataIfWritable, which should be checked first) > succeeds. If so, you have the prior frame. If not, this frame will still be > empty. > > You also might want to decode prior frames manually. If you do not provide the > prior frame, SkCodec will decode it first, but will not store it for later. If > you want to cache a frame that index depends on, you need to decode it yourself. I was thinking of not using takeBitmapDataIfWritable. My intention is to leave that frame preserved. I assume takeBitmapDataIfWritable will assume ownership of the bitmap itself (and thus not preserve the older frame). My reason for wanting to preserve the frame is I want to eventually have the frames live further up in Blink, which can manage the cache more wisely. So I'm trying to remove any what-stays-in-cache logic. Although, maybe that should happen at a later time and for now we can takeBitmapDataIfWritable. Because I am leaving prior frames in tact (and relying on the purging behavior for now), I believe we are keeping a cache of any frames we depend on manually. As far as checking that copyBitmapData succeeds, you are completely correct. I think in that case I may want to setFailed(); and return. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: if (startIncrementalDecodeResult == SkCodec::kSuccess) { On 2016/12/16 15:03:48, scroggo_chromium wrote: > nit: This empty block looks funny to me. Why not make this a switch statement, > or convert the second else to > > } else if (result != success) { > > ? Done. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: // call startIncrementalDecode again? On 2016/12/16 15:03:48, scroggo_chromium wrote: > Yes. I only recently (https://skia-review.googlesource.com/5758) made a change > where it would ever return incomplete, and I can see how it is a little > different from returning incomplete from incrementalDecode(). > > The intent is to say "we could not start, because there is not enough data", so > yes, another call to start is necessary. > > So the line below should probably leave the frame FrameEmpty. In the case of the > first frame, we'll need to make sure it is initialized. For other frames, this > should never happen, because we wait until they're fully received to decode... > But this code doesn't know that, so we may need to take it into account. It > might be awkward here, since the frame might not be truly empty (if its required > frame was copied into it), but we use empty to signal to attempt to start again. I was thinking that same thing (if we restart because it is left FrameEmpty then we re-copy the dependent frame data). I think what I might do instead is call this earlier, before copying the prior frame contents. If the start succeeds, prepare the buffer and begin. For that to work, I need to know that startIncrementalDecode won't actually do any decoding -- it will only get things setup, right?
+ccameron@ and @msarett for the color correction info (inside GIFImageDecoder.cpp's initializeNewFrame() )
https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: options.fHasPriorFrame = true; On 2016/12/16 17:43:41, cblume wrote: > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > This isn't necessarily true. copyBitmapData returns a boolean. You need to > check > > whether it (or takeBitmapDataIfWritable, which should be checked first) > > succeeds. If so, you have the prior frame. If not, this frame will still be > > empty. > > > > You also might want to decode prior frames manually. If you do not provide the > > prior frame, SkCodec will decode it first, but will not store it for later. If > > you want to cache a frame that index depends on, you need to decode it > yourself. > > I was thinking of not using takeBitmapDataIfWritable. > My intention is to leave that frame preserved. I assume takeBitmapDataIfWritable > will assume ownership of the bitmap itself (and thus not preserve the older > frame). That is correct. > > My reason for wanting to preserve the frame is I want to eventually have the > frames live further up in Blink, which can manage the cache more wisely. So I'm > trying to remove any what-stays-in-cache logic. > > Although, maybe that should happen at a later time and for now we can > takeBitmapDataIfWritable. But the net result for now is that you're leaving more in the cache. I think you should stick with the current behavior for now. > > > Because I am leaving prior frames in tact (and relying on the purging behavior > for now), I believe we are keeping a cache of any frames we depend on manually. > > > As far as checking that copyBitmapData succeeds, you are completely correct. I > think in that case I may want to setFailed(); and return. Agreed. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: // call startIncrementalDecode again? On 2016/12/16 17:43:41, cblume wrote: > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > Yes. I only recently (https://skia-review.googlesource.com/5758) made a change > > where it would ever return incomplete, and I can see how it is a little > > different from returning incomplete from incrementalDecode(). > > > > The intent is to say "we could not start, because there is not enough data", > so > > yes, another call to start is necessary. > > > > So the line below should probably leave the frame FrameEmpty. In the case of > the > > first frame, we'll need to make sure it is initialized. For other frames, this > > should never happen, because we wait until they're fully received to decode... > > But this code doesn't know that, so we may need to take it into account. It > > might be awkward here, since the frame might not be truly empty (if its > required > > frame was copied into it), but we use empty to signal to attempt to start > again. > > I was thinking that same thing (if we restart because it is left FrameEmpty then > we re-copy the dependent frame data). > > I think what I might do instead is call this earlier, before copying the prior > frame contents. If the start succeeds, prepare the buffer and begin. > > For that to work, I need to know that startIncrementalDecode won't actually do > any decoding -- it will only get things setup, right? It won't do any decoding, but it stores the options, including whether fHasPriorFrame. But I guess since we'll setFailed if the copy from the prior frame fails, we can just assume that fHasPriorFrame is true if the frame we have is FrameComplete.
https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: options.fHasPriorFrame = true; On 2016/12/16 18:45:45, scroggo_chromium wrote: > On 2016/12/16 17:43:41, cblume wrote: > > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > > This isn't necessarily true. copyBitmapData returns a boolean. You need to > > check > > > whether it (or takeBitmapDataIfWritable, which should be checked first) > > > succeeds. If so, you have the prior frame. If not, this frame will still be > > > empty. > > > > > > You also might want to decode prior frames manually. If you do not provide > the > > > prior frame, SkCodec will decode it first, but will not store it for later. > If > > > you want to cache a frame that index depends on, you need to decode it > > yourself. > > > > I was thinking of not using takeBitmapDataIfWritable. > > My intention is to leave that frame preserved. I assume > takeBitmapDataIfWritable > > will assume ownership of the bitmap itself (and thus not preserve the older > > frame). > > That is correct. > > > > > My reason for wanting to preserve the frame is I want to eventually have the > > frames live further up in Blink, which can manage the cache more wisely. So > I'm > > trying to remove any what-stays-in-cache logic. > > > > Although, maybe that should happen at a later time and for now we can > > takeBitmapDataIfWritable. > > But the net result for now is that you're leaving more in the cache. I think you > should stick with the current behavior for now. > > > > > > > Because I am leaving prior frames in tact (and relying on the purging behavior > > for now), I believe we are keeping a cache of any frames we depend on > manually. > > > > > > As far as checking that copyBitmapData succeeds, you are completely correct. I > > think in that case I may want to setFailed(); and return. > > Agreed. I copied what we were previously doing. However, it requires canReusePreviousFrameBuffer() to know the disposal method of the frame, which I do not know at this level. I *could* wait until we have looped through the image and then make sure no latter frames require this one. But that means the first pass cannot reuse buffers and the second pass requires building up an inverted dependency list. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: // call startIncrementalDecode again? On 2016/12/16 18:45:45, scroggo_chromium wrote: > On 2016/12/16 17:43:41, cblume wrote: > > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > > Yes. I only recently (https://skia-review.googlesource.com/5758) made a > change > > > where it would ever return incomplete, and I can see how it is a little > > > different from returning incomplete from incrementalDecode(). > > > > > > The intent is to say "we could not start, because there is not enough data", > > so > > > yes, another call to start is necessary. > > > > > > So the line below should probably leave the frame FrameEmpty. In the case of > > the > > > first frame, we'll need to make sure it is initialized. For other frames, > this > > > should never happen, because we wait until they're fully received to > decode... > > > But this code doesn't know that, so we may need to take it into account. It > > > might be awkward here, since the frame might not be truly empty (if its > > required > > > frame was copied into it), but we use empty to signal to attempt to start > > again. > > > > I was thinking that same thing (if we restart because it is left FrameEmpty > then > > we re-copy the dependent frame data). > > > > I think what I might do instead is call this earlier, before copying the prior > > frame contents. If the start succeeds, prepare the buffer and begin. > > > > For that to work, I need to know that startIncrementalDecode won't actually do > > any decoding -- it will only get things setup, right? > > It won't do any decoding, but it stores the options, including whether > fHasPriorFrame. But I guess since we'll setFailed if the copy from the prior > frame fails, we can just assume that fHasPriorFrame is true if the frame we have > is FrameComplete. I didn't understand your last sentence. I think I may have fixed this up. When startIncrementalDecode returns incomplete it leaves the frame as FrameEmpty. Then we need Blink to attempt to decode the same frame once it gets more data.
On 2016/12/16 18:24:45, cblume wrote: > +ccameron@ and @msarett for the color correction info (inside > GIFImageDecoder.cpp's initializeNewFrame() ) lgtm on tagging frames with color spaces
I'm trying a few changes to get Skia building with SkCodec.cpp and family included. So far, so good. A list of the Skia changes is here: https://skia-review.googlesource.com/c/6205/ However, now when I build Chrome I'm missing Skia's dependencies like libwebp for include files like webp/decode.h It looks like it wants to download those dependencies into external/ but I don't think that is being triggered in Chrome. I'll keep researching.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/160001/skia/BUILD.gn File skia/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/160001/skia/BUILD.gn#newcode232 skia/BUILD.gn:232: sources += skia_codec_sources I had put the required .cpp files inside a Skia build file, which defined skia_codec_sources. However, code review comments on that (seen here: https://skia-review.googlesource.com/c/6205/ ) made it clear that we should instead have the files be listed out in Chrome. https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:233: // again later. Ideally, we would only take (reuse) the bitmap on the last frame that references that particular required frame. But I only know that after making the first loop through the animation. I can build up this information, but I want to confirm this is the behavior we want. We cannot reuse a bitmap on the first animation loop. And on the second loop, 1.) we will have already allocated all the bitmaps, or 2.) we were purging as we went anyway (current behavior)
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: android_n5x_swarming_rel on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/android_n5x_...)
I'll look into the test failures when I get into the office. Right now I'm coding through SSH.
https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: options.fHasPriorFrame = true; On 2016/12/18 17:47:57, cblume wrote: > On 2016/12/16 18:45:45, scroggo_chromium wrote: > > On 2016/12/16 17:43:41, cblume wrote: > > > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > > > This isn't necessarily true. copyBitmapData returns a boolean. You need to > > > check > > > > whether it (or takeBitmapDataIfWritable, which should be checked first) > > > > succeeds. If so, you have the prior frame. If not, this frame will still > be > > > > empty. > > > > > > > > You also might want to decode prior frames manually. If you do not provide > > the > > > > prior frame, SkCodec will decode it first, but will not store it for > later. > > If > > > > you want to cache a frame that index depends on, you need to decode it > > > yourself. > > > > > > I was thinking of not using takeBitmapDataIfWritable. > > > My intention is to leave that frame preserved. I assume > > takeBitmapDataIfWritable > > > will assume ownership of the bitmap itself (and thus not preserve the older > > > frame). > > > > That is correct. > > > > > > > > My reason for wanting to preserve the frame is I want to eventually have the > > > frames live further up in Blink, which can manage the cache more wisely. So > > I'm > > > trying to remove any what-stays-in-cache logic. > > > > > > Although, maybe that should happen at a later time and for now we can > > > takeBitmapDataIfWritable. > > > > But the net result for now is that you're leaving more in the cache. I think > you > > should stick with the current behavior for now. > > > > > > > > > > > Because I am leaving prior frames in tact (and relying on the purging > behavior > > > for now), I believe we are keeping a cache of any frames we depend on > > manually. > > > > > > > > > As far as checking that copyBitmapData succeeds, you are completely correct. > I > > > think in that case I may want to setFailed(); and return. > > > > Agreed. > > I copied what we were previously doing. > However, it requires canReusePreviousFrameBuffer() to know the disposal method > of the frame, which I do not know at this level. > I *could* wait until we have looped through the image and then make sure no > latter frames require this one. Ah. Here's what I was thinking when I decided to leave the disposal method off of SkCodec::FrameInfo: The next frame's required frame tells you what you need to know. If it does not depend on this frame, no future frames will. No need to wait for a full loop, but it does mean we have to have received part of the next frame. Since Chromium will not attempt to decode frame i (where i > 0) until i is fully received, this leaves a gap between the end of i until the end of the header of (i + 1) where we do not know whether any frames depend on i. Not sure how common that is, but if it is a problem, that is an argument for making SkCodec provide that information. > But that means the first pass cannot reuse buffers and the second pass requires > building up an inverted dependency list. https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: // call startIncrementalDecode again? On 2016/12/18 17:47:57, cblume wrote: > On 2016/12/16 18:45:45, scroggo_chromium wrote: > > On 2016/12/16 17:43:41, cblume wrote: > > > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > > > Yes. I only recently (https://skia-review.googlesource.com/5758) made a > > change > > > > where it would ever return incomplete, and I can see how it is a little > > > > different from returning incomplete from incrementalDecode(). > > > > > > > > The intent is to say "we could not start, because there is not enough > data", > > > so > > > > yes, another call to start is necessary. > > > > > > > > So the line below should probably leave the frame FrameEmpty. In the case > of > > > the > > > > first frame, we'll need to make sure it is initialized. For other frames, > > this > > > > should never happen, because we wait until they're fully received to > > decode... > > > > But this code doesn't know that, so we may need to take it into account. > It > > > > might be awkward here, since the frame might not be truly empty (if its > > > required > > > > frame was copied into it), but we use empty to signal to attempt to start > > > again. > > > > > > I was thinking that same thing (if we restart because it is left FrameEmpty > > then > > > we re-copy the dependent frame data). > > > > > > I think what I might do instead is call this earlier, before copying the > prior > > > frame contents. If the start succeeds, prepare the buffer and begin. > > > > > > For that to work, I need to know that startIncrementalDecode won't actually > do > > > any decoding -- it will only get things setup, right? > > > > It won't do any decoding, but it stores the options, including whether > > fHasPriorFrame. But I guess since we'll setFailed if the copy from the prior > > frame fails, we can just assume that fHasPriorFrame is true if the frame we > have > > is FrameComplete. > > I didn't understand your last sentence. I think the code should look something like: if (/* requires prior frame */) { options.fHasPriorFrame = true; } fCodec->startIncrementalDecode(...); // Assuming startIncrementalDecode returned kSuccess... if (!prevBuffer.copyBitmapData()) { return setFailed(); // <--- } If not for the line that calls setFailed(), we would be in a weird state where we started the incremental decode, but lied about whether we put the prior frame's data in the buffer before decoding. But copyBitmapData won't fail in the general case (it could fail if we ran OOM, for example, but will not fail for picking the wrong color type, since we just use N32 always), and if it does we won't try to decode anyway. > > I think I may have fixed this up. When startIncrementalDecode returns incomplete > it leaves the frame as FrameEmpty. Then we need Blink to attempt to decode the > same frame once it gets more data. That's what I was thinking. https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:79: int repetitionCount = m_codec->getRepetitionCount(); It looks like you removed a line that says CHECK(m_codec); Is it no longer the case that we're guaranteed to have an m_codec? If not, we need to check for null. (Same for other cases) https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:122: IntSize frameSize = size(); Maybe a FIXME here that we need to verify that we either do not need this information, or SkCodec needs to give it to us? https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:184: setFailed(); This isn't quite right. Look at where these methods are currently called [1]. If we cannot takeBitmapDataData, we need to copy it. [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:233: // again later. On 2016/12/19 08:04:03, cblume wrote: > Ideally, we would only take (reuse) the bitmap on the last frame that references > that particular required frame. > But I only know that after making the first loop through the animation. > > I can build up this information, but I want to confirm this is the behavior we > want. > > We cannot reuse a bitmap on the first animation loop. And on the second loop, > 1.) we will have already allocated all the bitmaps, or > 2.) we were purging as we went anyway (current behavior) No, I do not think that's the behavior that we want. We can check the next frame's fRequiredFrame if it has been (partially) received. If it is a common/problematic case where the next frame has not been partially received, we should update SkCodec's API to give more info. That said, if we do not know whether there is another frame after this one (which is the case if we haven't read its header) we also do not know whether any frames depend on this one.
https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: options.fHasPriorFrame = true; On 2016/12/19 14:28:54, scroggo_chromium wrote: > On 2016/12/18 17:47:57, cblume wrote: > > On 2016/12/16 18:45:45, scroggo_chromium wrote: > > > On 2016/12/16 17:43:41, cblume wrote: > > > > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > > > > This isn't necessarily true. copyBitmapData returns a boolean. You need > to > > > > check > > > > > whether it (or takeBitmapDataIfWritable, which should be checked first) > > > > > succeeds. If so, you have the prior frame. If not, this frame will still > > be > > > > > empty. > > > > > > > > > > You also might want to decode prior frames manually. If you do not > provide > > > the > > > > > prior frame, SkCodec will decode it first, but will not store it for > > later. > > > If > > > > > you want to cache a frame that index depends on, you need to decode it > > > > yourself. > > > > > > > > I was thinking of not using takeBitmapDataIfWritable. > > > > My intention is to leave that frame preserved. I assume > > > takeBitmapDataIfWritable > > > > will assume ownership of the bitmap itself (and thus not preserve the > older > > > > frame). > > > > > > That is correct. > > > > > > > > > > > My reason for wanting to preserve the frame is I want to eventually have > the > > > > frames live further up in Blink, which can manage the cache more wisely. > So > > > I'm > > > > trying to remove any what-stays-in-cache logic. > > > > > > > > Although, maybe that should happen at a later time and for now we can > > > > takeBitmapDataIfWritable. > > > > > > But the net result for now is that you're leaving more in the cache. I think > > you > > > should stick with the current behavior for now. > > > > > > > > > > > > > > > Because I am leaving prior frames in tact (and relying on the purging > > behavior > > > > for now), I believe we are keeping a cache of any frames we depend on > > > manually. > > > > > > > > > > > > As far as checking that copyBitmapData succeeds, you are completely > correct. > > I > > > > think in that case I may want to setFailed(); and return. > > > > > > Agreed. > > > > I copied what we were previously doing. > > However, it requires canReusePreviousFrameBuffer() to know the disposal method > > of the frame, which I do not know at this level. > > I *could* wait until we have looped through the image and then make sure no > > latter frames require this one. > > Ah. Here's what I was thinking when I decided to leave the disposal method off > of SkCodec::FrameInfo: The next frame's required frame tells you what you need > to know. If it does not depend on this frame, no future frames will. No need to > wait for a full loop, but it does mean we have to have received part of the next > frame. Since Chromium will not attempt to decode frame i (where i > 0) until i > is fully received, this leaves a gap between the end of i until the end of the > header of (i + 1) where we do not know whether any frames depend on i. Not sure > how common that is, but if it is a problem, that is an argument for making > SkCodec provide that information. > > > But that means the first pass cannot reuse buffers and the second pass > requires > > building up an inverted dependency list. > Okay, I think I follow you now. As an example, suppose: frame: 0 1 2 depends on: -1 0 0 When we get to frame 1 (depends on 0), we cannot take the bitmap because a future frame might need it (in this case, frame 2). So here we need to copy the bitmap. Going one step further: frame: 0 1 2 3 depends on: -1 0 0 -1 If we are about to decode frame 2, and we see frame 3 does not depend on frame 0 any more, we know we can take it. I believe that is what you meant by needing info about frame i + 1. I'll update the code to reflect this.
https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: options.fHasPriorFrame = true; On 2016/12/19 21:12:40, cblume wrote: > On 2016/12/19 14:28:54, scroggo_chromium wrote: > > On 2016/12/18 17:47:57, cblume wrote: > > > On 2016/12/16 18:45:45, scroggo_chromium wrote: > > > > On 2016/12/16 17:43:41, cblume wrote: > > > > > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > > > > > This isn't necessarily true. copyBitmapData returns a boolean. You > need > > to > > > > > check > > > > > > whether it (or takeBitmapDataIfWritable, which should be checked > first) > > > > > > succeeds. If so, you have the prior frame. If not, this frame will > still > > > be > > > > > > empty. > > > > > > > > > > > > You also might want to decode prior frames manually. If you do not > > provide > > > > the > > > > > > prior frame, SkCodec will decode it first, but will not store it for > > > later. > > > > If > > > > > > you want to cache a frame that index depends on, you need to decode it > > > > > yourself. > > > > > > > > > > I was thinking of not using takeBitmapDataIfWritable. > > > > > My intention is to leave that frame preserved. I assume > > > > takeBitmapDataIfWritable > > > > > will assume ownership of the bitmap itself (and thus not preserve the > > older > > > > > frame). > > > > > > > > That is correct. > > > > > > > > > > > > > > My reason for wanting to preserve the frame is I want to eventually have > > the > > > > > frames live further up in Blink, which can manage the cache more wisely. > > So > > > > I'm > > > > > trying to remove any what-stays-in-cache logic. > > > > > > > > > > Although, maybe that should happen at a later time and for now we can > > > > > takeBitmapDataIfWritable. > > > > > > > > But the net result for now is that you're leaving more in the cache. I > think > > > you > > > > should stick with the current behavior for now. > > > > > > > > > > > > > > > > > > > Because I am leaving prior frames in tact (and relying on the purging > > > behavior > > > > > for now), I believe we are keeping a cache of any frames we depend on > > > > manually. > > > > > > > > > > > > > > > As far as checking that copyBitmapData succeeds, you are completely > > correct. > > > I > > > > > think in that case I may want to setFailed(); and return. > > > > > > > > Agreed. > > > > > > I copied what we were previously doing. > > > However, it requires canReusePreviousFrameBuffer() to know the disposal > method > > > of the frame, which I do not know at this level. > > > I *could* wait until we have looped through the image and then make sure no > > > latter frames require this one. > > > > Ah. Here's what I was thinking when I decided to leave the disposal method off > > of SkCodec::FrameInfo: The next frame's required frame tells you what you need > > to know. If it does not depend on this frame, no future frames will. No need > to > > wait for a full loop, but it does mean we have to have received part of the > next > > frame. Since Chromium will not attempt to decode frame i (where i > 0) until i > > is fully received, this leaves a gap between the end of i until the end of the > > header of (i + 1) where we do not know whether any frames depend on i. Not > sure > > how common that is, but if it is a problem, that is an argument for making > > SkCodec provide that information. > > > > > But that means the first pass cannot reuse buffers and the second pass > > requires > > > building up an inverted dependency list. > > > > Okay, I think I follow you now. > > As an example, suppose: > frame: 0 1 2 > depends on: -1 0 0 > > When we get to frame 1 (depends on 0), we cannot take the bitmap because a > future frame might need it (in this case, frame 2). So here we need to copy the > bitmap. > > Going one step further: > frame: 0 1 2 3 > depends on: -1 0 0 -1 > > If we are about to decode frame 2, and we see frame 3 does not depend on frame 0 > any more, we know we can take it. I believe that is what you meant by needing > info about frame i + 1. Yes. Taken a step further, it is impossible for frame 4 to depend on frame 0, since 3 is independent. > > I'll update the code to reflect this.
https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: options.fHasPriorFrame = true; On 2016/12/19 21:16:15, scroggo_chromium wrote: > On 2016/12/19 21:12:40, cblume wrote: > > On 2016/12/19 14:28:54, scroggo_chromium wrote: > > > On 2016/12/18 17:47:57, cblume wrote: > > > > On 2016/12/16 18:45:45, scroggo_chromium wrote: > > > > > On 2016/12/16 17:43:41, cblume wrote: > > > > > > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > > > > > > This isn't necessarily true. copyBitmapData returns a boolean. You > > need > > > to > > > > > > check > > > > > > > whether it (or takeBitmapDataIfWritable, which should be checked > > first) > > > > > > > succeeds. If so, you have the prior frame. If not, this frame will > > still > > > > be > > > > > > > empty. > > > > > > > > > > > > > > You also might want to decode prior frames manually. If you do not > > > provide > > > > > the > > > > > > > prior frame, SkCodec will decode it first, but will not store it for > > > > later. > > > > > If > > > > > > > you want to cache a frame that index depends on, you need to decode > it > > > > > > yourself. > > > > > > > > > > > > I was thinking of not using takeBitmapDataIfWritable. > > > > > > My intention is to leave that frame preserved. I assume > > > > > takeBitmapDataIfWritable > > > > > > will assume ownership of the bitmap itself (and thus not preserve the > > > older > > > > > > frame). > > > > > > > > > > That is correct. > > > > > > > > > > > > > > > > > My reason for wanting to preserve the frame is I want to eventually > have > > > the > > > > > > frames live further up in Blink, which can manage the cache more > wisely. > > > So > > > > > I'm > > > > > > trying to remove any what-stays-in-cache logic. > > > > > > > > > > > > Although, maybe that should happen at a later time and for now we can > > > > > > takeBitmapDataIfWritable. > > > > > > > > > > But the net result for now is that you're leaving more in the cache. I > > think > > > > you > > > > > should stick with the current behavior for now. > > > > > > > > > > > > > > > > > > > > > > > Because I am leaving prior frames in tact (and relying on the purging > > > > behavior > > > > > > for now), I believe we are keeping a cache of any frames we depend on > > > > > manually. > > > > > > > > > > > > > > > > > > As far as checking that copyBitmapData succeeds, you are completely > > > correct. > > > > I > > > > > > think in that case I may want to setFailed(); and return. > > > > > > > > > > Agreed. > > > > > > > > I copied what we were previously doing. > > > > However, it requires canReusePreviousFrameBuffer() to know the disposal > > method > > > > of the frame, which I do not know at this level. > > > > I *could* wait until we have looped through the image and then make sure > no > > > > latter frames require this one. > > > > > > Ah. Here's what I was thinking when I decided to leave the disposal method > off > > > of SkCodec::FrameInfo: The next frame's required frame tells you what you > need > > > to know. If it does not depend on this frame, no future frames will. No need > > to > > > wait for a full loop, but it does mean we have to have received part of the > > next > > > frame. Since Chromium will not attempt to decode frame i (where i > 0) until > i > > > is fully received, this leaves a gap between the end of i until the end of > the > > > header of (i + 1) where we do not know whether any frames depend on i. Not > > sure > > > how common that is, but if it is a problem, that is an argument for making > > > SkCodec provide that information. > > > > > > > But that means the first pass cannot reuse buffers and the second pass > > > requires > > > > building up an inverted dependency list. > > > > > > > Okay, I think I follow you now. > > > > As an example, suppose: > > frame: 0 1 2 > > depends on: -1 0 0 > > > > When we get to frame 1 (depends on 0), we cannot take the bitmap because a > > future frame might need it (in this case, frame 2). So here we need to copy > the > > bitmap. > > > > Going one step further: > > frame: 0 1 2 3 > > depends on: -1 0 0 -1 > > > > If we are about to decode frame 2, and we see frame 3 does not depend on frame > 0 > > any more, we know we can take it. I believe that is what you meant by needing > > info about frame i + 1. > > Yes. Taken a step further, it is impossible for frame 4 to depend on frame 0, > since 3 is independent. > > > > > I'll update the code to reflect this. > Right. Sounds good.
ccameron@chromium.org changed reviewers: + ccameron@chromium.org
color bits lgtm
https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/60001/third_party/WebKit/Sour... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:185: // call startIncrementalDecode again? On 2016/12/19 14:28:54, scroggo_chromium wrote: > On 2016/12/18 17:47:57, cblume wrote: > > On 2016/12/16 18:45:45, scroggo_chromium wrote: > > > On 2016/12/16 17:43:41, cblume wrote: > > > > On 2016/12/16 15:03:48, scroggo_chromium wrote: > > > > > Yes. I only recently (https://skia-review.googlesource.com/5758) made a > > > change > > > > > where it would ever return incomplete, and I can see how it is a little > > > > > different from returning incomplete from incrementalDecode(). > > > > > > > > > > The intent is to say "we could not start, because there is not enough > > data", > > > > so > > > > > yes, another call to start is necessary. > > > > > > > > > > So the line below should probably leave the frame FrameEmpty. In the > case > > of > > > > the > > > > > first frame, we'll need to make sure it is initialized. For other > frames, > > > this > > > > > should never happen, because we wait until they're fully received to > > > decode... > > > > > But this code doesn't know that, so we may need to take it into account. > > It > > > > > might be awkward here, since the frame might not be truly empty (if its > > > > required > > > > > frame was copied into it), but we use empty to signal to attempt to > start > > > > again. > > > > > > > > I was thinking that same thing (if we restart because it is left > FrameEmpty > > > then > > > > we re-copy the dependent frame data). > > > > > > > > I think what I might do instead is call this earlier, before copying the > > prior > > > > frame contents. If the start succeeds, prepare the buffer and begin. > > > > > > > > For that to work, I need to know that startIncrementalDecode won't > actually > > do > > > > any decoding -- it will only get things setup, right? > > > > > > It won't do any decoding, but it stores the options, including whether > > > fHasPriorFrame. But I guess since we'll setFailed if the copy from the prior > > > frame fails, we can just assume that fHasPriorFrame is true if the frame we > > have > > > is FrameComplete. > > > > I didn't understand your last sentence. > > I think the code should look something like: > > if (/* requires prior frame */) { > options.fHasPriorFrame = true; > } > > fCodec->startIncrementalDecode(...); > // Assuming startIncrementalDecode returned kSuccess... > > if (!prevBuffer.copyBitmapData()) { > return setFailed(); // <--- > } > > If not for the line that calls setFailed(), we would be in a weird state where > we started the incremental decode, but lied about whether we put the prior > frame's data in the buffer before decoding. But copyBitmapData won't fail in the > general case (it could fail if we ran OOM, for example, but will not fail for > picking the wrong color type, since we just use N32 always), and if it does we > won't try to decode anyway. > > > > > I think I may have fixed this up. When startIncrementalDecode returns > incomplete > > it leaves the frame as FrameEmpty. Then we need Blink to attempt to decode the > > same frame once it gets more data. > > That's what I was thinking. Done. https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:79: int repetitionCount = m_codec->getRepetitionCount(); On 2016/12/19 14:28:54, scroggo_chromium wrote: > It looks like you removed a line that says > > CHECK(m_codec); > > Is it no longer the case that we're guaranteed to have an m_codec? If not, we > need to check for null. > > (Same for other cases) Done. https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:122: IntSize frameSize = size(); On 2016/12/19 14:28:54, scroggo_chromium wrote: > Maybe a FIXME here that we need to verify that we either do not need this > information, or SkCodec needs to give it to us? Done. https://codereview.chromium.org/2565323003/diff/180001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:184: setFailed(); On 2016/12/19 14:28:54, scroggo_chromium wrote: > This isn't quite right. Look at where these methods are currently called [1]. If > we cannot takeBitmapDataData, we need to copy it. > > [1] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
On 2016/12/14 18:19:35, f(malita) wrote: > BUILD.gn lgtm f(malita) would you mind taking a look at the BUILD.gn files one more time since they have changed since you last looked?
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_compile_dbg_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
noel@chromium.org changed reviewers: + noel@chromium.org
https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); Drive by: std:: container types not allowed in platform unless we changed that requirement. m_codec->getFrameInfo() seems to force you to use it. Is there and m_codec-> API to grab info for a particular frame? Seems suboptimal to me to have to copy the info for all frames each time we want info about a particular frame.
https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); On 2016/12/21 06:16:56, noel gordon wrote: > Drive by: std:: container types not allowed in platform unless we changed that > requirement. m_codec->getFrameInfo() seems to force you to use it. > > Is there and m_codec-> API to grab info for a particular frame? Seems > suboptimal to me to have to copy the info for all frames each time we want info > about a particular frame. Having to copy the vector each time definitely isn't ideal. After seeing it spelled out like this, I believe Leon and I both thought it might be better for SkCodec to keep a cached copy of the vector and just give us a reference when we need it. Perhaps going one step further would be to instead just give us the frame info and size, which avoids std:: here.
All of the failed tests are from the CHECK(m_codec) inside: decodeSize(), and decodeFrameCount() decodeSize()'s documentation seems like I shouldn't overload it since the gif format doesn't support decoding to scale. However, that is where I was (incorrectly) setting the size. Since SkCodec::NewFromStream fails unless it finds the size information, I can set it there. As far as decodeFrameCount(), this method should parse until it has info. But in the case of the errors, the codec doesn't exist and so no parsing can be done. I'll try just returning 1 (assuming there will be a frame at some point).
On 2016/12/21 10:22:48, cblume wrote: > All of the failed tests are from the CHECK(m_codec) inside: > decodeSize(), and > decodeFrameCount() > > decodeSize()'s documentation seems like I shouldn't overload it since the gif > format doesn't support decoding to scale. > However, that is where I was (incorrectly) setting the size. > > Since SkCodec::NewFromStream fails unless it finds the size information, I can > set it there. > > As far as decodeFrameCount(), this method should parse until it has info. But in > the case of the errors, the codec doesn't exist and so no parsing can be done. > I'll try just returning 1 (assuming there will be a frame at some point). Oh sorry, I mixed up "decodeSize()" and "decodedSize()". My talk about the documentation and decoding-to-scale applies to decodedSize(). The function in question, decodeSize(), should be empty since we will have already parsed enough to know the size when we create the codec.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
On 2016/12/21 08:18:19, cblume wrote: > https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... > File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp > (right): > > https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... > third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: > std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); > On 2016/12/21 06:16:56, noel gordon wrote: > > Drive by: std:: container types not allowed in platform unless we changed that > > requirement. m_codec->getFrameInfo() seems to force you to use it. > > > > Is there an m_codec-> API to grab info for a particular frame? Seems > > suboptimal to me to have to copy the info for all frames each time we want > > info about a particular frame. > > Having to copy the vector each time definitely isn't ideal. > After seeing it spelled out like this, I believe Leon and I both thought it > might be better for SkCodec to keep a cached copy of the vector and just give us > a reference when we need it. Yes, that might work. On my quick read of the m_codec->getFrameInfo() code for GIF though, it seemed to have potential side-effects because it called the GIF parser before constructing the std::vector<SkCodec::FrameInfo> that it returns. Such side-effects might have to be dealt with if the frame info vector internally cached (not sure). > Perhaps going one step further would be to instead just give us the frame info > and size, which avoids std:: here. Yes: returning a const SkInfo& for the frame requested would remove the std:: and be close to ideal (but might have to account for the side-effects thing I mentioned above).
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); On 2016/12/21 06:16:56, noel gordon wrote: > Drive by: std:: container types not allowed in platform unless we changed that > requirement. m_codec->getFrameInfo() seems to force you to use it. I do not currently see anything in the blink style guide [1], the Chromium style guide [2], or the Google style guide [3]. But it must have been desirable to avoid STL at some point, or we wouldn't have needed WTF. (A search of third_party/WebKit shows that there are other users [4], although I have not investigated those.) To support an API like this (more on that in response to your next comments), std:: seemed like the only way to go, since we've hidden our array classes in Skia and Skia cannot depend on WTF. > > Is there and m_codec-> API to grab info for a particular frame? No. Originally, I proposed an API on SkCodec that looked a little more like ImageDecoder (e.g. getFrameCount(), getFrameDuration(index))[5], but we thought it made sense to provide all the data up front, rather than having several queries. > Seems > suboptimal to me to have to copy the info for all frames each time we want info > about a particular frame. Agreed [7]. [1] https://www.chromium.org/blink/coding-style [2] https://chromium.googlesource.com/chromium/src/+/master/styleguide/c++/c++.md [3] https://google.github.io/styleguide/cppguide.html [4] https://cs.chromium.org/search/?q=std::vector+file:third_party/WebKit&sq=pack... [5] https://codereview.chromium.org/2045293002/#ps20001 [6] https://codereview.chromium.org/2045293002/#msg16 [7] https://codereview.chromium.org/2565323003/diff/1/third_party/WebKit/Source/p...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: android_n5x_swarming_rel on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/android_n5x_...)
https://codereview.chromium.org/2565323003/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:61: if (!m_codec) { I am investigating the error in AccessibilityActionBrowserTest.ImgElementGetImage It looks like it is coming from https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/access... That line has: sk_sp<SkImage> image = imageBitmap->bitmapImage()->imageForCurrentFrame( ColorBehavior::transformToGlobalTarget()); The call imageBitmap->bitmapImage() is returning null. Looking inside bitmapImage(), is appears like it is allowed to return null? ccameron@ is this a bug on that line? scroggo@ the reason it is returning null is SkCodec::NewFromStream() is returning null. The image data itself seems super tiny: https://cs.chromium.org/chromium/src/content/browser/accessibility/accessibil... I would imagine it arrives in one buffer. I checked and both data and m_segmentStream are both non-null. I tried some printf debugging and gdb but didn't have any luck with either inside Skia. I tried printf(), SkDebugf(), and SkCodecPrintf(). I also tried ($ gdb content_browsertests) b blink::GIFImageDecoder::onSetData, b SkCodec::NewFromStream, and b ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:52
x-ing here a bit, sorry. On 2016/12/21 21:25:15, scroggo_chromium wrote: > https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... > File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp > (right): > > https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... > third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: > std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); > On 2016/12/21 06:16:56, noel gordon wrote: > > Drive by: std:: container types not allowed in platform unless we changed that > > requirement. m_codec->getFrameInfo() seems to force you to use it. > > I do not currently see anything in the blink style guide [1], the Chromium style > guide [2], or the Google style guide [3]. It's not in style guides yet, but is in the Onion Soup list http://bit.ly/2hJrI4z > But it must have been desirable to > avoid STL at some point, or we wouldn't have needed WTF. STL wasn't as fast or portable (at the time, circa 2004) so WTF was created and tuned for the browser use-case, WTF::String in particular. > (A search of > third_party/WebKit shows that there are other users [4], although I have not > investigated those.) These uses are: - public/web (allowed, interface to the embedder Chrome) - Source/web (allowed, ditto) - unit tests (ignore, should really be using WTF::Vector) - mojo auto-created code (ignore) - WebDataBase callback to content:: (will be mojo in future, ignore) To support Onion Soup, the exiting separation b/w blink and its embedder (chrome) needs to be maintained so that nether knows about the other's details or type system. Will not good if WTF types to leak into Chrome, or if Chrome's use of types like std::vector|string etc leak into Blink. Hence http://bit.ly/2hJrI4z . > To support an API like this (more on that in response to your next comments), > std:: seemed like the only way to go, since we've hidden our array classes in > Skia and Skia cannot depend on WTF. Makes sense. > > Is there and m_codec-> API to grab info for a particular frame? > > No. Originally, I proposed an API on SkCodec that looked a little more like > ImageDecoder (e.g. getFrameCount(), getFrameDuration(index))[5], but we thought > it made sense to provide all the data up front, rather than having several > queries. Provided it melds with Platform::ImageSource, we'll be good. That class is the core api that we need for all higher layers in Blink (style, layout, paint) to talk to image decoders without knowing the minutiae of any image decoder type; PNG, JPEG, and so on. It has served us well (we woulda binned it by now, if not). Having its frame-based *AtIndex() queries seems to be all the we need, once we know the image size() (and other meta data read from image headers), with isSizeAvailable() control, and frameCount() and frameDurationAtIndex() for animated images. Simple API, but it does the job. If problems like copying all frames on *AtIndex queries ... > > Seems > > suboptimal to me to have to copy the info for all frames each time we want > info > > about a particular frame. > > Agreed [7]. appear when use SkCodec under-the-hood, then yeah, not great. ImageSource is also not a eager API, mostly it wants status reports. If on the other hand SKCodec is eager, and decodes image frames as a side-effect when ImageSource requests status information, then that will cause problems. ImageSource is explicit about decoding, via createFrameAtIndex(). Hopefully we can mesh ImageSource with SkCodec to make them co-operate. std::vector<> in return types from SkCodec: interesting, but avoid if possible. If you can't avoid, wrap the std::vector in a public/platform/WebVector.h since you are allowed to use those in Platform.
https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); On 2016/12/22 12:56:39, noel gordon wrote: > x-ing here a bit, sorry. > > On 2016/12/21 21:25:15, scroggo_chromium wrote: > > > https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... > > File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp > > (right): > > > > > https://codereview.chromium.org/2565323003/diff/200001/third_party/WebKit/Sou... > > third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: > > std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); > > On 2016/12/21 06:16:56, noel gordon wrote: > > > Drive by: std:: container types not allowed in platform unless we changed > that > > > requirement. m_codec->getFrameInfo() seems to force you to use it. > > > > I do not currently see anything in the blink style guide [1], the Chromium > style > > guide [2], or the Google style guide [3]. > > It's not in style guides yet, but is in the Onion Soup list > http://bit.ly/2hJrI4z Thanks for the pointer - I had not seen that doc. > > > But it must have been desirable to > > avoid STL at some point, or we wouldn't have needed WTF. > > STL wasn't as fast or portable (at the time, circa 2004) so WTF was created and > tuned for the browser use-case, WTF::String in particular. > > > (A search of > > third_party/WebKit shows that there are other users [4], although I have not > > investigated those.) > > These uses are: > - public/web (allowed, interface to the embedder Chrome) > - Source/web (allowed, ditto) > - unit tests (ignore, should really be using WTF::Vector) > - mojo auto-created code (ignore) > - WebDataBase callback to content:: (will be mojo in future, ignore) > > To support Onion Soup, the exiting separation b/w blink and its embedder > (chrome) needs to be maintained so that nether knows about the other's details > or type system. Will not good if WTF types to leak into Chrome, or if Chrome's > use of types like std::vector|string etc leak into Blink. Hence > http://bit.ly/2hJrI4z . > > > To support an API like this (more on that in response to your next comments), > > std:: seemed like the only way to go, since we've hidden our array classes in > > Skia and Skia cannot depend on WTF. > > Makes sense. > > > > Is there and m_codec-> API to grab info for a particular frame? > > > > No. Originally, I proposed an API on SkCodec that looked a little more like > > ImageDecoder (e.g. getFrameCount(), getFrameDuration(index))[5], but we > thought > > it made sense to provide all the data up front, rather than having several > > queries. > > Provided it melds with Platform::ImageSource, we'll be good. That class is the > core api that we need for all higher layers in Blink (style, layout, paint) to > talk to image decoders without knowing the minutiae of any image decoder type; > PNG, JPEG, and so on. > > It has served us well (we woulda binned it by now, if not). Having its > frame-based *AtIndex() queries seems to be all the we need, once we know the > image size() (and other meta data read from image headers), with > isSizeAvailable() control, and frameCount() and frameDurationAtIndex() for > animated images. > > Simple API, but it does the job. If problems like copying all frames on > *AtIndex queries ... > > > > Seems > > > suboptimal to me to have to copy the info for all frames each time we want > > info > > > about a particular frame. > > > > Agreed [7]. > > appear when use SkCodec under-the-hood, then yeah, not great. I've realized something I should have noticed before - we do not need to call getFrameInfo so often. We should be able to call it once inside decodeFrameCount. > > ImageSource is also not a eager API, mostly it wants status reports. If on the > other hand SKCodec is eager, and decodes image frames as a side-effect when > ImageSource requests status information, then that will cause problems. > ImageSource is explicit about decoding, via createFrameAtIndex(). Hopefully we > can mesh ImageSource with SkCodec to make them co-operate. > > std::vector<> in return types from SkCodec: interesting, but avoid if possible. > If you can't avoid, wrap the std::vector in a public/platform/WebVector.h since > you are allowed to use those in Platform. https://codereview.chromium.org/2565323003/diff/240001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/240001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:61: if (!m_codec) { On 2016/12/22 08:24:14, cblume wrote: > I am investigating the error in > AccessibilityActionBrowserTest.ImgElementGetImage > > It looks like it is coming from > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/modules/access... > > That line has: > sk_sp<SkImage> image = imageBitmap->bitmapImage()->imageForCurrentFrame( > ColorBehavior::transformToGlobalTarget()); > > The call imageBitmap->bitmapImage() is returning null. > > Looking inside bitmapImage(), is appears like it is allowed to return null? > > ccameron@ is this a bug on that line? > > > > > scroggo@ the reason it is returning null is SkCodec::NewFromStream() is > returning null. > The image data itself seems super tiny: > https://cs.chromium.org/chromium/src/content/browser/accessibility/accessibil... > > I would imagine it arrives in one buffer. I checked and both data and > m_segmentStream are both non-null. > > I tried some printf debugging and gdb but didn't have any luck with either > inside Skia. > I tried printf(), SkDebugf(), and SkCodecPrintf(). > I also tried ($ gdb content_browsertests) b blink::GIFImageDecoder::onSetData, b > SkCodec::NewFromStream, and b > ../../third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:52 Weird. I created a gif based on that, and SkGifCodec decodes it as expected when running Skia's unit tests.
On 2016/12/22 08:24:14, cblume wrote: > I tried some printf debugging and gdb but didn't have any luck with either > inside Skia. I was able to get printf-style debugging working (using std::endl to force flushes). With it, I was able to make some progress. SkGifCodec::NewFromStream is called. It then calls parses the gif inside the while(true) loop, entering these cases in order: SkGIFType SkGIFGlobalHeader SkGIFGlobalColormap SkGIFImageStart SkGIFDone Inside the SkGIFImageStart case there is a comment explaining how we follow Mozilla and do not parse corrupt data. It bumps us to SkGIFDone. That is what is happening. https://cs.chromium.org/chromium/src/third_party/skia/third_party/gif/SkGifIm... This is exactly how it worked in the Blink gif decoder. https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... Anyway, with incomplete data, both the Blink gif decoder and the SkGIFCodec have a reader which has imagesCount() returning 0. I think the new problem is in SkGifCodec::NewFromStream, where it tests if (0 == reader->imagesCount() .... return nullptr;
On 2016/12/29 20:36:56, cblume wrote: > in order: > SkGIFType > SkGIFGlobalHeader > SkGIFGlobalColormap > SkGIFImageStart > SkGIFDone I tried the Blink GIF decoder to see the order of its cases inside GIFImageReader: type global header global colormap image start after this point, it doesn't hit the "done" case. It continues on: image header image header lzwstart sub block lzw sub block image start done done type global header global colormap image start image header lzwstart sub block lzw sub block image start done done done type global header global colormap image start image header lzwstart sub block lzw sub block image start done done done So I compared the Blink GIF decoder to SkGIFCodec. The difference under the ImageStart case is almost nothing. But the difference under the ImageHeader case is significant. I see // NOTE: Chromium ... created an image which would fail. Is this the correct behavior. I don't fully understand what that note is saying, though. Is this potentially related?
On 2017/01/01 06:08:40, cblume wrote: > On 2016/12/29 20:36:56, cblume wrote: > > in order: > > SkGIFType > > SkGIFGlobalHeader > > SkGIFGlobalColormap > > SkGIFImageStart > > SkGIFDone > > I tried the Blink GIF decoder to see the order of its cases inside > GIFImageReader: > > type > global header > global colormap > image start > > after this point, it doesn't hit the "done" case. It continues on: > > image header > image header > lzwstart > sub block > lzw > sub block > image start > done > done > type > global header > global colormap > image start > image header > lzwstart > sub block > lzw > sub block > image start > done > done > done > type > global header > global colormap > image start > image header > lzwstart > sub block > lzw > sub block > image start > done > done > done > > > So I compared the Blink GIF decoder to SkGIFCodec. > The difference under the ImageStart case is almost nothing. > But the difference under the ImageHeader case is significant. > I see // NOTE: Chromium ... created an image which would fail. Is this the > correct behavior. > I don't fully understand what that note is saying, though. Is this potentially > related? I've done a bit more debugging and generally found the cause. It seems to be SkStreamBuffer optimizes when the SkStream it wraps supports position and length. But SkGifCodec doesn't seem to be aware of this optimization. If, inside the SkGifImageReader::parse() while loop, I add: if (m_bytesToConsume != 0) { m_streamBuffer.get(); } then things work*. The SkStreamBuffer.h comments suggest that if the SkStream it wraps supports length and position then it will buffer inside get() instead of inside buffer(). So when I added the call to get(), it was buffering. For now, I've just disabled hasPosition() and hasLocation(). *I still have a problem where pixel values seem to be incorrect, causing test failures. I'll have to look into those next. I found this at this piece of code: case SkGIFImageStart: { const char currentComponent = m_streamBuffer.get()[0]; if (currentComponent == '!') { // extension. } if (currentComponent == ',') { // image separator. } otherwise set SkGIFDone currentComponent was 0
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...) chromeos_amd64-generic_chromium_compile_only_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/chromeos_amd64-...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
On 2016/12/29 20:36:56, cblume wrote: > On 2016/12/22 08:24:14, cblume wrote: > > I tried some printf debugging and gdb but didn't have any luck with either > > inside Skia. > > I was able to get printf-style debugging working (using std::endl to force > flushes). > With it, I was able to make some progress. > > SkGifCodec::NewFromStream is called. > It then calls parses the gif inside the while(true) loop, entering these cases > in order: > SkGIFType > SkGIFGlobalHeader > SkGIFGlobalColormap > SkGIFImageStart > SkGIFDone > > > Inside the SkGIFImageStart case there is a comment explaining how we follow > Mozilla and do not parse corrupt data. It bumps us to SkGIFDone. That is what is > happening. > https://cs.chromium.org/chromium/src/third_party/skia/third_party/gif/SkGifIm... > > This is exactly how it worked in the Blink gif decoder. > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > Anyway, with incomplete data, both the Blink gif decoder and the SkGIFCodec have > a reader which has imagesCount() returning 0. > > I think the new problem is in SkGifCodec::NewFromStream, where it tests if (0 == > reader->imagesCount() .... return nullptr; FWIW, this is intentional - there is nothing to draw, and we cannot create an SkImageInfo, so it doesn't make sense to create an SkCodec. (This decision was made before SkCodec supported incremental decoding, so maybe we need to rethink that, but I am not convinced yet.) > So I compared the Blink GIF decoder to SkGIFCodec. > The difference under the ImageStart case is almost nothing. > But the difference under the ImageHeader case is significant. > I see // NOTE: Chromium ... created an image which would fail. Is this the > correct behavior. > I don't fully understand what that note is saying, though. Is this potentially > related? In the original code in GIFImageReader.cpp, this block is after the check for GIFSizeQuery. (i.e. here [1]) If I left it where it was, we would create an SkCodec for an image with zero width and zero height, which is not useful, since decoding it does nothing. [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > I've done a bit more debugging and generally found the cause. > It seems to be SkStreamBuffer optimizes when the SkStream it wraps supports > position and length. But SkGifCodec doesn't seem to be aware of this > optimization. > > If, inside the SkGifImageReader::parse() while loop, I add: > if (m_bytesToConsume != 0) { m_streamBuffer.get(); } > then things work*. > > The SkStreamBuffer.h comments suggest that if the SkStream it wraps supports > length and position then it will buffer inside get() instead of inside buffer(). > So when I added the call to get(), it was buffering. > For now, I've just disabled hasPosition() and hasLocation(). That is weird. We never try to read fBuffer without calling get(). The only times we avoid calling get() are when we instead call markPosition(). In those cases, we rewind later and read this point, so I would look there. > *I still have a problem where pixel values seem to be incorrect, causing test > failures. I'll have to look into those next. I'd look at your read method in your SkStream subclass. > I found this at this piece of code: > > case SkGIFImageStart: { > const char currentComponent = m_streamBuffer.get()[0]; > if (currentComponent == '!') { // extension. } > if (currentComponent == ',') { // image separator. } > otherwise set SkGIFDone I'm not sure how this relates to the rest of your comments. https://codereview.chromium.org/2565323003/diff/280001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/280001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:98: size_t bytesPeeked = m_reader->getSomeData(segment, m_position); The second time through this loop, we'll be reading from the same position, since we do not update m_position. FWIW, I think it would be simpler to implement peek with read, rather than read with peek, as you've done here. i.e.: size_t peek(void* buffer, size_t size) const override { size_t position = m_position; size_t bytesPeeked = read(buffer, size); m_position = position; return bytesPeeked; }
> On 2016/12/29 20:36:56, cblume wrote: > > On 2016/12/22 08:24:14, cblume wrote: > > > I tried some printf debugging and gdb but didn't have any luck with either > > > inside Skia. > > > > I was able to get printf-style debugging working (using std::endl to force > > flushes). > > With it, I was able to make some progress. > > > > SkGifCodec::NewFromStream is called. > > It then calls parses the gif inside the while(true) loop, entering these cases > > in order: > > SkGIFType > > SkGIFGlobalHeader > > SkGIFGlobalColormap > > SkGIFImageStart > > SkGIFDone > > > > > > Inside the SkGIFImageStart case there is a comment explaining how we follow > > Mozilla and do not parse corrupt data. It bumps us to SkGIFDone. That is what > is > > happening. > > > https://cs.chromium.org/chromium/src/third_party/skia/third_party/gif/SkGifIm... > > > > This is exactly how it worked in the Blink gif decoder. > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > > > Anyway, with incomplete data, both the Blink gif decoder and the SkGIFCodec > have > > a reader which has imagesCount() returning 0. > > > > I think the new problem is in SkGifCodec::NewFromStream, where it tests if (0 > == > > reader->imagesCount() .... return nullptr; > > FWIW, this is intentional - there is nothing to draw, and we cannot create an > SkImageInfo, so it doesn't make sense to create an SkCodec. (This decision was > made before SkCodec supported incremental decoding, so maybe we need to rethink > that, but I am not convinced yet.) Sounds good. I later figured out that the problem was elsewhere....sort of. We are indeed getting corrupt data. But that was to do with the SkStreamBuffer not buffering the data I believe. More on this below. > > > So I compared the Blink GIF decoder to SkGIFCodec. > > The difference under the ImageStart case is almost nothing. > > But the difference under the ImageHeader case is significant. > > I see // NOTE: Chromium ... created an image which would fail. Is this the > > correct behavior. > > I don't fully understand what that note is saying, though. Is this potentially > > related? > > In the original code in GIFImageReader.cpp, this block is after the check for > GIFSizeQuery. (i.e. here [1]) If I left it where it was, we would create an > SkCodec for an image with zero width and zero height, which is not useful, since > decoding it does nothing. I agree with this. This spot ended up being fine. > > [1] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > I've done a bit more debugging and generally found the cause. > > It seems to be SkStreamBuffer optimizes when the SkStream it wraps supports > > position and length. But SkGifCodec doesn't seem to be aware of this > > optimization. > > > > If, inside the SkGifImageReader::parse() while loop, I add: > > if (m_bytesToConsume != 0) { m_streamBuffer.get(); } > > then things work*. > > > > The SkStreamBuffer.h comments suggest that if the SkStream it wraps supports > > length and position then it will buffer inside get() instead of inside > buffer(). > > So when I added the call to get(), it was buffering. > > For now, I've just disabled hasPosition() and hasLocation(). > > That is weird. We never try to read fBuffer without calling get(). The only > times we avoid calling get() are when we instead call markPosition(). In those > cases, we rewind later and read this point, so I would look there. I don't fully follow. I think what you are saying is rewind() is not supposed to always reset m_position to 0. If we mark a position, we are supposed to reset to there, right? I thought markPosition() was paired with getDataAtPosition() (which calls SkStream::seek()). SkStreamBuffer knows about the marked position but does not communicate it back to SkStream. So calling rewind() on the SkStream cannot rewind to the marked position, right? > > > > *I still have a problem where pixel values seem to be incorrect, causing test > > failures. I'll have to look into those next. > > I'd look at your read method in your SkStream subclass. > > > I found this at this piece of code: > > > > case SkGIFImageStart: { > > const char currentComponent = m_streamBuffer.get()[0]; > > if (currentComponent == '!') { // extension. } > > if (currentComponent == ',') { // image separator. } > > otherwise set SkGIFDone > > I'm not sure how this relates to the rest of your comments. > Sorry, I wasn't clear enough. When my SkStream subclass returns true for hasLength and hasPosition, the loop hits these cases: SkGIFImageStart SkGIFDone I confirmed that inside SkGIFImageStart, currentComponent is 0. This means it doesn't enter the extension branch or the image separator branch. Instead, it falls through to where it sets SkGIFDone. Hence why the next time through the loop it goes to the SkGIFDone case. If I return false in hasLength and hasPosition, currentComponent is no longer 0. It behaves correctly.
https://codereview.chromium.org/2565323003/diff/280001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/280001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:98: size_t bytesPeeked = m_reader->getSomeData(segment, m_position); On 2017/01/03 22:36:58, scroggo_chromium wrote: > The second time through this loop, we'll be reading from the same position, > since we do not update m_position. Fixed. Thank you. > FWIW, I think it would be simpler to implement peek with read, rather than read > with peek, as you've done here. i.e.: > > size_t peek(void* buffer, size_t size) const override { > size_t position = m_position; > size_t bytesPeeked = read(buffer, size); > m_position = position; > return bytesPeeked; > } But peek is const and read is not. read() wants to update m_position. I could make m_position mutable. But I don't think that is better than read relying on peek.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_android_rel_ng on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/linux_androi...)
On 2017/01/04 01:29:59, cblume wrote: > > On 2016/12/29 20:36:56, cblume wrote: > > > On 2016/12/22 08:24:14, cblume wrote: > > > If, inside the SkGifImageReader::parse() while loop, I add: > > > if (m_bytesToConsume != 0) { m_streamBuffer.get(); } > > > then things work*. > > > > > > The SkStreamBuffer.h comments suggest that if the SkStream it wraps supports > > > length and position then it will buffer inside get() instead of inside > > buffer(). > > > So when I added the call to get(), it was buffering. > > > For now, I've just disabled hasPosition() and hasLocation(). > > > > That is weird. We never try to read fBuffer without calling get(). The only > > times we avoid calling get() are when we instead call markPosition(). In those > > cases, we rewind later and read this point, so I would look there. > > I don't fully follow. > > I think what you are saying is rewind() is not supposed to always reset > m_position to 0. > If we mark a position, we are supposed to reset to there, right? > I thought markPosition() was paired with getDataAtPosition() (which calls > SkStream::seek()). > > SkStreamBuffer knows about the marked position but does not communicate it back > to SkStream. So calling rewind() on the SkStream cannot rewind to the marked > position, right? Oh. I just looked at SkGifCodec and we do not call rewind there at all. It is all getDataAtPosition. I still see a difference in behavior when I return true in hasLength / hasPosition though. And when I stepped through it with debug prints I saw the calls to read happening in a different order. I'll investigate a bit more.
On 2017/01/04 04:07:03, cblume wrote: > On 2017/01/04 01:29:59, cblume wrote: > > > On 2016/12/29 20:36:56, cblume wrote: > > > > On 2016/12/22 08:24:14, cblume wrote: > > > > If, inside the SkGifImageReader::parse() while loop, I add: > > > > if (m_bytesToConsume != 0) { m_streamBuffer.get(); } > > > > then things work*. > > > > > > > > The SkStreamBuffer.h comments suggest that if the SkStream it wraps > supports > > > > length and position then it will buffer inside get() instead of inside > > > buffer(). > > > > So when I added the call to get(), it was buffering. > > > > For now, I've just disabled hasPosition() and hasLocation(). > > > > > > That is weird. We never try to read fBuffer without calling get(). The only > > > times we avoid calling get() are when we instead call markPosition(). In > those > > > cases, we rewind later and read this point, so I would look there. > > > > I don't fully follow. > > > > I think what you are saying is rewind() is not supposed to always reset > > m_position to 0. > > If we mark a position, we are supposed to reset to there, right? > > I thought markPosition() was paired with getDataAtPosition() (which calls > > SkStream::seek()). > > > > SkStreamBuffer knows about the marked position but does not communicate it > back > > to SkStream. So calling rewind() on the SkStream cannot rewind to the marked > > position, right? > > > Oh. I just looked at SkGifCodec and we do not call rewind there at all. It is > all getDataAtPosition. > > > I still see a difference in behavior when I return true in hasLength / > hasPosition though. > And when I stepped through it with debug prints I saw the calls to read > happening in a different order. > > I'll investigate a bit more. I definitely get a different pattern of read/buffer when I disable position/length. Here is what it looks like with position/length disabled: looping buffering 6 bytes type looping buffering 7 bytes global header looping buffering 12 bytes global colormap marking position 2 looping buffering 1 bytes image start looping buffering 0 bytes ******* Notice this line done Here it is when I enable length/position: looping buffering 6 bytes type looping buffering 7 bytes global header looping buffering 12 bytes global colormap marking position 2 looping buffering 1 bytes image start looping buffering 9 bytes ****** Compared to this line. It reads 0 in the other case. image header looping buffering 9 bytes image header looping buffering 1 bytes lzw start looping buffering 1 bytes subblock ...and so on.... I also took a look at what the read/buffer pattern was with the old Blink decoder. It is very different. But I'll include it just in case there is some info we want from it: looping type read 6 bytes looping global header read 5 bytes looping global colormap looping image start read 1 byte looping image header read 9 bytes looping image header read 9 bytes looping lzwstart read 1 byte looping sub block read 1 byte looping lzw looping sub block read 1 byte looping image start read 1 byte looping ...and so on... I'll investigate *why* we get different read/buffer patterns.
I've figured it out. Boy, that was a doozy. The simple solution was that my SkStream subclass didn't override move(). Implementing it allowed me to have a rewindable SkStream subclass. Because the SkStream subclass wasn't moving, it was reading an invalid value and exiting parsing early.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
There is still an issue where pixel values appear to be incorrect. I'll look into this next.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_android_rel_ng on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/linux_androi...)
On 2017/01/04 01:29:59, cblume wrote: > > On 2016/12/29 20:36:56, cblume wrote: > > > On 2016/12/22 08:24:14, cblume wrote: > > > > I tried some printf debugging and gdb but didn't have any luck with either > > > > inside Skia. > > > > > > I was able to get printf-style debugging working (using std::endl to force > > > flushes). > > > With it, I was able to make some progress. > > > > > > SkGifCodec::NewFromStream is called. > > > It then calls parses the gif inside the while(true) loop, entering these > cases > > > in order: > > > SkGIFType > > > SkGIFGlobalHeader > > > SkGIFGlobalColormap > > > SkGIFImageStart > > > SkGIFDone > > > > > > > > > Inside the SkGIFImageStart case there is a comment explaining how we follow > > > Mozilla and do not parse corrupt data. It bumps us to SkGIFDone. That is > what > > is > > > happening. > > > > > > https://cs.chromium.org/chromium/src/third_party/skia/third_party/gif/SkGifIm... > > > > > > This is exactly how it worked in the Blink gif decoder. > > > > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > > > > > > Anyway, with incomplete data, both the Blink gif decoder and the SkGIFCodec > > have > > > a reader which has imagesCount() returning 0. > > > > > > I think the new problem is in SkGifCodec::NewFromStream, where it tests if > (0 > > == > > > reader->imagesCount() .... return nullptr; > > > > FWIW, this is intentional - there is nothing to draw, and we cannot create an > > SkImageInfo, so it doesn't make sense to create an SkCodec. (This decision was > > made before SkCodec supported incremental decoding, so maybe we need to > rethink > > that, but I am not convinced yet.) > > Sounds good. > > I later figured out that the problem was elsewhere....sort of. > We are indeed getting corrupt data. > But that was to do with the SkStreamBuffer not buffering the data I believe. > More on this below. > > > > > > So I compared the Blink GIF decoder to SkGIFCodec. > > > The difference under the ImageStart case is almost nothing. > > > But the difference under the ImageHeader case is significant. > > > I see // NOTE: Chromium ... created an image which would fail. Is this the > > > correct behavior. > > > I don't fully understand what that note is saying, though. Is this > potentially > > > related? > > > > In the original code in GIFImageReader.cpp, this block is after the check for > > GIFSizeQuery. (i.e. here [1]) If I left it where it was, we would create an > > SkCodec for an image with zero width and zero height, which is not useful, > since > > decoding it does nothing. > > I agree with this. This spot ended up being fine. > > > > > [1] > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > > I've done a bit more debugging and generally found the cause. > > > It seems to be SkStreamBuffer optimizes when the SkStream it wraps supports > > > position and length. But SkGifCodec doesn't seem to be aware of this > > > optimization. > > > > > > If, inside the SkGifImageReader::parse() while loop, I add: > > > if (m_bytesToConsume != 0) { m_streamBuffer.get(); } > > > then things work*. > > > > > > The SkStreamBuffer.h comments suggest that if the SkStream it wraps supports > > > length and position then it will buffer inside get() instead of inside > > buffer(). > > > So when I added the call to get(), it was buffering. > > > For now, I've just disabled hasPosition() and hasLocation(). > > > > That is weird. We never try to read fBuffer without calling get(). The only > > times we avoid calling get() are when we instead call markPosition(). In those > > cases, we rewind later and read this point, so I would look there. > > I don't fully follow. > > I think what you are saying is rewind() is not supposed to always reset > m_position to 0. > If we mark a position, we are supposed to reset to there, right? > I thought markPosition() was paired with getDataAtPosition() (which calls > SkStream::seek()). > > SkStreamBuffer knows about the marked position but does not communicate it back > to SkStream. So calling rewind() on the SkStream cannot rewind to the marked > position, right? Sorry, I was unclear. I did not mean the method "rewind()"; I just meant logically rewinding to a certain point, which is done by seek(). > > > > > > > > *I still have a problem where pixel values seem to be incorrect, causing > test > > > failures. I'll have to look into those next. > > > > I'd look at your read method in your SkStream subclass. > > > > > I found this at this piece of code: > > > > > > case SkGIFImageStart: { > > > const char currentComponent = m_streamBuffer.get()[0]; > > > if (currentComponent == '!') { // extension. } > > > if (currentComponent == ',') { // image separator. } > > > otherwise set SkGIFDone > > > > I'm not sure how this relates to the rest of your comments. > > > > Sorry, I wasn't clear enough. > > When my SkStream subclass returns true for hasLength and hasPosition, the loop > hits these cases: > SkGIFImageStart > SkGIFDone > > I confirmed that inside SkGIFImageStart, currentComponent is 0. This means it > doesn't enter the extension branch or the image separator branch. Instead, it > falls through to where it sets SkGIFDone. Hence why the next time through the > loop it goes to the SkGIFDone case. > > > If I return false in hasLength and hasPosition, currentComponent is no longer 0. > It behaves correctly.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
On 2017/01/05 16:44:04, scroggo_chromium wrote: > Sorry, I was unclear. I did not mean the method "rewind()"; I just meant > logically rewinding to a certain point, which is done by seek(). Ah. No worries. :) I figured it out and fixed it. The call to get() had the effect of my SkStream subclass moving its position. When I added an extra call to get(), I was assuring that the position had moved. Inside the parse loop we end with a call to flush(). Flush calls SkStream::move(), which I had not implemented. So without the extra call to get(), the SkStream subclass wasn't moving its position. flush() wasn't checking whether the return value indicated success and falling back onto get(). Although, it probably shouldn't. It should probably only assert. Anyway, by implementing move() we now have the correct position inside my SkStream subclass. I have not yet implemented fork() or duplicate(). As far as I can tell, those are not yet used by SkGifCodec. But maybe they are used by another codec. Or maybe they will be used in the future. I'll look into implementing those later. I have an update on the invalid pixel values. It looks like it only happens in animated images (or possibly also progressive first frames that aren't immediately complete?). It seems when we try to reuse the previous frame we are hitting an assert that the frame is not complete. Maybe this is because the frame was partial. This is what I am investigating now.
On 2017/01/06 00:01:19, cblume wrote: > On 2017/01/05 16:44:04, scroggo_chromium wrote: > > Sorry, I was unclear. I did not mean the method "rewind()"; I just meant > > logically rewinding to a certain point, which is done by seek(). > Ah. No worries. :) I figured it out and fixed it. > > The call to get() had the effect of my SkStream subclass moving its position. > When I added an extra call to get(), I was assuring that the position had moved. > > Inside the parse loop we end with a call to flush(). Flush calls > SkStream::move(), which I had not implemented. So without the extra call to > get(), the SkStream subclass wasn't moving its position. flush() wasn't checking > whether the return value indicated success and falling back onto get(). > Although, it probably shouldn't. It should probably only assert. > > Anyway, by implementing move() we now have the correct position inside my > SkStream subclass. > I have not yet implemented fork() or duplicate(). As far as I can tell, those > are not yet used by SkGifCodec. But maybe they are used by another codec. Or > maybe they will be used in the future. I'll look into implementing those later. No, the codecs will not require fork or duplicate. > > > > > I have an update on the invalid pixel values. It looks like it only happens in > animated images (or possibly also progressive first frames that aren't > immediately complete?). It seems when we try to reuse the previous frame we are > hitting an assert that the frame is not complete. Maybe this is because the > frame was partial. This is what I am investigating now.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/360001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/360001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:68: SkCodec* temp = SkCodec::NewFromStream(m_segmentStream); "temp" was a bad name. https://codereview.chromium.org/2565323003/diff/380001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/380001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:176: } The old decoder would exit early when failed() was set. I'm just adding that back in. https://codereview.chromium.org/2565323003/diff/380001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:221: } I moved this logic up, above the call to startIncrementalDecode(). Previously, if we take the old frame, the info we provided to startIncrementalDecode() (frame.bitmap().getPixels()) would no longer be correct. So I'm attempting to reuse the frame first, before calling startIncrementalDecode(). https://codereview.chromium.org/2565323003/diff/380001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (left): https://codereview.chromium.org/2565323003/diff/380001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:82: EXPECT_EQ(cAnimationLoopOnce, decoder->repetitionCount()); This test *used* to assert that we hadn't yet seen the loop count and so we defaulted to looping once. However, you will notice it has all of the data ready and tells setData() that all of the data has been received. The new parsing behavior saw the loop count at this point and knew we looped indefinitely. I believe this new behavior is intentional. So I removed the test that asserts old behavior.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
https://codereview.chromium.org/2565323003/diff/380001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/380001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:325: } SkGifCodec is the one that is now responsible for invalid disposal methods being handled correctly. This test should be moved there. This is because from the client side we always get a completely filled frame. The client does not "overwrite previous" for example. Instead, the client simply provides the required frame asked of it.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/400001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/400001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:264: correctAlphaWhenFrameBufferSawNoAlpha(index); Moved this to only happen when the frame is complete.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
Just a little update on my bug hunt: inside frame.takeBitmapDataIfWritable(&requiredPreviousFrame) we are hitting DCHECK_EQ(FrameComplete, other->m_status); other->m_status is 0, which is FrameEmpty When I out the takeBitmapDataIfWritable and always just copy the bitmap, I get much further into the animation. Although, I hit a separate problem then. When a frame has finished its incremental decode, we set that frame's status to FrameComplete. Which means in a single-threaded scenario, if the image data gives us a complete frame, it should always be marked complete before moving to the next frame. I know we will issue decode commands on separate threads when we need to decode a separate frame, but I'm sure we lock the resource, too. So there is probably a case where the frame finishes but I don't mark it as complete. I'll look into it.
I am printing out the thread IDs each time we generate a frame and am indeed seeing that the decodes are spread across several threads, as expected. What ISN'T expected is there doesn't seem to be any synchronization. I printed this: about to decode frame 0 on thread 7 done decoding frame 0 on thread 7 about to decode frame 0 on thread 9 done decoding frame 0 on thread 9 about to decode frame about to decode frame 00about to decode frame on thread on thread 0 on thread 9107 done decoding frame 0 on thread 10done decoding frame done decoding frame 00 on thread on thread 97 about to decode frame 0 on thread 8 about to decode frame 0 on thread 10 about to decode frame 0 on thread 9 done decoding frame 0 on thread 9 about to decode frame 0 on thread 9 done decoding frame 0 on thread 9 about to decode frame 0 on thread 9 done decoding frame 0 on thread 10 done decoding frame 0 on thread 8 done decoding frame 0 on thread 9 about to decode frame 0 on thread 9 done decoding frame 0 on thread 9 about to decode frame 0 on thread 8 done decoding frame 0 on thread 8 about to decode frame 0 on thread 9 start succeeded increment success *** these are where the actual decoding happens done decoding frame 0 on thread 9 about to decode frame 1 on thread 8 *** finally we decode the next frame done decoding frame 1 on thread 8 This is unexpected because ImageFrameGenerator (where all of these calls are coming from) should be asserting that the mutex is locked. I'll investigate this a bit more. If I do not attempt to reuse previous frames, the decode works fine (despite being very slow). This gives me more reason to suspect a potential threading problem.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
I made a mistake when having all those frame-0 decodes interleaved. When Chrome first loads up it will decode several different gifs. That is why they are interleaved. I believe we are not attempting to decode separate frames of the same image concurrently. That said, I did find a few more mistakes which I've addressed in the last 3 patch sets. I'll explain each: * Patch Set 23: Don't initialize the frame until we know the frame exists * The default behavior if you don't override it is to assume there is 1 frame. This is true even if we hit a file that is corrupt and does not include any frame data or we don't have enough data downloaded to know if there is a first frame. There is a gif progressive decode test that compares the results of a image being truncated vs. progressively decoded given a certain amount of bytes downloaded. This test is still failing because of differences when 670 (or more?) bytes are available. However, it was previously starting at 1 byte for progressive decode, where it would not have enough data to know how many frames are available. I was continuing the "assume 1" behavior. But that means it calls initiailzeNewFrame() here despite not knowing the width/height of the image. So it doesn't know how much memory to allocate for that frame. The truncated example will attempt to create a new decoder and thus call initializeNewFrame() for each byte size. It finally succeeds at ~byte 450. But the progressive example doesn't call initializeNewFrame() again at ~byte 450 because it sees that it already initialized/allocated that frame. * Follow up: Should animated images be checking for frameInfos.size() - 1? Said another way: Am I allocating the next frame too soon and holding a frame past the end? * Patch Set 24 : Set the frame status after marking that pixels have changed (edit) * Setting the frame status triggers behavior, which might include updating things (like is-really-transparent) if the pixel values have changed. This means I should mark that the pixels have changed before setting the status. * Follow up: I really dislike this API design. This is one of those things you just have to know and remember. That makes it hard for people to contribute and maintain. This should really change. * Patch Set 25 : Lock / unlock pixels when copying and decoding. (edit) * I hadn't been locking / unlocking pixels at all. If this is ashmem that means we could run into trouble. * Follow up: I would like a sort of RAII to automatically unlock. Right now I manually call unlock at each exit point. This isn't clean and is asking for a mistake during maintenance some day.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: android_n5x_swarming_rel on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/android_n5x_...)
aelias@chromium.org changed reviewers: + msarett@google.com
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
I still have a few bits to clean but I wanted to mark this progress. https://codereview.chromium.org/2565323003/diff/380001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/380001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:325: } I spoke with Leon and we agreed that this is really testing implementation details, which is no good. So we are okay removing this test. https://codereview.chromium.org/2565323003/diff/580001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/580001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:171: // elsewhere. I'm going to want to address this before we finish this patch. https://codereview.chromium.org/2565323003/diff/580001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:229: } This whole chunk is basically copied and pasted from initializeNewFrame. It appeared like at this point the frame did not know it required a previous frame when data streamed in 1 byte at a time. With full data, the previous frame was known. I put this here temporarily to enforce that the required previous frame (and any other frame init) was done. https://codereview.chromium.org/2565323003/diff/580001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:258: if (!frame.copyBitmapData(requiredPreviousFrame)) { I wanted to only copy, not take -- again, temporary to reduce the bug path.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_chromeos_ozone_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_asan_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-simulator on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator/bui...)
https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (left): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:57: // There are some additional wrinkles here. First, ImageSource::clear() Just making a note to myself as I look over this change - this block is no longer true. ImageSource::clear() no longer exists, and the only time we delete the reader is in setFailed. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:73: if (isAllDataReceived() && parseCompleted() && m_reader->imagesCount() == 1) Why was this case removed? https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:75: else if (failed() || (m_reader && (!m_reader->imagesCount()))) SkCodec will never report 0 images, but don't we still want to return cAnimationLoopOnce if there was a failure? I know we've talked about continuing to loop the animation if there was a failure (crbug.com/267883), but I would lean towards leaving behavior changes out of this CL. (If we must make the change here, please call it out in the description, along with a reference to the bug. But I think there will be more to it than this change.) https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:52: if (!data) { We may want to do something besides return. In the DeferredImageDecoder world, null is passed to the decoder so it will drop its reference. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:60: if (!m_segmentStream) { The last block ensured that m_segmentStream is non-null https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:88: return 0; This happens to be cAnimationLoopOnce, but I think it's better to return it explicitly. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:102: return cAnimationNone; This should still return cAnimationLoopOnce (zero) (unless there's only one image, as noted on the left). Although SkCodec will potentially parse forward, as you note in the comment above, it is still possible that the loop count has not yet been downloaded. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:179: options.fHasPriorFrame = true; Doesn't this need to check whether we actually have the prior frame? I suppose we'll always create it if we do not (though we don't have to, which is a feature of the SkCodec API), but at least we should put this after we copyBitmapData down below? https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:184: frame.setOriginalFrameRect(IntRect(IntPoint(), frameSize)); Add a comment that this is incorrect? https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:191: frame.setSizeAndColorSpace(frameSize.width(), frameSize.height(), This seems a little misleading. If frameSize corresponded to the actual originalFrameRect, this would be incorrect. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:227: } Should this set the status to FramePartial, so we won't try to do this again next time? https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: correctAlphaWhenFrameBufferSawNoAlpha(index); This should only be called if there was no alpha. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:246: // We want to display the bit of the frame we have decoded only if it is SkCodec does not report a frame beyond the first unless it's complete, so if it's incomplete then there was an error in the image. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:247: // the first frame of an animation. That means we need to fill the rest Everything already got zeroed by your call to setSizeAndColorSpace. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:79: len = std::min(len, m_reader->size()); I think the second parameter here should be m_reader->size() - position https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:137: bool move(long offset) override { Need to update m_hasReadAllContents https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:161: // m_segmentStream is a raw pointer because it passes ownership to Move this comment above the line it applies to?
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_asan_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
Description was changed from ========== [WIP] Move gif image decoder to SkCodec SkCodec is gaining advantages over Blink's image decoders. scroggo@ has a nice summary: - Skia already has some features that Chromium is interested in: - decoding scaled versions of images - decoding subsets (i.e. issue 468914) - SIMD optimizations for writing pixels - In general, sharing code means that new features/bug fixes in Android benefit Chromium and vice versa - Skia's API is designed to allow the client to handle caching, so that the client that knows the bigger picture can make caching decisions in a more informed way than ImageDecoder can. BUG=663569 ========== to ========== Move gif image decoder to SkCodec SkCodec is gaining advantages over Blink's image decoders. scroggo@ has a nice summary: - Skia already has some features that Chromium is interested in: - decoding scaled versions of images - decoding subsets (i.e. issue 468914) - SIMD optimizations for writing pixels - In general, sharing code means that new features/bug fixes in Android benefit Chromium and vice versa - Skia's API is designed to allow the client to handle caching, so that the client that knows the bigger picture can make caching decisions in a more informed way than ImageDecoder can. BUG=663569 ==========
https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (left): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:73: if (isAllDataReceived() && parseCompleted() && m_reader->imagesCount() == 1) On 2017/02/24 19:04:30, scroggo_chromium wrote: > Why was this case removed? Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:75: else if (failed() || (m_reader && (!m_reader->imagesCount()))) On 2017/02/24 19:04:29, scroggo_chromium wrote: > SkCodec will never report 0 images, but don't we still want to return > cAnimationLoopOnce if there was a failure? > > I know we've talked about continuing to loop the animation if there was a > failure (crbug.com/267883), but I would lean towards leaving behavior changes > out of this CL. (If we must make the change here, please call it out in the > description, along with a reference to the bug. But I think there will be more > to it than this change.) Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:52: if (!data) { On 2017/02/24 19:04:29, scroggo_chromium wrote: > We may want to do something besides return. In the DeferredImageDecoder world, > null is passed to the decoder so it will drop its reference. Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:60: if (!m_segmentStream) { On 2017/02/24 19:04:29, scroggo_chromium wrote: > The last block ensured that m_segmentStream is non-null Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:88: return 0; On 2017/02/24 19:04:29, scroggo_chromium wrote: > This happens to be cAnimationLoopOnce, but I think it's better to return it > explicitly. Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:102: return cAnimationNone; On 2017/02/24 19:04:29, scroggo_chromium wrote: > This should still return cAnimationLoopOnce (zero) (unless there's only one > image, as noted on the left). Although SkCodec will potentially parse forward, > as you note in the comment above, it is still possible that the loop count has > not yet been downloaded. Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:179: options.fHasPriorFrame = true; On 2017/02/24 19:04:29, scroggo_chromium wrote: > Doesn't this need to check whether we actually have the prior frame? > > I suppose we'll always create it if we do not (though we don't have to, which is > a feature of the SkCodec API), but at least we should put this after we > copyBitmapData down below? I think I see what you are saying. On line 198 below we recursively call decode() again on the prior frame if it is not yet complete. The benefit of using this over SkCodec's API is we get a chance to cache those frames as we decode them. However, I'm happy to move it below the decode() / copyBitmapData() calls. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:184: frame.setOriginalFrameRect(IntRect(IntPoint(), frameSize)); On 2017/02/24 19:04:30, scroggo_chromium wrote: > Add a comment that this is incorrect? Perhaps I misunderstood what this is supposed to represent. If the frame updates only a small region of the total image, this would represent the smaller region that is updated, right? However, SkCodec deals in full image frames I believe. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:191: frame.setSizeAndColorSpace(frameSize.width(), frameSize.height(), On 2017/02/24 19:04:29, scroggo_chromium wrote: > This seems a little misleading. If frameSize corresponded to the actual > originalFrameRect, this would be incorrect. I'm similarly unsure what you mean here. The frame we send to SkCodec and the frame we get back is the full image size, correct? So I thought this should always be that size. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:227: } On 2017/02/24 19:04:29, scroggo_chromium wrote: > Should this set the status to FramePartial, so we won't try to do this again > next time? Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: correctAlphaWhenFrameBufferSawNoAlpha(index); On 2017/02/24 19:04:30, scroggo_chromium wrote: > This should only be called if there was no alpha. Oh, got ya. So presumably this is now done within SkCodec (which knows whether or not it saw the alpha) and I can get rid of it. Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:246: // We want to display the bit of the frame we have decoded only if it is On 2017/02/24 19:04:30, scroggo_chromium wrote: > SkCodec does not report a frame beyond the first unless it's complete, so if > it's incomplete then there was an error in the image. Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:247: // the first frame of an animation. That means we need to fill the rest On 2017/02/24 19:04:29, scroggo_chromium wrote: > Everything already got zeroed by your call to setSizeAndColorSpace. Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:79: len = std::min(len, m_reader->size()); On 2017/02/24 19:04:30, scroggo_chromium wrote: > I think the second parameter here should be > > m_reader->size() - position Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:137: bool move(long offset) override { On 2017/02/24 19:04:30, scroggo_chromium wrote: > Need to update m_hasReadAllContents Done. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:161: // m_segmentStream is a raw pointer because it passes ownership to On 2017/02/24 19:04:30, scroggo_chromium wrote: > Move this comment above the line it applies to? Done.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: correctAlphaWhenFrameBufferSawNoAlpha(index); On 2017/03/05 19:17:40, cblume wrote: > On 2017/02/24 19:04:30, scroggo_chromium wrote: > > This should only be called if there was no alpha. > > Oh, got ya. > So presumably this is now done within SkCodec (which knows whether or not it saw > the alpha) and I can get rid of it. Done. I have tried removing this, but it causes the GIFImageDecoderTest.bitmapAlphaType test to fail. Putting just this line back fixed it, strangely. I'm guessing that for this test I should be correcting the alpha and I just happen to be brute force correcting it. Should I loop over all the pixels to see if there is no alpha? Does SkCodec already do this somehow?
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:79: len = std::min(len, m_reader->size()); On 2017/03/05 19:17:42, cblume wrote: > On 2017/02/24 19:04:30, scroggo_chromium wrote: > > I think the second parameter here should be > > > > m_reader->size() - position > > Done. I also updated this in peek().
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
msarett@chromium.org changed reviewers: + msarett@chromium.org
https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: correctAlphaWhenFrameBufferSawNoAlpha(index); On 2017/03/06 03:21:36, cblume wrote: > On 2017/03/05 19:17:40, cblume wrote: > > On 2017/02/24 19:04:30, scroggo_chromium wrote: > > > This should only be called if there was no alpha. > > > > Oh, got ya. > > So presumably this is now done within SkCodec (which knows whether or not it > saw > > the alpha) and I can get rid of it. Done. > > I have tried removing this, but it causes the > GIFImageDecoderTest.bitmapAlphaType test to fail. Putting just this line back > fixed it, strangely. > > I'm guessing that for this test I should be correcting the alpha and I just > happen to be brute force correcting it. > > Should I loop over all the pixels to see if there is no alpha? Does SkCodec > already do this somehow? radient.gif (the image used in GIFImageDecoderTest.bitmapAlphaType) does not seem to have a transparent index - so we should know upfront that the image is opaque. But we can't actually mark the image as opaque until the decode is complete... since the pixels that haven't been decoded yet are transparent. I'm guessing you don't want to remove this line, you just want to only call it if the image is actually opaque.
https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:184: frame.setOriginalFrameRect(IntRect(IntPoint(), frameSize)); On 2017/03/05 19:17:41, cblume wrote: > On 2017/02/24 19:04:30, scroggo_chromium wrote: > > Add a comment that this is incorrect? > > Perhaps I misunderstood what this is supposed to represent. > If the frame updates only a small region of the total image, this would > represent the smaller region that is updated, right? > However, SkCodec deals in full image frames I believe. As I understand it, it represents the portion of the image updated by the frame. But it appears to only be used internally by ImageDecoder. It is not used for invals, for example. SkCodec does not currently expose this information, with the thinking that an external client does not need it. If we set it to the full frame, that seems like a workaround. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:191: frame.setSizeAndColorSpace(frameSize.width(), frameSize.height(), On 2017/03/05 19:17:41, cblume wrote: > On 2017/02/24 19:04:29, scroggo_chromium wrote: > > This seems a little misleading. If frameSize corresponded to the actual > > originalFrameRect, this would be incorrect. > > I'm similarly unsure what you mean here. > The frame we send to SkCodec and the frame we get back is the full image size, > correct? > So I thought this should always be that size. When I hear "frameSize", I think "the size of the rectangle updated by this frame", i.e. originalFrameRect, i.e. not necessarily the full frame. I think it would be more clear to set this to size.width(), size.height(), as it is in initFrameBuffer. (Ultimately, that's what you're doing, but the name throws me off.) https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: correctAlphaWhenFrameBufferSawNoAlpha(index); On 2017/03/06 03:21:36, cblume wrote: > On 2017/03/05 19:17:40, cblume wrote: > > On 2017/02/24 19:04:30, scroggo_chromium wrote: > > > This should only be called if there was no alpha. > > > > Oh, got ya. > > So presumably this is now done within SkCodec (which knows whether or not it > saw > > the alpha) and I can get rid of it. Done. > > I have tried removing this, but it causes the > GIFImageDecoderTest.bitmapAlphaType test to fail. Putting just this line back > fixed it, strangely. > > I'm guessing that for this test I should be correcting the alpha and I just > happen to be brute force correcting it. Well, you're "correcting it" regardless of whether there was alpha in the frame. This method is intended to be called if no pixels within the frameRect saw alpha, and the method itself checks for dependency and rectangles. If the frame buffer had alpha, you'd be doing the wrong thing here, but that happens to not be caught by the test. > > Should I loop over all the pixels to see if there is no alpha? No. If we need this information, it should be computed by SkCodec, when it loops through the pixels, rather than making a second pass. > Does SkCodec already do this somehow? No, SkCodec originally kept track of whether alpha was used, but removed it. https://bugs.chromium.org/p/skia/issues/detail?id=3582#c16 provides some context. Part of the thinking was that doing the check means we slow down decoding properly encoded images in order to make improperly decoded images draw faster. https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:53: m_segmentStream = nullptr; This just means we've lost our pointer to it, so next time we'll create a new one. But m_codec still holds on to the old one, so calling m_segmentReader->setReader, as below, has no effect on m_codec. Instead, I think you want to call m_segmentStream->setReader(null, false); https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:126: float GIFImageDecoder::frameDurationAtIndex(size_t index) const { I wonder if we should instead just return m_frameBufferCache[index].duration() (after ensuring index is within the range). That would make this the same as WEBP (and the WIP APNG https://codereview.chromium.org/2618633004/), meaning this no longer needs to be virtual (since it would still correctly return 0 for a static image). But then you would need to make sure you call ImageFrame::setDuration without decoding. This was previously done in initializeNewFrame, which you appear to have removed. https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:213: options.fHasPriorFrame = true; I was thinking you could move this into the block above. e.g. if (frame.getStatus() == ImageFrame::FrameEmpty) { ... if (requiredPreviousFrameIndex == WTF::kNotFound) { ... } else { // Actually decode the prior frame and copy/steal it into // the frame buffer options.fHasPriorFrame = true; } } https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:241: // Only frame 0 may return kIncompleteInput without it being an error This comment does not match the code below. You do not actually check the index, but you do check isAllDataReceived (which I think is also relevant). https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: return; There are no statements after this return, so it does not make a difference. And when I look at the implementation of postDecodeProcessing, it only returns false based on the Status, so I think this should never return false. Also, this should probably be called up above, when we set the status to FrameComplete? https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:163: // reference so we can append more data as it arrives. nit: I would say "pointer" instead of "reference". "reference" sounds like this is a ref-counted object, and we hold a ref, which is not the case.
https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:184: frame.setOriginalFrameRect(IntRect(IntPoint(), frameSize)); On 2017/03/06 16:27:31, scroggo_chromium wrote: > On 2017/03/05 19:17:41, cblume wrote: > > On 2017/02/24 19:04:30, scroggo_chromium wrote: > > > Add a comment that this is incorrect? > > > > Perhaps I misunderstood what this is supposed to represent. > > If the frame updates only a small region of the total image, this would > > represent the smaller region that is updated, right? > > However, SkCodec deals in full image frames I believe. > > As I understand it, it represents the portion of the image updated by the frame. > But it appears to only be used internally by ImageDecoder. It is not used for > invals, for example. SkCodec does not currently expose this information, with > the thinking that an external client does not need it. > > If we set it to the full frame, that seems like a workaround. Ohhh okay, I understand what you mean. You mean that it will not represent the portion of the image that has changed. Yeah, I think a comment to point that out is a good idea. I'll add it. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:191: frame.setSizeAndColorSpace(frameSize.width(), frameSize.height(), On 2017/03/06 16:27:31, scroggo_chromium wrote: > On 2017/03/05 19:17:41, cblume wrote: > > On 2017/02/24 19:04:29, scroggo_chromium wrote: > > > This seems a little misleading. If frameSize corresponded to the actual > > > originalFrameRect, this would be incorrect. > > > > I'm similarly unsure what you mean here. > > The frame we send to SkCodec and the frame we get back is the full image size, > > correct? > > So I thought this should always be that size. > > When I hear "frameSize", I think "the size of the rectangle updated by this > frame", i.e. originalFrameRect, i.e. not necessarily the full frame. I think it > would be more clear to set this to size.width(), size.height(), as it is in > initFrameBuffer. (Ultimately, that's what you're doing, but the name throws me > off.) I understand now. And I agree with you. Fixed. https://codereview.chromium.org/2565323003/diff/740001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: correctAlphaWhenFrameBufferSawNoAlpha(index); On 2017/03/06 16:27:31, scroggo_chromium wrote: > On 2017/03/06 03:21:36, cblume wrote: > > On 2017/03/05 19:17:40, cblume wrote: > > > On 2017/02/24 19:04:30, scroggo_chromium wrote: > > > > This should only be called if there was no alpha. > > > > > > Oh, got ya. > > > So presumably this is now done within SkCodec (which knows whether or not it > > saw > > > the alpha) and I can get rid of it. Done. > > > > I have tried removing this, but it causes the > > GIFImageDecoderTest.bitmapAlphaType test to fail. Putting just this line back > > fixed it, strangely. > > > > I'm guessing that for this test I should be correcting the alpha and I just > > happen to be brute force correcting it. > > Well, you're "correcting it" regardless of whether there was alpha in the frame. > This method is intended to be called if no pixels within the frameRect saw > alpha, and the method itself checks for dependency and rectangles. > > If the frame buffer had alpha, you'd be doing the wrong thing here, but that > happens to not be caught by the test. I think calling this method at all is a problem. Inside the method it will see that I've marked this frame as taking the whole image's rect. Because of that, it marks this frame as not dependent on a previous frame. This makes sense outside of my usage of SkCodec, but causes me a problem with it. I suppose I could mark this frame's rect even more incorrectly?? Have it always be (0,0)x(1x1) or some such? But that seems worse. > > Should I loop over all the pixels to see if there is no alpha? > > No. If we need this information, it should be computed by SkCodec, when it loops > through the pixels, rather than making a second pass. I agree that it would be better for SkCodec to provide the information rather than loop over the pixels a second time. But only if we actually need this information. I feel like we don't. > > Does SkCodec already do this somehow? > > No, SkCodec originally kept track of whether alpha was used, but removed it. > https://bugs.chromium.org/p/skia/issues/detail?id=3582#c16 provides some > context. > > Part of the thinking was that doing the check means we slow down decoding > properly encoded images in order to make improperly decoded images draw faster. I agree with this reasoning 100%. I would prefer to not make a special case for poorly-encoded images. The test itself covers 2 things: 1.) That the pre-multiplied v. not-pre-multiplied is preserved, and 2.) That both of these are clobbered and the image made opaque once the frame is fully decoded. I think I'll just get rid of the #2 part of the test. That is testing old behavior. https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:53: m_segmentStream = nullptr; On 2017/03/06 16:27:31, scroggo_chromium wrote: > This just means we've lost our pointer to it, so next time we'll create a new > one. But m_codec still holds on to the old one, so calling > m_segmentReader->setReader, as below, has no effect on m_codec. > > Instead, I think you want to call > > m_segmentStream->setReader(null, false); Done. Interestingly, the crash in DeferredImageDecoderTestWoPlatform.mixImagesGif went away when I set m_segmentStream = nullptr. But you are right, that is clearly not the correct behavior. I'll investigate the cause of this crash. https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:126: float GIFImageDecoder::frameDurationAtIndex(size_t index) const { On 2017/03/06 16:27:31, scroggo_chromium wrote: > I wonder if we should instead just return m_frameBufferCache[index].duration() > (after ensuring index is within the range). That would make this the same as > WEBP (and the WIP APNG https://codereview.chromium.org/2618633004/), meaning > this no longer needs to be virtual (since it would still correctly return 0 for > a static image). > > But then you would need to make sure you call ImageFrame::setDuration without > decoding. This was previously done in initializeNewFrame, which you appear to > have removed. Done. https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:213: options.fHasPriorFrame = true; On 2017/03/06 16:27:31, scroggo_chromium wrote: > I was thinking you could move this into the block above. e.g. > > if (frame.getStatus() == ImageFrame::FrameEmpty) { > ... > if (requiredPreviousFrameIndex == WTF::kNotFound) { > ... > } else { > // Actually decode the prior frame and copy/steal it into > // the frame buffer > options.fHasPriorFrame = true; > } > } Done. https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:241: // Only frame 0 may return kIncompleteInput without it being an error On 2017/03/06 16:27:31, scroggo_chromium wrote: > This comment does not match the code below. You do not actually check the index, > but you do check isAllDataReceived (which I think is also relevant). The comment was not very clear. I don't want to actually check if the frame is 0. I'll update the comments and you can tell me if it is now clear. I think it is much better now. https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: return; On 2017/03/06 16:27:31, scroggo_chromium wrote: > There are no statements after this return, so it does not make a difference. > > And when I look at the implementation of postDecodeProcessing, it only returns > false based on the Status, so I think this should never return false. > > Also, this should probably be called up above, when we set the status to > FrameComplete? Done. https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/800001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:163: // reference so we can append more data as it arrives. On 2017/03/06 16:27:31, scroggo_chromium wrote: > nit: I would say "pointer" instead of "reference". "reference" sounds like this > is a ref-counted object, and we hold a ref, which is not the case. Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:77: m_hasReadAllContents = reader->size() == m_position; I need some help here. With just this, I get this assert: SkStreamBuffer.cpp:36: fatal error: "assert(bytesRead == bytesToBuffer)" The problem with this assertion is it happens inside SkStreamBuffer::get(). It has bytesToBuffer == 1. But when it calls read() it cannot read anything. So even though it tries to read 1 byte, it reads 0. Hence bytesRead != bytesToBuffer. Makes sense, since we aren't doing anything when reader is null. So I added an else case to this if branch which sets m_position = 0. Setting m_hasReadAllContents to either true or false didn't make a difference. But this gives a different assertion: SkStreamBuffer.cpp:80: fatal error: "assert(position + length <= fStream->getLength())" The problem in this case is position and length are parameters passed in. Position doesn't seem to have gotten reset. So after a few reads, position + length is past the end of the buffer.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:346: EXPECT_EQ(premulFrame->bitmap().alphaType(), kPremul_SkAlphaType); I don't think radient.gif is one of those edge case images where we would have to iterate over all of the pixels to find out that it is opaque (I agree, we definitely shouldn't do that). I think this is an image without a transparent index that we should know to mark as opaque when the decode is complete.
https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:346: EXPECT_EQ(premulFrame->bitmap().alphaType(), kPremul_SkAlphaType); On 2017/03/07 13:27:18, msarett1 wrote: > I don't think radient.gif is one of those edge case images where we would have > to iterate over all of the pixels to find out that it is opaque (I agree, we > definitely shouldn't do that). > > I think this is an image without a transparent index that we should know to mark > as opaque when the decode is complete. You are right. radient.gif is only marked as transparent while there actually are transparent pixels because the first frame is partially decoded. Do we want to correctly mark that it is opaque? If so we'll need to update SkCodec to provide that information. I don't think there is any way for me to know right now as a client of SkCodec. I feel like maybe this could be included as a bool in the frame info? What do you think?
https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:137: if (!m_codec) I think you can DCHECK(m_codec); This will only be called if decodeFrameCount returned a value > 1, meaning m_codec is non-null. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:141: if (frameInfos.size() <= index) Similarly, this should never be false. (I see the old code does not have a DCHECK or a conditional.) https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:154: frame.setDisposalMethod(ImageFrame::DisposeKeep); You should probably also set the required index here, rather than inside decode. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:230: int rowsDecoded = 0; I see you do not do anything with this. But you could - if setSizeAndColorSpace allowed us to skip the zeroFillFrameRect (add a boolean?), you could fill in the remaining rows for a partial decode. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:242: // 2.) all data has been received and the frame is incomplete This comment is confusing. I think what you're getting at is that we're either on the first frame or there was an error (since the client will only ever attempt to decode a frame beyond the first if it is complete). They could also both be true. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:250: // Otherwise, assume we are on frame 0 and have a partial frame. This is not a valid assumption. The client should have verified frameIsCompleteAtIndex before decoding, but there could be an error in the file, meaning this could happen for any frame, regardless of isAllDataReceived(). I think what you really want is: if (index || isAllDataReceived()) { setFailed(); return; } frame.setPixelsChanged(true); https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:252: frame.setStatus(ImageFrame::FramePartial); frame should already have this status. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:256: return; nit: in some cases we return, and in other places we break. But nothing follows this switch statement, so they are not interestingly different. maybe pick one or the other? https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:77: m_hasReadAllContents = reader->size() == m_position; On 2017/03/07 09:00:11, cblume wrote: > I need some help here. > > With just this, I get this assert: > SkStreamBuffer.cpp:36: fatal error: "assert(bytesRead == bytesToBuffer)" > > The problem with this assertion is it happens inside SkStreamBuffer::get(). It > has bytesToBuffer == 1. But when it calls read() it cannot read anything. So > even though it tries to read 1 byte, it reads 0. Hence bytesRead != > bytesToBuffer. > > Makes sense, since we aren't doing anything when reader is null. I don't think I have enough context here. If there's no reader, why are we calling get()? > > So I added an else case to this if branch which sets m_position = 0. Setting > m_hasReadAllContents to either true or false didn't make a difference. But this > gives a different assertion: SkStreamBuffer.cpp:80: fatal error: > "assert(position + length <= fStream->getLength())" > > The problem in this case is position and length are parameters passed in. > Position doesn't seem to have gotten reset. So after a few reads, position + > length is past the end of the buffer. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:346: EXPECT_EQ(premulFrame->bitmap().alphaType(), kPremul_SkAlphaType); On 2017/03/07 19:36:29, cblume wrote: > On 2017/03/07 13:27:18, msarett1 wrote: > > I don't think radient.gif is one of those edge case images where we would have > > to iterate over all of the pixels to find out that it is opaque (I agree, we > > definitely shouldn't do that). > > > > I think this is an image without a transparent index that we should know to > mark > > as opaque when the decode is complete. > > You are right. > radient.gif is only marked as transparent while there actually are transparent > pixels because the first frame is partially decoded. > > Do we want to correctly mark that it is opaque? If so we'll need to update > SkCodec to provide that information. I don't think there is any way for me to > know right now as a client of SkCodec. > > I feel like maybe this could be included as a bool in the frame info? What do > you think? That sounds reasonable to me.
https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:346: EXPECT_EQ(premulFrame->bitmap().alphaType(), kPremul_SkAlphaType); On 2017/03/07 21:35:08, scroggo_chromium wrote: > On 2017/03/07 19:36:29, cblume wrote: > > On 2017/03/07 13:27:18, msarett1 wrote: > > > I don't think radient.gif is one of those edge case images where we would > have > > > to iterate over all of the pixels to find out that it is opaque (I agree, we > > > definitely shouldn't do that). > > > > > > I think this is an image without a transparent index that we should know to > > mark > > > as opaque when the decode is complete. > > > > You are right. > > radient.gif is only marked as transparent while there actually are transparent > > pixels because the first frame is partially decoded. > > > > Do we want to correctly mark that it is opaque? If so we'll need to update > > SkCodec to provide that information. I don't think there is any way for me to > > know right now as a client of SkCodec. > > > > I feel like maybe this could be included as a bool in the frame info? What do > > you think? > > That sounds reasonable to me. Oh yeah - we already do report whether the first frame is opaque, in the SkImageInfo. But we should report for later frames as well.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:137: if (!m_codec) On 2017/03/07 21:35:07, scroggo_chromium wrote: > I think you can > > DCHECK(m_codec); > > This will only be called if decodeFrameCount returned a value > 1, meaning > m_codec is non-null. Done. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:141: if (frameInfos.size() <= index) On 2017/03/07 21:35:07, scroggo_chromium wrote: > Similarly, this should never be false. (I see the old code does not have a > DCHECK or a conditional.) Done. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:154: frame.setDisposalMethod(ImageFrame::DisposeKeep); On 2017/03/07 21:35:07, scroggo_chromium wrote: > You should probably also set the required index here, rather than inside decode. Done. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:230: int rowsDecoded = 0; On 2017/03/07 21:35:08, scroggo_chromium wrote: > I see you do not do anything with this. But you could - if setSizeAndColorSpace > allowed us to skip the zeroFillFrameRect (add a boolean?), you could fill in the > remaining rows for a partial decode. I like that idea. I'll add it tomorrow. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:242: // 2.) all data has been received and the frame is incomplete On 2017/03/07 21:35:07, scroggo_chromium wrote: > This comment is confusing. I think what you're getting at is that we're either > on the first frame or there was an error (since the client will only ever > attempt to decode a frame beyond the first if it is complete). They could also > both be true. I think I understand. I believe we changed SkCodec to only include frame info if a frame is complete (other than frame 0). But I could potentially ask for a frame before it is included in the frame info vector. So maybe *this client* only has these two cases. But the comment says SkCodec (not this client) wouldn't allow for both cases to be true at the same time. I'll clean up the comment. :) https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:250: // Otherwise, assume we are on frame 0 and have a partial frame. On 2017/03/07 21:35:07, scroggo_chromium wrote: > This is not a valid assumption. The client should have verified > frameIsCompleteAtIndex before decoding, but there could be an error in the file, > meaning this could happen for any frame, regardless of isAllDataReceived(). > > I think what you really want is: > > if (index || isAllDataReceived()) { > setFailed(); > return; > } > > frame.setPixelsChanged(true); Done. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:252: frame.setStatus(ImageFrame::FramePartial); On 2017/03/07 21:35:07, scroggo_chromium wrote: > frame should already have this status. Done. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:256: return; On 2017/03/07 21:35:07, scroggo_chromium wrote: > nit: in some cases we return, and in other places we break. But nothing follows > this switch statement, so they are not interestingly different. maybe pick one > or the other? The reason I do this is: I want to use "break" in case we later add code below here. The exception to that is if we encountered an error and call setFailed(). In that case I would want to return early instead of execute any code that is added below here in the future. https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:77: m_hasReadAllContents = reader->size() == m_position; On 2017/03/07 21:35:08, scroggo_chromium wrote: > If there's no reader, why are we calling get()? That is a very good question. I'll look into this more and try to get an answer. I think this knowledge gap I have is the cause of a test failing. $ out/Debug/blink_platform_unittests --test-launcher-bot-mode --gtest_filter=DeferredImageDecoderTestWoPlatform.mixImagesGif triggers the behavior I described. GIFImageDecoder::onSetData with m_segmentStream->setReader(nullptr, false); caused it. simply returning (which I used to do before your code review comments) also caused it. But when I mistakenly set m_segmentStream = nullptr; the test passed. I'll investigate tomorrow.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...) win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:242: // 2.) all data has been received and the frame is incomplete On 2017/03/08 11:36:11, cblume wrote: > On 2017/03/07 21:35:07, scroggo_chromium wrote: > > This comment is confusing. I think what you're getting at is that we're either > > on the first frame or there was an error (since the client will only ever > > attempt to decode a frame beyond the first if it is complete). They could also > > both be true. > > I think I understand. > I believe we changed SkCodec to only include frame info if a frame is complete > (other than frame 0). We talked about doing that, but ended up adding FrameInfo.fFullyReceived instead (https://skia-review.googlesource.com/c/5703/), since it provided information about the first frame (whereas only reporting frames beyond the first if they were fully received was inconsistent with the first frame, which would need some other way to report whether it was fully received). > But I could potentially ask for a frame before it is > included in the frame info vector. In practice, I do not think so. decode is only called by frameBufferAtIndex, and will ensure index < frameCount first. It *could* be called before fFullyReceived is true, although in practice our client (BitmapImage) does not. > > So maybe *this client* only has these two cases. But the comment says SkCodec > (not this client) wouldn't allow for both cases to be true at the same time. > I'll clean up the comment. :) https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: // This decoder will not attempt to decode a frame unless it is complete Come to think of it, although BitmapImage will not try to decode an incomplete frame, nothing guarantees that. I think it'd be better to check against fFullyReceived.
https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:242: // 2.) all data has been received and the frame is incomplete On 2017/03/08 16:36:10, scroggo_chromium wrote: > On 2017/03/08 11:36:11, cblume wrote: > > On 2017/03/07 21:35:07, scroggo_chromium wrote: > > > This comment is confusing. I think what you're getting at is that we're > either > > > on the first frame or there was an error (since the client will only ever > > > attempt to decode a frame beyond the first if it is complete). They could > also > > > both be true. > > > > I think I understand. > > I believe we changed SkCodec to only include frame info if a frame is complete > > (other than frame 0). > > We talked about doing that, but ended up adding FrameInfo.fFullyReceived instead > (https://skia-review.googlesource.com/c/5703/), since it provided information > about the first frame (whereas only reporting frames beyond the first if they > were fully received was inconsistent with the first frame, which would need some > other way to report whether it was fully received). > > > But I could potentially ask for a frame before it is > > included in the frame info vector. > > In practice, I do not think so. decode is only called by frameBufferAtIndex, and > will ensure index < frameCount first. It *could* be called before fFullyReceived > is true, although in practice our client (BitmapImage) does not. > > > > > So maybe *this client* only has these two cases. But the comment says SkCodec > > (not this client) wouldn't allow for both cases to be true at the same time. > > I'll clean up the comment. :) > Right. Sorry, I meant to say this client only attempts to decode complete frames (other than 0). But a different client could get SkCodec::kIncompleteInput if it were to attempt to decode incomplete frames. https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: // This decoder will not attempt to decode a frame unless it is complete On 2017/03/08 16:36:10, scroggo_chromium wrote: > Come to think of it, although BitmapImage will not try to decode an incomplete > frame, nothing guarantees that. I think it'd be better to check against > fFullyReceived. Yeah, I think that makes sense. I also think if (index != 0 might actually be more clear to me. What do you think? if (index reads like a pointer being valid to me. Maybe that's just my old habits.
https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: // This decoder will not attempt to decode a frame unless it is complete On 2017/03/08 19:49:43, cblume wrote: > On 2017/03/08 16:36:10, scroggo_chromium wrote: > > Come to think of it, although BitmapImage will not try to decode an incomplete > > frame, nothing guarantees that. I think it'd be better to check against > > fFullyReceived. > > Yeah, I think that makes sense. > > I also think if (index != 0 might actually be more clear to me. What do you > think? if (index reads like a pointer being valid to me. Maybe that's just my > old habits. I do not have a strong preference here, but noel@ has encouraged me to use e.g. if (index) instead of if (index != 0) The style guide[1] currently accepts both: "Checks for zero/non-zero can be compared directly to zero if desired, but this is left to discretion." but I think I've seen some other changes over time, and I could believe this guideline has changed. (WebKit prefers the former [2], and this code (and the style guide, I believe - it looks very similar, too...) comes from WebKit, so that seems likely. Following WebKit style will likely keep you consistent with surrounding code.) [1] https://www.chromium.org/blink/coding-style#TOC-Null-false-and-0 [2] https://webkit.org/code-style-guidelines/#null-false-and-zero
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/840001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:230: int rowsDecoded = 0; On 2017/03/08 11:36:11, cblume wrote: > On 2017/03/07 21:35:08, scroggo_chromium wrote: > > I see you do not do anything with this. But you could - if > setSizeAndColorSpace > > allowed us to skip the zeroFillFrameRect (add a boolean?), you could fill in > the > > remaining rows for a partial decode. > > I like that idea. > I'll add it tomorrow. Done. https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: // This decoder will not attempt to decode a frame unless it is complete On 2017/03/08 21:04:11, scroggo_chromium wrote: > On 2017/03/08 19:49:43, cblume wrote: > > On 2017/03/08 16:36:10, scroggo_chromium wrote: > > > Come to think of it, although BitmapImage will not try to decode an > incomplete > > > frame, nothing guarantees that. I think it'd be better to check against > > > fFullyReceived. > > > > Yeah, I think that makes sense. > > > > I also think if (index != 0 might actually be more clear to me. What do you > > think? if (index reads like a pointer being valid to me. Maybe that's just > my > > old habits. > > I do not have a strong preference here, but noel@ has encouraged me to use e.g. > > if (index) > > instead of > > if (index != 0) > > The style guide[1] currently accepts both: > > "Checks for zero/non-zero can be compared directly to zero if desired, but this > is left to discretion." > > but I think I've seen some other changes over time, and I could believe this > guideline has changed. (WebKit prefers the former [2], and this code (and the > style guide, I believe - it looks very similar, too...) comes from WebKit, so > that seems likely. Following WebKit style will likely keep you consistent with > surrounding code.) > > [1] https://www.chromium.org/blink/coding-style#TOC-Null-false-and-0 > [2] https://webkit.org/code-style-guidelines/#null-false-and-zero Cool. I'll leave it as if (index :)
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_compile_dbg_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_comp...) win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/860001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:235: // This decoder will not attempt to decode a frame unless it is complete On 2017/03/08 21:04:11, scroggo_chromium wrote: > On 2017/03/08 19:49:43, cblume wrote: > > On 2017/03/08 16:36:10, scroggo_chromium wrote: > > > Come to think of it, although BitmapImage will not try to decode an > incomplete > > > frame, nothing guarantees that. I think it'd be better to check against > > > fFullyReceived. > > > > Yeah, I think that makes sense. > > > > I also think if (index != 0 might actually be more clear to me. What do you > > think? if (index reads like a pointer being valid to me. Maybe that's just > my > > old habits. > > I do not have a strong preference here, but noel@ has encouraged me to use e.g. > > if (index) > > instead of > > if (index != 0) > > The style guide[1] currently accepts both: > > "Checks for zero/non-zero can be compared directly to zero if desired, but this > is left to discretion." Yeah, recent addition. Reading if (index != 0) one might ask if index is pointer-type (naming style requires indexPtr to make that case clear) since 0 is another way of saying NULL, etc. With the style change that allow both, then we could get lots of if (indexPtr != nullptr) added to the code base. That'd get boring pretty quick, imho. It's if ([something convertible to boolean expression]) so anything that truthy or falsey works: integer types, floats, pointer types, operator() bool, etc. > Following WebKit style will likely keep you consistent with surrounding code. Yeah that, be consistent. The style rule (comparisons to 0) was designed to be simple and easy to remember, save typing, and work in all cases, aka, the simplest thing that could possibly work (KISS). if (index), if (indexPtr), etc. > [1] https://www.chromium.org/blink/coding-style#TOC-Null-false-and-0 > [2] https://webkit.org/code-style-guidelines/#null-false-and-zero
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp (right): https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:109: sk_sp<SkColorSpace> colorSpace) { Why not add a boolean (or an enum) for zeroFill? https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:111: // should only be called once. It leaks memory otherwise. This is a modified version of an existing comment, but I don't think it is true or has been for a long time. Pixel memory is owned by an SkPixelRef (or in the case of the ExternalMemoryAllocator, owned externally), so when we unref the old one, it will delete the pixel memory (or ignored for ExternalMemoryAllocator). Nothing else looks like it should leak memory. It would be wasteful and get rid of any existing progress, though. https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:119: return false; I don't like having two separate methods for this, but as-is, this can be: return m_bitmap.tryAllocPixels(m_allocator, 0);
https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp (right): https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:109: sk_sp<SkColorSpace> colorSpace) { On 2017/03/13 20:36:40, scroggo_chromium wrote: > Why not add a boolean (or an enum) for zeroFill? I am happy to make it 1 function with a parameter if you want. I actually prefer this way because it keeps functions as small and simple as possible. It keeps the mental burden low the number of cases small. For example 2 parameters (a bool and an enum with 5 values) have 10 total cases. As separate functions that would have been 7 cases. The book Clean Code talks about how a function should do only 1 thing, and specifically calls out flag parameters as an example where the function no longer does one thing. To quote: "Mousing over the call and seeing render(boolean isSuite) helps a little, but not that much. We should have split the function into two: renderForSuite() and renderForSingleTest()." This sentiment is mirrored in the C++ Core Guidelines in: - F.2: A function should perform a single logical operation http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#a-namerf-logicala... - F.3: Keep functions short and simple http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#a-namerf-singleaf... If I am honest though, I think I split these poorly. setSizeAndColorSpace still does two things. It sets the size/color space and zero fills. I think a better option would be to have setSizeAndColorSpace NOT zero fill and update all the call sites to also call ImageFrame::zeroFillPixelData(). https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:111: // should only be called once. It leaks memory otherwise. On 2017/03/13 20:36:40, scroggo_chromium wrote: > This is a modified version of an existing comment, but I don't think it is true > or has been for a long time. Pixel memory is owned by an SkPixelRef (or in the > case of the ExternalMemoryAllocator, owned externally), so when we unref the old > one, it will delete the pixel memory (or ignored for ExternalMemoryAllocator). > Nothing else looks like it should leak memory. It would be wasteful and get rid > of any existing progress, though. Okay. If it is safe then I think this comment should be deleted. When we unref the old data it gets ignored for ExternalMemoryAllocator? Does that mean it would leak in that case? I'm not sure I understood. https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:119: return false; On 2017/03/13 20:36:40, scroggo_chromium wrote: > I don't like having two separate methods for this, but as-is, this can be: > > return m_bitmap.tryAllocPixels(m_allocator, 0); Done.
https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp (right): https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:111: // should only be called once. It leaks memory otherwise. On 2017/03/13 21:53:12, cblume wrote: > On 2017/03/13 20:36:40, scroggo_chromium wrote: > > This is a modified version of an existing comment, but I don't think it is > true > > or has been for a long time. Pixel memory is owned by an SkPixelRef (or in the > > case of the ExternalMemoryAllocator, owned externally), so when we unref the > old > > one, it will delete the pixel memory (or ignored for ExternalMemoryAllocator). > > Nothing else looks like it should leak memory. It would be wasteful and get > rid > > of any existing progress, though. > > Okay. If it is safe then I think this comment should be deleted. > > When we unref the old data it gets ignored for ExternalMemoryAllocator? Does > that mean it would leak in that case? I'm not sure I understood. The idea of the ExternalMemoryAllocator is that someone else (externally) owns that memory. In practice, it is owned by discardable memory somewhere. https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:135: std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); I think I suggested before that you only call this inside decodeFrameCount, and store the frameInfos on the decoder. Maybe a cleaner way to handle this isCleared bit is to keep track of how much of m_data has been parsed: class GIFImageDecoder { private: size_t m_bytesParsed = 0; }; GIFImageDecoder::decodeFrameCount() { if (m_data->size() >= m_bytesParsed) { m_frameInfos = m_codec->getFrameInfo(); m_bytesParsed = m_data->size(); } return m_frameInfos.size(); }
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp (right): https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:109: sk_sp<SkColorSpace> colorSpace) { On 2017/03/13 21:53:12, cblume wrote: > On 2017/03/13 20:36:40, scroggo_chromium wrote: > > Why not add a boolean (or an enum) for zeroFill? > > I am happy to make it 1 function with a parameter if you want. > > I actually prefer this way because it keeps functions as small and simple as > possible. It keeps the mental burden low the number of cases small. For example > 2 parameters (a bool and an enum with 5 values) have 10 total cases. As separate > functions that would have been 7 cases. > > The book Clean Code talks about how a function should do only 1 thing, and > specifically calls out flag parameters as an example where the function no > longer does one thing. > > To quote: > "Mousing over the call and seeing render(boolean isSuite) helps a little, but > not that much. We should have split the function into two: renderForSuite() and > renderForSingleTest()." > > This sentiment is mirrored in the C++ Core Guidelines in: > - F.2: A function should perform a single logical operation > http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#a-namerf-logicala... > - F.3: Keep functions short and simple > http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#a-namerf-singleaf... > > > > If I am honest though, I think I split these poorly. > setSizeAndColorSpace still does two things. It sets the size/color space and > zero fills. > > I think a better option would be to have setSizeAndColorSpace NOT zero fill and > update all the call sites to also call ImageFrame::zeroFillPixelData(). I made a separate pull request that I think refactors ImageFrame::setSizeAndColorSpace better. https://codereview.chromium.org/2749703002/ I'm happy to add a parameter if you feel that is better. But I think this new PR fixes the issue where these 2 functions felt weird -- I think this PR feels more natural. https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/900001/third_party/WebKit/Sou... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:135: std::vector<SkCodec::FrameInfo> frameInfos = m_codec->getFrameInfo(); On 2017/03/14 16:08:48, scroggo_chromium wrote: > I think I suggested before that you only call this inside decodeFrameCount, and > store the frameInfos on the decoder. Maybe a cleaner way to handle this > isCleared bit is to keep track of how much of m_data has been parsed: > > class GIFImageDecoder { > private: > size_t m_bytesParsed = 0; > }; > > GIFImageDecoder::decodeFrameCount() { > if (m_data->size() >= m_bytesParsed) { > m_frameInfos = m_codec->getFrameInfo(); > m_bytesParsed = m_data->size(); > } > return m_frameInfos.size(); > } I'll try this out. But I think my SegmentStream::read() will need to know whether it was cleared or not. So I would imagine I still need to keep something like it. This is a result of the deferred image decode swapping order.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_android on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/cast_shell_a...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/1060001/skia/BUILD.gn File skia/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/1060001/skia/BUILD.gn#newcode278 skia/BUILD.gn:278: "//third_party/skia/src/codec/SkWbmpCodec.cpp", I want to revisit this. I think I eagerly included everything in skia/src/codec/ but we may not need it all. When I wanted to compare binary size, I saw SkSwizzler, SkBmpCodec, SkBmpMaskCodec, and SkBmpRLECodec (and maybe others) in the final binary. They weren't being stripped out. But they also might not be used. https://codereview.chromium.org/2565323003/diff/1060001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1060001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:188: frame.zeroFillPixelData(); I added the zero initialization here. Previously, I wasn't setting options.fZeroInitialized. Nor was I zeroing the pixel data (after landing a patch that no longer calls zeroFillPixelData() inside allocatePixelData() ). This caused errors. But I'm surprised by that. The default should be for SkCodec to zero fill. So even if I didn't zero-fill, SkCodec should have. Right? But the errors feel like that wasn't happening. Additionally, I may want to lie about the zero initialization. If I claim it was zero filled then SkCodec won't zero fill. When I learn how many rows were decoded, I zero-fill the rest. Right? So I think I want to lie here (and include a comment explaining that)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/1120001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1120001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:188: frame.setStatus(ImageFrame::FrameAllocated); There are a few places where SkCodec::startIncrementalDecode() can return SkCodec::kIncompleteInput for gifs. [1] [2] In this code, when that happens we simply return. But this means we did not update the frame status. The next time around, we will try to allocate again or take/copy again. To avoid doing this, I added ImageFrame::FrameAllocated. [1] https://cs.chromium.org/chromium/src/third_party/skia/src/codec/SkGifCodec.cp... [2] https://cs.chromium.org/chromium/src/third_party/skia/src/codec/SkGifCodec.cp...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_android on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/cast_shell_a...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_android on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/cast_shell_a...) cast_shell_linux on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/cast_shell_linu...) linux_chromium_tsan_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...) mac_chromium_compile_dbg_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_comp...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_tsan_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_asan_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
I believe these two tests: GIFImageDecoderTest.externalAllocator DeferredImageDecoderTest.frameOpacity require this patch to land: https://skia-review.googlesource.com/c/9810/ scroggo@ Is there any chance we could land that patch soon? Thanks! https://codereview.chromium.org/2565323003/diff/1200001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1200001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:64: decoder->frameBufferAtIndex(0); Previously, decode->repetitionCount() would end up getting the frame infos. From there, it was able to return the correct value without requiring a frame be decoded. But the problem is it was fetching the frame infos vector more often. And since repetitionCount() is a const member function, it couldn't update the stored values. Rather than have it fetch the frame infos vector again, I figured it made more sense for the test to reflect what the image decoders do: allow the decoder to update its state.
https://codereview.chromium.org/2565323003/diff/1200001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1200001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:132: m_frameInfos = m_codec->getFrameInfo(); Oh also, I might need your help with an access violation inside SkGifCodec here. The call stack has: chrome_child!SkGifCodec::onGetFrameInfo+0xb2 chrome_child!blink::GIFImageDecoder::decodeFrameCount This is what is causing the 9gag failure. Is it because SkGifCodec::onGetFrameInfo() calls fReader->frameContext() which might return null? We don't check for null and we dereference it. Is it only null because I am doing something wrong here?
https://codereview.chromium.org/2565323003/diff/1060001/skia/BUILD.gn File skia/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/1060001/skia/BUILD.gn#newcode278 skia/BUILD.gn:278: "//third_party/skia/src/codec/SkWbmpCodec.cpp", On 2017/03/25 07:54:39, cblume wrote: > I want to revisit this. > > I think I eagerly included everything in skia/src/codec/ but we may not need it > all. > > When I wanted to compare binary size, I saw SkSwizzler, This is used by all of the SkCodecs. > SkBmpCodec, > SkBmpMaskCodec, and SkBmpRLECodec (and maybe others) in the final binary. They > weren't being stripped out. But they also might not be used. They're used by SkCodec::NewFromStream, which will create an SkBmpCodec if it's a BMP image. We have #ifdefs in SkCodec to remove some of them - specifically the ones that depend on external libraries, but we could have more to disable the ones you don't want. https://codereview.chromium.org/2565323003/diff/1060001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1060001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:188: frame.zeroFillPixelData(); On 2017/03/25 07:54:39, cblume wrote: > I added the zero initialization here. > > > Previously, I wasn't setting options.fZeroInitialized. Nor was I zeroing the > pixel data (after landing a patch that no longer calls zeroFillPixelData() > inside allocatePixelData() ). > > This caused errors. But I'm surprised by that. > The default should be for SkCodec to zero fill. So even if I didn't zero-fill, > SkCodec should have. Right? Right. > But the errors feel like that wasn't happening. If that is the case, it is a bug in SkCodec. > > > Additionally, I may want to lie about the zero initialization. If I claim it was > zero filled then SkCodec won't zero fill. When I learn how many rows were > decoded, I zero-fill the rest. Right? So I think I want to lie here (and include > a comment explaining that) No, that would cause a problem. Setting it to kYes_ZeroInitialized means that SkCodec can skip zeroing the memory. If an image frame is subset (and independent), SkCodec won't zero it, but then will draw the frame on top of it. So you would need to follow up by zeroing the pixels that were not touched by the frame. What you really want is for SkCodec to not zero the rows that it didn't decode. It does not, even if you use kNo_ZeroInitialized. That allows you to follow up and zero-fill the rest. (There are a couple exceptions (e.g. subset frame) where SkCodec goes ahead and fills the entire image rectangle, and then reports via rowsDecoded that all were initialized.) https://codereview.chromium.org/2565323003/diff/1200001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1200001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:132: m_frameInfos = m_codec->getFrameInfo(); On 2017/03/28 08:51:40, cblume wrote: > Oh also, I might need your help with an access violation inside SkGifCodec here. > > The call stack has: > chrome_child!SkGifCodec::onGetFrameInfo+0xb2 > chrome_child!blink::GIFImageDecoder::decodeFrameCount > > This is what is causing the 9gag failure. > > > > Is it because SkGifCodec::onGetFrameInfo() calls fReader->frameContext() which > might return null? We don't check for null and we dereference it. > > Is it only null because I am doing something wrong here? I don't see how it could be null. - imagesCount() returns either m_frames.size() or m_frames.size() - 1 - onGetFrameInfo calls frameContext on [0, imagesCount() - 1] - frameContext returns non-null for [0, m_frames.size() - 1]
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-device on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-device/builds...) ios-device-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-device-xcode-...) ios-simulator-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator-xco...)
https://codereview.chromium.org/2565323003/diff/1060001/skia/BUILD.gn File skia/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/1060001/skia/BUILD.gn#newcode278 skia/BUILD.gn:278: "//third_party/skia/src/codec/SkWbmpCodec.cpp", On 2017/04/05 18:06:14, scroggo_chromium wrote: > On 2017/03/25 07:54:39, cblume wrote: > > I want to revisit this. > > > > I think I eagerly included everything in skia/src/codec/ but we may not need > it > > all. > > > > When I wanted to compare binary size, I saw SkSwizzler, > > This is used by all of the SkCodecs. > > > SkBmpCodec, > > SkBmpMaskCodec, and SkBmpRLECodec (and maybe others) in the final binary. They > > weren't being stripped out. But they also might not be used. > > They're used by SkCodec::NewFromStream, which will create an SkBmpCodec if it's > a BMP image. We have #ifdefs in SkCodec to remove some of them - specifically > the ones that depend on external libraries, but we could have more to disable > the ones you don't want. Oh. In that case don't worry about it. I was measuring binary size difference and concluded at the time "Oh, I guess we'll only really get apples-to-apples comparisons once it is all moved over." But when I reviewed this file I wondered if maybe I could remove them and get an apples-to-apples comparison right now. But I'm not too worried about it. I think we can ignore this for now. https://codereview.chromium.org/2565323003/diff/1060001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1060001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:188: frame.zeroFillPixelData(); On 2017/04/05 18:06:14, scroggo_chromium wrote: > On 2017/03/25 07:54:39, cblume wrote: > > I added the zero initialization here. > > > > > > Previously, I wasn't setting options.fZeroInitialized. Nor was I zeroing the > > pixel data (after landing a patch that no longer calls zeroFillPixelData() > > inside allocatePixelData() ). > > > > This caused errors. But I'm surprised by that. > > The default should be for SkCodec to zero fill. So even if I didn't zero-fill, > > SkCodec should have. Right? > > Right. > > > But the errors feel like that wasn't happening. > > If that is the case, it is a bug in SkCodec. This was a false alarm on my part. The errors were separate I believe. > > Additionally, I may want to lie about the zero initialization. If I claim it > was > > zero filled then SkCodec won't zero fill. When I learn how many rows were > > decoded, I zero-fill the rest. Right? So I think I want to lie here (and > include > > a comment explaining that) > > No, that would cause a problem. > > Setting it to kYes_ZeroInitialized means that SkCodec can skip zeroing the > memory. If an image frame is subset (and independent), SkCodec won't zero it, > but then will draw the frame on top of it. So you would need to follow up by > zeroing the pixels that were not touched by the frame. > > What you really want is for SkCodec to not zero the rows that it didn't decode. > It does not, even if you use kNo_ZeroInitialized. That allows you to follow up > and zero-fill the rest. (There are a couple exceptions (e.g. subset frame) where > SkCodec goes ahead and fills the entire image rectangle, and then reports via > rowsDecoded that all were initialized.) Oh. Perfect. So this is actually working exactly like we want right now. Goodie :D https://codereview.chromium.org/2565323003/diff/1220001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:63: SegmentStream segment_stream; A lot of variables like this one use the new Blink style guide naming [1] and will not pass presubmit until that change is adopted tomorrow. [1] https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/9aAJamXilD8
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-device-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-device-xcode-...) ios-simulator on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator/bui...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_asan_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h:389: // And gif is also treated differently, since it relies on SkCodec which IIUC, previously, GIF and APNG required at least kFramePartial, and WEBP required kFrameComplete. With this change, GIF matches WEBP, and APNG is the only one to use the default. What is different about using SkCodec that requires the frame to be complete now? Or is it because with SkCodec the Status could now be kFrameAlloc? Can you write a test for this? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:16: bool all_contents_received) { It looks like this parameter is unused. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:19: has_read_all_contents_ = reader->size() == position_; If the position > reader->size(), should has_read_all_contents_ not be true? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:75: has_read_all_contents_ = true; Why is this set to true? Should this be in an "else" to the "if (reader_)"? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:83: has_read_all_contents_ = true; Similarly, why did this get set to true? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:86: has_read_all_contents_ = position == reader_->size(); This gets repeated a lot. Maybe add an inlined method? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:88: position_ = position; It seems odd to me that we correct position if there is a reader_, but otherwise we blindly use the input. Maybe that's okay though. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:12: // SegmentStream has 4 accessors which do not alter state: I have not yet reviewed this file. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:45: // if we did not create m_codec and thus did not pass ownership to it This still references "m_codec" (from before the name change). https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:65: SkCodec* codec = SkCodec::NewFromStream(segment_stream_); Why do you have this local variable? Instead, you could say codec_.reset(SkCodec::NewFromStream(...)); if (!codec_) { segment_stream_ = nullptr; return; } https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:70: // creation fails. In this case, release our reference so we can create a I interpret "release our reference" as "decrement the refcount", which is not what is going on here. Instead, maybe this should say something like: If NewFromStream failed, it deleted segment_stream_. Stop pointing to reclaimed memory. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:95: if (IsAllDataReceived() && frame_infos_.size() == 1) Could this be a problem if we have not parsed since all data was received? e.g. 1. Receive the first frame (IsAllDataReceived == false) 2. Parse available data (frame_infos_.size() == 1) 3. Receive the rest of the data (including more frames) 4. Call this method Then might we return kCAnimationNone, when the image actually is animated? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:131: if (!Failed() && !(segment_stream_ && segment_stream_->IsCleared())) This seems to say that if segment_stream_ is null, we will call getFrameInfo. I would expect that we would not, but in that case I think codec_ will also be null? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:146: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || Alternatively, this could be frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_infos_[index].fAlphaType)); https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:165: if (segment_stream_ && segment_stream_->IsCleared()) Again, I think if codec_ is non-null, segment_stream_ will also be non-null. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:191: frame.SetStatus(ImageFrame::kFrameAllocated); I see you added this new state, but it does not look like you ever check for it. I assume if startIncrementalDecode returns kIncompleteInput, we'll need to call it again on the next attempt to decode. Right now it looks like we only call it if the status is kFrameEmpty? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:192: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || Isn't this taken care of by InitializeNewFrame? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:234: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || Again, I think this was handled by InitializeNewFrame? Maybe you reset it because of the SetHasAlpha(true) call below? Isn't that unnecessary because we always treat a partial image as having alpha? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:268: // However, if the current and next frame depend on different frames then we I think I broke this with https://skia-review.googlesource.com/c/9810/. What you really want to know is the disposal method for frame |index|, which I (currently) do not provide. This seems like a decent way to determine it, but in the above CL, I use the frame rectangles to determine dependency. So we could get in the following situation: Frame A is Keep Frame B depends on A, is DisposePrevious Frame C is independent, because its rectangle covers A's, and it has no transparency. C is also DisposePrevious Frame D depends on A CanReusePreviousFrameBuffer(B) will determine that it can reuse A (since C does not depend on it), but we'll need A again for D. Maybe the right solution here is to make SkCodec report the disposal method. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (left): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:58: bool SetFailed() override; Shouldn't we delete codec_ on failure?
https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:268: // However, if the current and next frame depend on different frames then we On 2017/04/17 20:04:56, scroggo_chromium wrote: > I think I broke this with https://skia-review.googlesource.com/c/9810/. > > What you really want to know is the disposal method for frame |index|, which I > (currently) do not provide. This seems like a decent way to determine it, but in > the above CL, I use the frame rectangles to determine dependency. So we could > get in the following situation: > > Frame A is Keep > Frame B depends on A, is DisposePrevious > Frame C is independent, because its rectangle covers A's, and it has no > transparency. C is also DisposePrevious > Frame D depends on A > > CanReusePreviousFrameBuffer(B) will determine that it can reuse A (since C does > not depend on it), but we'll need A again for D. > > Maybe the right solution here is to make SkCodec report the disposal method. https://skia-review.googlesource.com/c/13722/ adds the method to SkCodec.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h:389: // And gif is also treated differently, since it relies on SkCodec which On 2017/04/17 20:04:55, scroggo_chromium wrote: > IIUC, previously, GIF and APNG required at least kFramePartial, and WEBP > required kFrameComplete. With this change, GIF matches WEBP, and APNG is the > only one to use the default. > > What is different about using SkCodec that requires the frame to be complete > now? Or is it because with SkCodec the Status could now be kFrameAlloc? > > Can you write a test for this? Your understanding is correct. The way I understand it (which might be wrong) is that if I want to decode frame X+1 (which relies on frame X), I will want to keep frame X around so we don't decode from the beginning to get it back. That means I need the complete frame X when using SkCodec. However, the part I might not understand correctly is why gif didn't previously require the full frame. I'm not sure why a partial frame would be sufficient to decode future frames. I think we actually don't want to test this. It is a protected method. I believe we want to test public interfaces. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:16: bool all_contents_received) { On 2017/04/17 20:04:56, scroggo_chromium wrote: > It looks like this parameter is unused. Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:19: has_read_all_contents_ = reader->size() == position_; On 2017/04/17 20:04:56, scroggo_chromium wrote: > If the position > reader->size(), should has_read_all_contents_ not be true? Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:75: has_read_all_contents_ = true; On 2017/04/17 20:04:56, scroggo_chromium wrote: > Why is this set to true? Should this be in an "else" to the "if (reader_)"? Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:83: has_read_all_contents_ = true; On 2017/04/17 20:04:56, scroggo_chromium wrote: > Similarly, why did this get set to true? Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:86: has_read_all_contents_ = position == reader_->size(); On 2017/04/17 20:04:56, scroggo_chromium wrote: > This gets repeated a lot. Maybe add an inlined method? Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:88: position_ = position; On 2017/04/17 20:04:56, scroggo_chromium wrote: > It seems odd to me that we correct position if there is a reader_, but otherwise > we blindly use the input. Maybe that's okay though. I agree. We cannot correct without a reader. So my only other option would be to ignore the input. Given those two options I feel like blindly not correcting is the better move. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:45: // if we did not create m_codec and thus did not pass ownership to it On 2017/04/17 20:04:56, scroggo_chromium wrote: > This still references "m_codec" (from before the name change). > Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:65: SkCodec* codec = SkCodec::NewFromStream(segment_stream_); On 2017/04/17 20:04:56, scroggo_chromium wrote: > Why do you have this local variable? Instead, you could say > > codec_.reset(SkCodec::NewFromStream(...)); > > if (!codec_) { > segment_stream_ = nullptr; > return; > } Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:70: // creation fails. In this case, release our reference so we can create a On 2017/04/17 20:04:56, scroggo_chromium wrote: > I interpret "release our reference" as "decrement the refcount", which is not > what is going on here. > > Instead, maybe this should say something like: > > If NewFromStream failed, it deleted segment_stream_. Stop pointing to reclaimed > memory. Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:95: if (IsAllDataReceived() && frame_infos_.size() == 1) On 2017/04/17 20:04:56, scroggo_chromium wrote: > Could this be a problem if we have not parsed since all data was received? > > e.g. > 1. Receive the first frame (IsAllDataReceived == false) > 2. Parse available data (frame_infos_.size() == 1) > 3. Receive the rest of the data (including more frames) > 4. Call this method > > Then might we return kCAnimationNone, when the image actually is animated? I think I follow. But doesn't SkCodec parse the info when it is provided? So step 3 would imply we've parsed further? If not, I need to make sure we've parsed all we have when this function is called. That would mean calling one of SkCodec's functions which forces the parse, right? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:131: if (!Failed() && !(segment_stream_ && segment_stream_->IsCleared())) On 2017/04/17 20:04:56, scroggo_chromium wrote: > This seems to say that if segment_stream_ is null, we will call getFrameInfo. I > would expect that we would not, but in that case I think codec_ will also be > null? Oops. The ! should have been in front of the IsCleared(). I accidentally clumped the null check in with it. Thank you for catching this. However, segment_stream_ can be null when codec_ is non-null. That is what SegmentStream::IsCleared() is all about. https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:146: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/17 20:04:56, scroggo_chromium wrote: > Alternatively, this could be > > frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_infos_[index].fAlphaType)); Doing that would include kUnknown_SkAlphaType. I'm willing to do this if you prefer it. I avoided it because I was unsure if it might (now or in the future) be possible for SkGifDecoder to return kUnknown_SkAlphaType. I guess it might not be possible. The gif format stores the duration, the disposal method, and the transparency in the graphics control extension. So we couldn't fill in the other values while still not knowing the transparency. Even going byte-by-byte, the disposal method is stored in the same byte. The duration are in the following 2 bytes. So if we have the duration, we must already have the transparency information. Okay, yeah. I'll use SkAlphaTypeIsOpaque(). :) https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:191: frame.SetStatus(ImageFrame::kFrameAllocated); On 2017/04/17 20:04:56, scroggo_chromium wrote: > I see you added this new state, but it does not look like you ever check for it. > I assume if startIncrementalDecode returns kIncompleteInput, we'll need to call > it again on the next attempt to decode. Right now it looks like we only call it > if the status is kFrameEmpty? You are right. It looks like I accidentally nested this inside the else branch of the if(kFrameEmpty) What I want to happen is: if(kFrameEmpty) call AllocatePixelData() only once set to kFrameAllocated if(kFrameAllocated) startIncrementalDecode() That should let me only allocate once but call startIncrementalDecode() as many times as needed until we get at least partial. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:192: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/17 20:04:56, scroggo_chromium wrote: > Isn't this taken care of by InitializeNewFrame? Not really. The call to frame.AllocatePixelData() above essentially resets the alpha to true. So after calling that, we have to reset it back to what it *actually* is. I have a separate CR for this which I'll return to once I get more time: https://codereview.chromium.org/2762643004/ https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:234: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/17 20:04:56, scroggo_chromium wrote: > Again, I think this was handled by InitializeNewFrame? > > Maybe you reset it because of the SetHasAlpha(true) call below? Isn't that > unnecessary because we always treat a partial image as having alpha? I reset it because of the SetHasAlpha(true) below, correct. My thought was a partial image has alpha. But once it becomes a full image, it may no longer have alpha. Should the image continue to have alpha even if the palette says there is none only because it was partially decoded for a bit? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:268: // However, if the current and next frame depend on different frames then we On 2017/04/17 20:04:56, scroggo_chromium wrote: > I think I broke this with https://skia-review.googlesource.com/c/9810/. > > What you really want to know is the disposal method for frame |index|, which I > (currently) do not provide. This seems like a decent way to determine it, but in > the above CL, I use the frame rectangles to determine dependency. So we could > get in the following situation: > > Frame A is Keep > Frame B depends on A, is DisposePrevious > Frame C is independent, because its rectangle covers A's, and it has no > transparency. C is also DisposePrevious > Frame D depends on A > > CanReusePreviousFrameBuffer(B) will determine that it can reuse A (since C does > not depend on it), but we'll need A again for D. > > Maybe the right solution here is to make SkCodec report the disposal method. I see what you're saying. I think you might be right. I think it is wise to keep frame C marked independent. But I cannot use this to know that frame A is no longer needed and can be safely purged if I'm under memory pressure. I'll need some other indicator. I think exposing disposal method is the best way to do it. The only time I'll know a frame is done is when it is no longer referenced by DisposePrevious. So if I encounter a frame that is Keep, DisposeBackground, or Unspecified then I know that frame is no longer used for the rest of the image. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (left): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:58: bool SetFailed() override; On 2017/04/17 20:04:56, scroggo_chromium wrote: > Shouldn't we delete codec_ on failure? Done.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_asan_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h:389: // And gif is also treated differently, since it relies on SkCodec which On 2017/04/20 03:42:26, cblume wrote: > On 2017/04/17 20:04:55, scroggo_chromium wrote: > > IIUC, previously, GIF and APNG required at least kFramePartial, and WEBP > > required kFrameComplete. With this change, GIF matches WEBP, and APNG is the > > only one to use the default. > > > > What is different about using SkCodec that requires the frame to be complete > > now? Or is it because with SkCodec the Status could now be kFrameAlloc? > > > > Can you write a test for this? > > Your understanding is correct. > The way I understand it (which might be wrong) is that if I want to decode frame > X+1 (which relies on frame X), I will want to keep frame X around so we don't > decode from the beginning to get it back. That means I need the complete frame X > when using SkCodec. > > However, the part I might not understand correctly is why gif didn't previously > require the full frame. I'm not sure why a partial frame would be sufficient to > decode future frames. It will help if you take a look at the two callsites, in ClearCacheExceptFrame. If the client wants to clear all frames except X, we don't quite honor it, with the thinking that the client will probably want to decode X+1 in the future. If this method returns false for X, we'll *also* keep around the frame that X depends on. The default is to compare the status against empty. If X is empty, clearly keeping it in the cache will not help us decode X+1, so we hang on to the frame that it depended on. Partial is enough to decode any frame that depends on X, since we've already copied the frame that X depends on in order to decode X. I suspect you really want to make sure we're at least in the FramePartial Status. If we've only allocated, then we haven't yet done the copy, so we need to hang on to the frame X depends on in order to finish decoding X. (I just looked back at the new Decode method. I've added a comment there, too, but now I think you want to be in at least the FrameAllocated Status. Though you don't change to that state when you copy the required frame, I believe that was your intent. In which case the default implementation of this method should work fine.) > > I think we actually don't want to test this. It is a protected method. I believe > we want to test public interfaces. In general, yes, we want to test public interfaces, but you're changing a behavior. How do we know it's for the better? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:16: bool all_contents_received) { On 2017/04/20 03:42:26, cblume wrote: > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > It looks like this parameter is unused. > > Done. You've commented out the parameter, but why not remove it entirely if it's not used? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:88: position_ = position; On 2017/04/20 03:42:26, cblume wrote: > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > It seems odd to me that we correct position if there is a reader_, but > otherwise > > we blindly use the input. Maybe that's okay though. > > I agree. > We cannot correct without a reader. So my only other option would be to ignore > the input. Given those two options I feel like blindly not correcting is the > better move. Maybe add a comment? https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:95: if (IsAllDataReceived() && frame_infos_.size() == 1) On 2017/04/20 03:42:26, cblume wrote: > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > Could this be a problem if we have not parsed since all data was received? > > > > e.g. > > 1. Receive the first frame (IsAllDataReceived == false) > > 2. Parse available data (frame_infos_.size() == 1) > > 3. Receive the rest of the data (including more frames) > > 4. Call this method > > > > Then might we return kCAnimationNone, when the image actually is animated? > > I think I follow. > But doesn't SkCodec parse the info when it is provided? So step 3 would imply > we've parsed further? SkCodec will parse it when you call getRepetitionCount(), down below. I *think* GetFrameCount() will always be called before RepetitionCount() in practice, but it seems fragile to require it. > > If not, I need to make sure we've parsed all we have when this function is > called. That would mean calling one of SkCodec's functions which forces the > parse, right? Yes. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:131: if (!Failed() && !(segment_stream_ && segment_stream_->IsCleared())) On 2017/04/20 03:42:27, cblume wrote: > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > This seems to say that if segment_stream_ is null, we will call getFrameInfo. > I > > would expect that we would not, but in that case I think codec_ will also be > > null? > > Oops. The ! should have been in front of the IsCleared(). I accidentally clumped > the null check in with it. > Thank you for catching this. > > However, segment_stream_ can be null when codec_ is non-null. That is what > SegmentStream::IsCleared() is all about. > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... I don't think so. If the SegmentReader is set to null, you don't set segment_stream_ to null, you just call setReader(null). (segment_reader_ looks to only be null if you don't have a codec or if you've already failed. In both cases you won't reach this check.) https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:146: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/20 03:42:26, cblume wrote: > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > Alternatively, this could be > > > > frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_infos_[index].fAlphaType)); > > Doing that would include kUnknown_SkAlphaType. > I'm willing to do this if you prefer it. I avoided it because I was unsure if it > might (now or in the future) be possible for SkGifDecoder to return > kUnknown_SkAlphaType. > > I guess it might not be possible. The gif format stores the duration, the > disposal method, and the transparency in the graphics control extension. So we > couldn't fill in the other values while still not knowing the transparency. > > Even going byte-by-byte, the disposal method is stored in the same byte. The > duration are in the following 2 bytes. So if we have the duration, we must > already have the transparency information. > > Okay, yeah. I'll use SkAlphaTypeIsOpaque(). :) Unknown is really just a bogus value to use in e.g. a bitmap that has been reset(). https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:192: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/20 03:42:27, cblume wrote: > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > Isn't this taken care of by InitializeNewFrame? > > Not really. > The call to frame.AllocatePixelData() above essentially resets the alpha to > true. > So after calling that, we have to reset it back to what it *actually* is. > When you say it "essentially resets the alpha to true", what do you mean? The ImageFrame's alpha (has_alpha_) or the SkBitmap's alpha (fInfo.alphaType())? AllocatePixelData does not appear to modify has_alpha_[1]; it just sets the alphaType on the SkBitmap, and it always assume there is alpha, since we may not have decoded the full image. SetHasAlpha changes has_alpha_ (but I think in this case, you're setting it to the same value it already was). Then it will ComputeAlphaType and set that to the SkBitmap, but again, I think it will set it to the same value. It only makes it opaque if the Status is Complete. So I don't think this line is necessary. [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > I have a separate CR for this which I'll return to once I get more time: > https://codereview.chromium.org/2762643004/ It looks like that one affects ZeroFillPixelData. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:234: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/20 03:42:26, cblume wrote: > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > Again, I think this was handled by InitializeNewFrame? > > > > Maybe you reset it because of the SetHasAlpha(true) call below? Isn't that > > unnecessary because we always treat a partial image as having alpha? > > I reset it because of the SetHasAlpha(true) below, correct. > My thought was a partial image has alpha. > But once it becomes a full image, it may no longer have alpha. > > Should the image continue to have alpha even if the palette says there is none > only because it was partially decoded for a bit? Yes, that is the way we behave today, and I do not see any reason to change that. (And certainly not in this larger CL.) https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:73: size_t new_position = position; nit: Why not reuse the variable "position"? https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:128: codec_ = nullptr; Presumably codec_ is never null in this situation. If it were, we'd leak segment_stream_ https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:199: frame.SetStatus(ImageFrame::kFrameAllocated); Should this be set inside AllocatePixelData? https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:218: options.fHasPriorFrame = true; After this, what should the FrameStatus be? Looking down below, it looks like we want it to be kFrameAllocated, so we'll startIncrementalDecode, but it doesn't look like it is. (I think it will be FrameEmpty?) In the old code, InitFrameBuffer sets the status to FramePartial after it either (A) allocates and zeroes or (B) steals/copies from the prior frame. The interesting bit is that you've changed what Partial means - it used to mean that we've at least gotten into the starting state for decode (A or B, above), but now you're using it to mean that startIncrementalDecode succeeded. Presumably Allocated means A or B has happened (though we don't yet change it to Allocated in this case). Meanwhile, the other ImageDecoders still use the old meaning of Partial, and never go into the Allocated state. I don't know that this causes any direct problems, but it seems like an extra maintenance burden (due to confusion).
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h:389: // And gif is also treated differently, since it relies on SkCodec which On 2017/04/21 20:09:31, scroggo_chromium wrote: > On 2017/04/20 03:42:26, cblume wrote: > > On 2017/04/17 20:04:55, scroggo_chromium wrote: > > > IIUC, previously, GIF and APNG required at least kFramePartial, and WEBP > > > required kFrameComplete. With this change, GIF matches WEBP, and APNG is the > > > only one to use the default. > > > > > > What is different about using SkCodec that requires the frame to be complete > > > now? Or is it because with SkCodec the Status could now be kFrameAlloc? > > > > > > Can you write a test for this? > > > > Your understanding is correct. > > The way I understand it (which might be wrong) is that if I want to decode > frame > > X+1 (which relies on frame X), I will want to keep frame X around so we don't > > decode from the beginning to get it back. That means I need the complete frame > X > > when using SkCodec. > > > > However, the part I might not understand correctly is why gif didn't > previously > > require the full frame. I'm not sure why a partial frame would be sufficient > to > > decode future frames. > > It will help if you take a look at the two callsites, in ClearCacheExceptFrame. > If the client wants to clear all frames except X, we don't quite honor it, with > the thinking that the client will probably want to decode X+1 in the future. If > this method returns false for X, we'll *also* keep around the frame that X > depends on. > > The default is to compare the status against empty. If X is empty, clearly > keeping it in the cache will not help us decode X+1, so we hang on to the frame > that it depended on. > > Partial is enough to decode any frame that depends on X, since we've already > copied the frame that X depends on in order to decode X. > > I suspect you really want to make sure we're at least in the FramePartial > Status. If we've only allocated, then we haven't yet done the copy, so we need > to hang on to the frame X depends on in order to finish decoding X. (I just > looked back at the new Decode method. I've added a comment there, too, but now I > think you want to be in at least the FrameAllocated Status. Though you don't > change to that state when you copy the required frame, I believe that was your > intent. In which case the default implementation of this method should work > fine.) > > > > > I think we actually don't want to test this. It is a protected method. I > believe > > we want to test public interfaces. > > In general, yes, we want to test public interfaces, but you're changing a > behavior. How do we know it's for the better? Okay, I think I follow now. So I understood that when we clear frames, we might keep the current frame + the frame it depends on. I now understand that is because we may not be done decoding this frame. In my mind, we were done decoding this frame but maybe the disposal method is DisposePrevious. I guess it can be both cases. I just didn't think of the other. Anyway, I also now see how if we are at least partial, we know then we know the frame this depends on is both complete and still in cache. What threw me is gif (the format, not my patch) requires the required frame to be complete. Essentially, we might need to continue decoding frame |index| before it is actually sufficient for successors. It may actually not be sufficient yet. BUT we are promising that it *can* be sufficient (eventually) so long as we are partially decoded. So now that I understand that, 1.) I think I can just keep the old behavior, and 2.) I think this function should be renamed in a separate CL. :) https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:16: bool all_contents_received) { On 2017/04/21 20:09:31, scroggo_chromium wrote: > On 2017/04/20 03:42:26, cblume wrote: > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > It looks like this parameter is unused. > > > > Done. > > You've commented out the parameter, but why not remove it entirely if it's not > used? Oops. Thought it was part of an interface we were adhering to. Fixed. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:88: position_ = position; On 2017/04/21 20:09:31, scroggo_chromium wrote: > On 2017/04/20 03:42:26, cblume wrote: > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > It seems odd to me that we correct position if there is a reader_, but > > otherwise > > > we blindly use the input. Maybe that's okay though. > > > > I agree. > > We cannot correct without a reader. So my only other option would be to ignore > > the input. Given those two options I feel like blindly not correcting is the > > better move. > > Maybe add a comment? Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:95: if (IsAllDataReceived() && frame_infos_.size() == 1) On 2017/04/21 20:09:31, scroggo_chromium wrote: > On 2017/04/20 03:42:26, cblume wrote: > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > Could this be a problem if we have not parsed since all data was received? > > > > > > e.g. > > > 1. Receive the first frame (IsAllDataReceived == false) > > > 2. Parse available data (frame_infos_.size() == 1) > > > 3. Receive the rest of the data (including more frames) > > > 4. Call this method > > > > > > Then might we return kCAnimationNone, when the image actually is animated? > > > > I think I follow. > > But doesn't SkCodec parse the info when it is provided? So step 3 would imply > > we've parsed further? > > SkCodec will parse it when you call getRepetitionCount(), down below. I *think* > GetFrameCount() will always be called before RepetitionCount() in practice, but > it seems fragile to require it. > > > > > If not, I need to make sure we've parsed all we have when this function is > > called. That would mean calling one of SkCodec's functions which forces the > > parse, right? > > Yes. Okay, I follow. I rearranged the function to call getRepetitionCount() earlier and use it instead of frame_infos_.size() (which gets updated in a separate function). This makes things less fragile. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:131: if (!Failed() && !(segment_stream_ && segment_stream_->IsCleared())) On 2017/04/21 20:09:31, scroggo_chromium wrote: > On 2017/04/20 03:42:27, cblume wrote: > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > This seems to say that if segment_stream_ is null, we will call > getFrameInfo. > > I > > > would expect that we would not, but in that case I think codec_ will also be > > > null? > > > > Oops. The ! should have been in front of the IsCleared(). I accidentally > clumped > > the null check in with it. > > Thank you for catching this. > > > > However, segment_stream_ can be null when codec_ is non-null. That is what > > SegmentStream::IsCleared() is all about. > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > > I don't think so. If the SegmentReader is set to null, you don't set > segment_stream_ to null, you just call setReader(null). > > (segment_reader_ looks to only be null if you don't have a codec or if you've > already failed. In both cases you won't reach this check.) Ah. I misunderstood what you were saying. You are right, segment_stream_ will not be null in that case. segment_stream_.reader_ will be. But still, my mistake was putting the ! on the outside of the (). It needed to be in front of the IsCleared(). I've fixed this. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:146: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/21 20:09:31, scroggo_chromium wrote: > On 2017/04/20 03:42:26, cblume wrote: > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > Alternatively, this could be > > > > > > frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_infos_[index].fAlphaType)); > > > > Doing that would include kUnknown_SkAlphaType. > > I'm willing to do this if you prefer it. I avoided it because I was unsure if > it > > might (now or in the future) be possible for SkGifDecoder to return > > kUnknown_SkAlphaType. > > > > I guess it might not be possible. The gif format stores the duration, the > > disposal method, and the transparency in the graphics control extension. So we > > couldn't fill in the other values while still not knowing the transparency. > > > > Even going byte-by-byte, the disposal method is stored in the same byte. The > > duration are in the following 2 bytes. So if we have the duration, we must > > already have the transparency information. > > > > Okay, yeah. I'll use SkAlphaTypeIsOpaque(). :) > > Unknown is really just a bogus value to use in e.g. a bitmap that has been > reset(). Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:165: if (segment_stream_ && segment_stream_->IsCleared()) On 2017/04/17 20:04:56, scroggo_chromium wrote: > Again, I think if codec_ is non-null, segment_stream_ will also be non-null. Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:192: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/21 20:09:31, scroggo_chromium wrote: > On 2017/04/20 03:42:27, cblume wrote: > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > Isn't this taken care of by InitializeNewFrame? > > > > Not really. > > The call to frame.AllocatePixelData() above essentially resets the alpha to > > true. > > So after calling that, we have to reset it back to what it *actually* is. > > > > > When you say it "essentially resets the alpha to true", what do you mean? The > ImageFrame's alpha (has_alpha_) or the SkBitmap's alpha (fInfo.alphaType())? > > AllocatePixelData does not appear to modify has_alpha_[1]; it just sets the > alphaType on the SkBitmap, and it always assume there is alpha, since we may not > have decoded the full image. > > SetHasAlpha changes has_alpha_ (but I think in this case, you're setting it to > the same value it already was). Then it will ComputeAlphaType and set that to > the SkBitmap, but again, I think it will set it to the same value. It only makes > it opaque if the Status is Complete. > > So I don't think this line is necessary. > > [1] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > I have a separate CR for this which I'll return to once I get more time: > > https://codereview.chromium.org/2762643004/ > > It looks like that one affects ZeroFillPixelData. Correct. AllocatePixelData() does not modify has_alpha_. It does modify the SkBitmap's alpha, though. This odd mismatch is being addressed in a separate CL: https://codereview.chromium.org/2762643004/diff/40001/third_party/WebKit/Sour... But for now, I'm doing what the existing behavior is and what the other decoders do: explicitly call SetHasAlpha() after calling AllocatePixelData(). For example: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:234: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/21 20:09:31, scroggo_chromium wrote: > On 2017/04/20 03:42:26, cblume wrote: > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > Again, I think this was handled by InitializeNewFrame? > > > > > > Maybe you reset it because of the SetHasAlpha(true) call below? Isn't that > > > unnecessary because we always treat a partial image as having alpha? > > > > I reset it because of the SetHasAlpha(true) below, correct. > > My thought was a partial image has alpha. > > But once it becomes a full image, it may no longer have alpha. > > > > Should the image continue to have alpha even if the palette says there is none > > only because it was partially decoded for a bit? > > Yes, that is the way we behave today, and I do not see any reason to change > that. (And certainly not in this larger CL.) I'm not entirely sure I follow. Inside InitializeNewFrame() we set the frame alpha to what it should be. We then have to change it to .SetHasAlpha(true) (no matter what it was before) if we get a partial decode. But once the frame is done being decoded, we then have to set it back. I could remove the .SetHasAlpha() call inside InitializeNewFrame() since it is redundant. Is that what you mean? Oh, I think you mean that once a frame had a partial decode and became .SetHasAlpha(true), we leave it that way. We don't set it back to what it claimed to be. Is that what you mean? If so, I'll remove this line. I agree, let's not change that behavior if this isn't how it worked before. https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:73: size_t new_position = position; On 2017/04/21 20:09:32, scroggo_chromium wrote: > nit: Why not reuse the variable "position"? Done. https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:128: codec_ = nullptr; On 2017/04/21 20:09:32, scroggo_chromium wrote: > Presumably codec_ is never null in this situation. If it were, we'd leak > segment_stream_ I see what you're saying. Since this only happens when decoding fails, we could check if codec_ isn't null and then delete segment_stream_. Even if that scenario never happens, neither should calling SetFailed(). I am happy to favor on the side of defensive coding in case something changes later or I understand correctly and codec_ could be non-null. https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:199: frame.SetStatus(ImageFrame::kFrameAllocated); On 2017/04/21 20:09:32, scroggo_chromium wrote: > Should this be set inside AllocatePixelData? Mmmm maybe. I also need it in the else branch here (which does not call AllocatePixelData). I was missing the call to SetStatus(Allocated) in the else branch on this PS, which caused dependent frames to not call startIncrementalDecode. I've added it in a new PS. Anyway, if I leave it outside AllocatePixelData() then we have a nice balance between these two branches where the reader can see both set the status to allocated. If instead I move it inside AllocatePixelData() then the two branches are lopsided. Actually, since it happens no matter which branch is taken I should probably move it out of the if/else. I could still have it inside AllocatePixelData() and then just set the status twice for non-dependent frames. Which do you prefer? https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:218: options.fHasPriorFrame = true; On 2017/04/21 20:09:32, scroggo_chromium wrote: > After this, what should the FrameStatus be? Looking down below, it looks like we > want it to be kFrameAllocated, so we'll startIncrementalDecode, but it doesn't > look like it is. (I think it will be FrameEmpty?) > > In the old code, InitFrameBuffer sets the status to FramePartial after it either > (A) allocates and zeroes or (B) steals/copies from the prior frame. > > The interesting bit is that you've changed what Partial means - it used to mean > that we've at least gotten into the starting state for decode (A or B, above), > but now you're using it to mean that startIncrementalDecode succeeded. > Presumably Allocated means A or B has happened (though we don't yet change it to > Allocated in this case). Meanwhile, the other ImageDecoders still use the old > meaning of Partial, and never go into the Allocated state. I don't know that > this causes any direct problems, but it seems like an extra maintenance burden > (due to confusion). Hahaha. Good eye. That is exactly the bug I fixed on the last patch set. :) The status should have been set to allocated. I see what you mean about redefining Partial. Without updating all the other decoders, having two definitions is definitely a bad thing. Although, they may all already be using the new definition of Partial. The other coders couldn't possibly fail between allocating/stealing/copying and getting into the starting state for decode IIUC. So essentially, their status would change to Allocated and then immediately change to Partial. So we don't need to update them to add the superfluous Allocated status. It would be as if the other decoders got to skip calling startIncrementalDecode and thus got to skip the Allocated status. So the definition of Partial for all image decoders should be "In a state where we can decode the rest of the frame".
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_asan_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/1600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:99: if (IsAllDataReceived() && frame_count == 1) I would prefer to not make a needless copy of the vector above. But this method is const so I can't call DecodeFrameCount(). This will eventually be fixed when we no longer keep a local copy of the vector. So for now, I'm okay with this needless vector copy.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/ImageDecoder.h:389: // And gif is also treated differently, since it relies on SkCodec which On 2017/04/21 23:47:38, cblume wrote: > On 2017/04/21 20:09:31, scroggo_chromium wrote: > > On 2017/04/20 03:42:26, cblume wrote: > > > On 2017/04/17 20:04:55, scroggo_chromium wrote: > > > > IIUC, previously, GIF and APNG required at least kFramePartial, and WEBP > > > > required kFrameComplete. With this change, GIF matches WEBP, and APNG is > the > > > > only one to use the default. > > > > > > > > What is different about using SkCodec that requires the frame to be > complete > > > > now? Or is it because with SkCodec the Status could now be kFrameAlloc? > > > > > > > > Can you write a test for this? > > > > > > Your understanding is correct. > > > The way I understand it (which might be wrong) is that if I want to decode > > frame > > > X+1 (which relies on frame X), I will want to keep frame X around so we > don't > > > decode from the beginning to get it back. That means I need the complete > frame > > X > > > when using SkCodec. > > > > > > However, the part I might not understand correctly is why gif didn't > > previously > > > require the full frame. I'm not sure why a partial frame would be sufficient > > to > > > decode future frames. > > > > It will help if you take a look at the two callsites, in > ClearCacheExceptFrame. > > If the client wants to clear all frames except X, we don't quite honor it, > with > > the thinking that the client will probably want to decode X+1 in the future. > If > > this method returns false for X, we'll *also* keep around the frame that X > > depends on. > > > > The default is to compare the status against empty. If X is empty, clearly > > keeping it in the cache will not help us decode X+1, so we hang on to the > frame > > that it depended on. > > > > Partial is enough to decode any frame that depends on X, since we've already > > copied the frame that X depends on in order to decode X. > > > > I suspect you really want to make sure we're at least in the FramePartial > > Status. If we've only allocated, then we haven't yet done the copy, so we need > > to hang on to the frame X depends on in order to finish decoding X. (I just > > looked back at the new Decode method. I've added a comment there, too, but now > I > > think you want to be in at least the FrameAllocated Status. Though you don't > > change to that state when you copy the required frame, I believe that was your > > intent. In which case the default implementation of this method should work > > fine.) > > > > > > > > I think we actually don't want to test this. It is a protected method. I > > believe > > > we want to test public interfaces. > > > > In general, yes, we want to test public interfaces, but you're changing a > > behavior. How do we know it's for the better? > > Okay, I think I follow now. > > So I understood that when we clear frames, we might keep the current frame + the > frame it depends on. I now understand that is because we may not be done > decoding this frame. In my mind, we were done decoding this frame but maybe the > disposal method is DisposePrevious. I guess it can be both cases. I just didn't > think of the other. > > Anyway, I also now see how if we are at least partial, we know then we know the > frame this depends on is both complete and still in cache. I'm not sure that's the case. If this frame is partial, and *not* DisposePrevious, what we're saying is that if the following frame depends on this one, then this frame is decoded enough that there is no need to redecode its required frame in order to decode the following one. At least in theory, we could have evicted the frame that this one depends on (so its status is now Empty), and that does not affect the outcome here. > > > What threw me is gif (the format, not my patch) requires the required frame to > be complete. Essentially, we might need to continue decoding frame |index| > before it is actually sufficient for successors. It may actually not be > sufficient yet. BUT we are promising that it *can* be sufficient (eventually) so > long as we are partially decoded. > > > > So now that I understand that, 1.) I think I can just keep the old behavior, and Agreed. > 2.) I think this function should be renamed in a separate CL. :) Yeah, I can see the confusion. Maybe it's clearer if we flip the return value and call it something like NeedRequiredFrameToDecodeSuccessors and return status == kFrameEmpty in the default version. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:131: if (!Failed() && !(segment_stream_ && segment_stream_->IsCleared())) On 2017/04/21 23:47:38, cblume wrote: > On 2017/04/21 20:09:31, scroggo_chromium wrote: > > On 2017/04/20 03:42:27, cblume wrote: > > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > > This seems to say that if segment_stream_ is null, we will call > > getFrameInfo. > > > I > > > > would expect that we would not, but in that case I think codec_ will also > be > > > > null? > > > > > > Oops. The ! should have been in front of the IsCleared(). I accidentally > > clumped > > > the null check in with it. > > > Thank you for catching this. > > > > > > However, segment_stream_ can be null when codec_ is non-null. That is what > > > SegmentStream::IsCleared() is all about. > > > > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > > > > I don't think so. If the SegmentReader is set to null, you don't set > > segment_stream_ to null, you just call setReader(null). > > > > (segment_reader_ looks to only be null if you don't have a codec or if you've > > already failed. In both cases you won't reach this check.) > > Ah. I misunderstood what you were saying. > You are right, segment_stream_ will not be null in that case. > segment_stream_.reader_ will be. > > But still, my mistake was putting the ! on the outside of the (). It needed to > be in front of the IsCleared(). I've fixed this. I agree that that was wrong. But I still don't think you need to check for a null segment_stream_, since it will not be null if we are not in the Failed state. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:192: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/21 23:47:38, cblume wrote: > On 2017/04/21 20:09:31, scroggo_chromium wrote: > > On 2017/04/20 03:42:27, cblume wrote: > > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > > Isn't this taken care of by InitializeNewFrame? > > > > > > Not really. > > > The call to frame.AllocatePixelData() above essentially resets the alpha to > > > true. > > > So after calling that, we have to reset it back to what it *actually* is. > > > > > > > > > When you say it "essentially resets the alpha to true", what do you mean? The > > ImageFrame's alpha (has_alpha_) or the SkBitmap's alpha (fInfo.alphaType())? > > > > AllocatePixelData does not appear to modify has_alpha_[1]; it just sets the > > alphaType on the SkBitmap, and it always assume there is alpha, since we may > not > > have decoded the full image. > > > > SetHasAlpha changes has_alpha_ (but I think in this case, you're setting it to > > the same value it already was). Then it will ComputeAlphaType and set that to > > the SkBitmap, but again, I think it will set it to the same value. It only > makes > > it opaque if the Status is Complete. > > > > So I don't think this line is necessary. > > > > [1] > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > > I have a separate CR for this which I'll return to once I get more time: > > > https://codereview.chromium.org/2762643004/ > > > > It looks like that one affects ZeroFillPixelData. > > Correct. > AllocatePixelData() does not modify has_alpha_. It does modify the SkBitmap's > alpha, though. > This odd mismatch is being addressed in a separate CL: > https://codereview.chromium.org/2762643004/diff/40001/third_party/WebKit/Sour... > But for now, I'm doing what the existing behavior is and what the other decoders > do: explicitly call SetHasAlpha() after calling AllocatePixelData(). > For example: > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... FWIW, the line you point to in WEBP is *also* after ZeroFillPixelData, which *does* affect has_alpha_. That line is also unnecessary, IMO, since it doesn't change any behavior. Likewise, this line doesn't change any behavior, but it does add more noise to this CL. If you think it's necessary to be consistent with the other decoders (which already are not consistent), I would recommend landing your other change first to simplify this one. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:234: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/21 23:47:38, cblume wrote: > On 2017/04/21 20:09:31, scroggo_chromium wrote: > > On 2017/04/20 03:42:26, cblume wrote: > > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > > Again, I think this was handled by InitializeNewFrame? > > > > > > > > Maybe you reset it because of the SetHasAlpha(true) call below? Isn't that > > > > unnecessary because we always treat a partial image as having alpha? > > > > > > I reset it because of the SetHasAlpha(true) below, correct. > > > My thought was a partial image has alpha. > > > But once it becomes a full image, it may no longer have alpha. > > > > > > Should the image continue to have alpha even if the palette says there is > none > > > only because it was partially decoded for a bit? > > > > Yes, that is the way we behave today, and I do not see any reason to change > > that. (And certainly not in this larger CL.) > > I'm not entirely sure I follow. > > Inside InitializeNewFrame() we set the frame alpha to what it should be. Yes. > We then have to change it to .SetHasAlpha(true) (no matter what it was before) > if we get a partial decode. I disagree. ImageFrame already handles this for you. I propose that if the frame is reported to be opaque, you leave has_alpha_ as false. If the image is incomplete, the SkBitmap will continue to have a non-opaque alpha type. The only difference will be that ImageFrame::HasAlpha() will return false, but no clients call that while the frame is incomplete. > But once the frame is done being decoded, we then have to set it back. > > I could remove the .SetHasAlpha() call inside InitializeNewFrame() since it is > redundant. Is that what you mean? No. > > > Oh, I think you mean that once a frame had a partial decode and became > .SetHasAlpha(true), we leave it that way. We don't set it back to what it > claimed to be. Is that what you mean? If so, I'll remove this line. I agree, > let's not change that behavior if this isn't how it worked before. No, this is also not what I mean. https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:128: codec_ = nullptr; On 2017/04/21 23:47:38, cblume wrote: > On 2017/04/21 20:09:32, scroggo_chromium wrote: > > Presumably codec_ is never null in this situation. If it were, we'd leak > > segment_stream_ > > I see what you're saying. > Since this only happens when decoding fails, we could check if codec_ isn't null > and then delete segment_stream_. Even if that scenario never happens, neither > should calling SetFailed(). > I am happy to favor on the side of defensive coding in case something changes > later or I understand correctly and codec_ could be non-null. DCHECK(codec_); should be a good defense. https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:199: frame.SetStatus(ImageFrame::kFrameAllocated); On 2017/04/21 23:47:38, cblume wrote: > On 2017/04/21 20:09:32, scroggo_chromium wrote: > > Should this be set inside AllocatePixelData? > > Mmmm maybe. I also need it in the else branch here (which does not call > AllocatePixelData). > > I was missing the call to SetStatus(Allocated) in the else branch on this PS, > which caused dependent frames to not call startIncrementalDecode. I've added it > in a new PS. > > Anyway, if I leave it outside AllocatePixelData() then we have a nice balance > between these two branches where the reader can see both set the status to > allocated. If instead I move it inside AllocatePixelData() then the two branches > are lopsided. > > Actually, since it happens no matter which branch is taken I should probably > move it out of the if/else. I could still have it inside AllocatePixelData() and > then just set the status twice for non-dependent frames. > > Which do you prefer? Another option - change the status to Allocated inside the other methods that make the Frame have an allocation (TakeBitmapDataIfWritable and CopyBitmapData). Currently it looks like those two stick with FrameEmpty (although the caller follows up by setting it to Partial). https://codereview.chromium.org/2565323003/diff/1600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:83: if (!codec_ || Failed()) SetFailed deletes codec_, so I don't think the second branch will ever be reached. How about if (!codec_) return kCAnimationLoopOnce; DCHECK(!Failed()); https://codereview.chromium.org/2565323003/diff/1600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:99: if (IsAllDataReceived() && frame_count == 1) On 2017/04/23 23:13:53, cblume wrote: > I would prefer to not make a needless copy of the vector above. > But this method is const so I can't call DecodeFrameCount(). > > This will eventually be fixed when we no longer keep a local copy of the vector. > So for now, I'm okay with this needless vector copy. You can call codec->getFrameCount() to avoid the copy.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:131: if (!Failed() && !(segment_stream_ && segment_stream_->IsCleared())) On 2017/04/24 15:06:53, scroggo_chromium wrote: > On 2017/04/21 23:47:38, cblume wrote: > > On 2017/04/21 20:09:31, scroggo_chromium wrote: > > > On 2017/04/20 03:42:27, cblume wrote: > > > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > > > This seems to say that if segment_stream_ is null, we will call > > > getFrameInfo. > > > > I > > > > > would expect that we would not, but in that case I think codec_ will > also > > be > > > > > null? > > > > > > > > Oops. The ! should have been in front of the IsCleared(). I accidentally > > > clumped > > > > the null check in with it. > > > > Thank you for catching this. > > > > > > > > However, segment_stream_ can be null when codec_ is non-null. That is what > > > > SegmentStream::IsCleared() is all about. > > > > > > > > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > > > > > > I don't think so. If the SegmentReader is set to null, you don't set > > > segment_stream_ to null, you just call setReader(null). > > > > > > (segment_reader_ looks to only be null if you don't have a codec or if > you've > > > already failed. In both cases you won't reach this check.) > > > > Ah. I misunderstood what you were saying. > > You are right, segment_stream_ will not be null in that case. > > segment_stream_.reader_ will be. > > > > But still, my mistake was putting the ! on the outside of the (). It needed to > > be in front of the IsCleared(). I've fixed this. > > I agree that that was wrong. But I still don't think you need to check for a > null segment_stream_, since it will not be null if we are not in the Failed > state. Done. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:192: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/24 15:06:53, scroggo_chromium wrote: > On 2017/04/21 23:47:38, cblume wrote: > > On 2017/04/21 20:09:31, scroggo_chromium wrote: > > > On 2017/04/20 03:42:27, cblume wrote: > > > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > > > Isn't this taken care of by InitializeNewFrame? > > > > > > > > Not really. > > > > The call to frame.AllocatePixelData() above essentially resets the alpha > to > > > > true. > > > > So after calling that, we have to reset it back to what it *actually* is. > > > > > > > > > > > > > When you say it "essentially resets the alpha to true", what do you mean? > The > > > ImageFrame's alpha (has_alpha_) or the SkBitmap's alpha (fInfo.alphaType())? > > > > > > AllocatePixelData does not appear to modify has_alpha_[1]; it just sets the > > > alphaType on the SkBitmap, and it always assume there is alpha, since we may > > not > > > have decoded the full image. > > > > > > SetHasAlpha changes has_alpha_ (but I think in this case, you're setting it > to > > > the same value it already was). Then it will ComputeAlphaType and set that > to > > > the SkBitmap, but again, I think it will set it to the same value. It only > > makes > > > it opaque if the Status is Complete. > > > > > > So I don't think this line is necessary. > > > > > > [1] > > > > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > > > > I have a separate CR for this which I'll return to once I get more time: > > > > https://codereview.chromium.org/2762643004/ > > > > > > It looks like that one affects ZeroFillPixelData. > > > > Correct. > > AllocatePixelData() does not modify has_alpha_. It does modify the SkBitmap's > > alpha, though. > > This odd mismatch is being addressed in a separate CL: > > > https://codereview.chromium.org/2762643004/diff/40001/third_party/WebKit/Sour... > > But for now, I'm doing what the existing behavior is and what the other > decoders > > do: explicitly call SetHasAlpha() after calling AllocatePixelData(). > > For example: > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > FWIW, the line you point to in WEBP is *also* after ZeroFillPixelData, which > *does* affect has_alpha_. That line is also unnecessary, IMO, since it doesn't > change any behavior. > > Likewise, this line doesn't change any behavior, but it does add more noise to > this CL. If you think it's necessary to be consistent with the other decoders > (which already are not consistent), I would recommend landing your other change > first to simplify this one. Oh, right. So I also have another CL to separate zeroing from setting the alpha. But that hasn't landed yet either. https://codereview.chromium.org/2762643004/ I'll get rid of this .setHasAlpha(). We can put it back when those CLs land and make it necessary. But you are right -- until then, it is noise. Be advised that frame.ZeroFillFrameRect() calls .SetHasAlpha(true); so I need to set it back right after. https://codereview.chromium.org/2565323003/diff/1400001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:234: frame.SetHasAlpha(frame_infos_[index].fAlphaType == kPremul_SkAlphaType || On 2017/04/24 15:06:53, scroggo_chromium wrote: > On 2017/04/21 23:47:38, cblume wrote: > > On 2017/04/21 20:09:31, scroggo_chromium wrote: > > > On 2017/04/20 03:42:26, cblume wrote: > > > > On 2017/04/17 20:04:56, scroggo_chromium wrote: > > > > > Again, I think this was handled by InitializeNewFrame? > > > > > > > > > > Maybe you reset it because of the SetHasAlpha(true) call below? Isn't > that > > > > > unnecessary because we always treat a partial image as having alpha? > > > > > > > > I reset it because of the SetHasAlpha(true) below, correct. > > > > My thought was a partial image has alpha. > > > > But once it becomes a full image, it may no longer have alpha. > > > > > > > > Should the image continue to have alpha even if the palette says there is > > none > > > > only because it was partially decoded for a bit? > > > > > > Yes, that is the way we behave today, and I do not see any reason to change > > > that. (And certainly not in this larger CL.) > > > > I'm not entirely sure I follow. > > > > Inside InitializeNewFrame() we set the frame alpha to what it should be. > > Yes. > > > We then have to change it to .SetHasAlpha(true) (no matter what it was before) > > if we get a partial decode. > > I disagree. ImageFrame already handles this for you. I propose that if the frame > is reported to be opaque, you leave has_alpha_ as false. If the image is > incomplete, the SkBitmap will continue to have a non-opaque alpha type. > > The only difference will be that ImageFrame::HasAlpha() will return false, but > no clients call that while the frame is incomplete. Okay. The way I understand what you are saying is there will deliberately be a difference between has_alpha_ and the SkBitmap's alpha. has_alpha_ will be what the image reports. It does not change during a partial decode. SkBitmap's alpha starts as true but ImageFrame::SetStatus() updates it once it hits FrameComplete. I see now. Done. https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:128: codec_ = nullptr; On 2017/04/24 15:06:54, scroggo_chromium wrote: > On 2017/04/21 23:47:38, cblume wrote: > > On 2017/04/21 20:09:32, scroggo_chromium wrote: > > > Presumably codec_ is never null in this situation. If it were, we'd leak > > > segment_stream_ > > > > I see what you're saying. > > Since this only happens when decoding fails, we could check if codec_ isn't > null > > and then delete segment_stream_. Even if that scenario never happens, neither > > should calling SetFailed(). > > I am happy to favor on the side of defensive coding in case something changes > > later or I understand correctly and codec_ could be non-null. > > DCHECK(codec_); > > should be a good defense. Done. https://codereview.chromium.org/2565323003/diff/1500001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:199: frame.SetStatus(ImageFrame::kFrameAllocated); On 2017/04/24 15:06:54, scroggo_chromium wrote: > On 2017/04/21 23:47:38, cblume wrote: > > On 2017/04/21 20:09:32, scroggo_chromium wrote: > > > Should this be set inside AllocatePixelData? > > > > Mmmm maybe. I also need it in the else branch here (which does not call > > AllocatePixelData). > > > > I was missing the call to SetStatus(Allocated) in the else branch on this PS, > > which caused dependent frames to not call startIncrementalDecode. I've added > it > > in a new PS. > > > > Anyway, if I leave it outside AllocatePixelData() then we have a nice balance > > between these two branches where the reader can see both set the status to > > allocated. If instead I move it inside AllocatePixelData() then the two > branches > > are lopsided. > > > > Actually, since it happens no matter which branch is taken I should probably > > move it out of the if/else. I could still have it inside AllocatePixelData() > and > > then just set the status twice for non-dependent frames. > > > > Which do you prefer? > > Another option - change the status to Allocated inside the other methods that > make the Frame have an allocation (TakeBitmapDataIfWritable and CopyBitmapData). > Currently it looks like those two stick with FrameEmpty (although the caller > follows up by setting it to Partial). Done. https://codereview.chromium.org/2565323003/diff/1600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:83: if (!codec_ || Failed()) On 2017/04/24 15:06:54, scroggo_chromium wrote: > SetFailed deletes codec_, so I don't think the second branch will ever be > reached. > > How about > > if (!codec_) > return kCAnimationLoopOnce; > > DCHECK(!Failed()); Done. https://codereview.chromium.org/2565323003/diff/1600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:99: if (IsAllDataReceived() && frame_count == 1) On 2017/04/24 15:06:54, scroggo_chromium wrote: > On 2017/04/23 23:13:53, cblume wrote: > > I would prefer to not make a needless copy of the vector above. > > But this method is const so I can't call DecodeFrameCount(). > > > > This will eventually be fixed when we no longer keep a local copy of the > vector. > > So for now, I'm okay with this needless vector copy. > > You can call codec->getFrameCount() to avoid the copy. Done.
https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:75: // Given those two options, blindly following sounds better. What would happen if we blindly followed in the case where there is a reader? I think peek/read will already properly fail silently if position_ is beyond the end of reader_->size(). What if we went through the following: - seek(position) (where position > reader_->size()) - setReader(reader) (where the new reader's size() > position_, but < the previously supplied position) Then we want the previously supplied position, right? (Maybe this cannot happen anyway?) https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:89: std::min(static_cast<size_t>(absolute_position), reader_->size()); Should this one check for a null reader_, too? Oh, I was going to suggest that this relied on UpdateInternals to handle the case where the position is > reader_->size(), but UpdateInternals behaves differently from this one. Why? https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:63: // If we don't have a SkCodec yet, create one from the stream nit: This comment is probably not necessary. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:151: // the frame rect, we set the frame rect to be the image's full size. Maybe add a comment that the original frame rect is not used anyway? https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:174: if (!codec_) This seems redundant. We cannot have a codec if we have Failed. How about: void GIFImageDecoder::Decode(size_t index) { if (!codec) return; DCHECK(!Failed()); ... https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:181: // It is a fatal error if all data is received and we have decoded all It surprised me that this got moved up front. It seems like we could know there is a failure at this point, but this will also change behavior, won't it? Previously we would have decoded frames that |index| depends on, and now we won't. But I'm not sure that will have an effect on what the user sees. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:247: PostDecodeProcessing(index); Should we check the return value? (At least to assert?) https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:259: frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_infos_[index].fAlphaType)); Maybe add a comment that ZeroFillFrameRect changed has_alpha_ to true, but we want to leave it whatever it was set by InitializeNewFrame? https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:64: decoder->FrameBufferAtIndex(0); Are you sure this is necessary? https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:361: EXPECT_EQ(kPremul_SkAlphaType, premul_frame->Bitmap().alphaType()); Did this need to change? (Did git cl format change it?)
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:75: // Given those two options, blindly following sounds better. On 2017/04/24 20:25:07, scroggo_chromium wrote: > What would happen if we blindly followed in the case where there is a reader? I > think peek/read will already properly fail silently if position_ is beyond the > end of reader_->size(). > > What if we went through the following: > > - seek(position) (where position > reader_->size()) > - setReader(reader) (where the new reader's size() > position_, but < the > previously supplied position) > > Then we want the previously supplied position, right? > > (Maybe this cannot happen anyway?) I like your idea. That would simplify things a lot. I'll try it out. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:89: std::min(static_cast<size_t>(absolute_position), reader_->size()); On 2017/04/24 20:25:07, scroggo_chromium wrote: > Should this one check for a null reader_, too? > > Oh, I was going to suggest that this relied on UpdateInternals to handle the > case where the position is > reader_->size(), but UpdateInternals behaves > differently from this one. Why? I don't think there is a good reason why. It is probably a mistake. I like your idea of allowing going past the end without caring about reader_ existing or not. I'll update here, too. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:63: // If we don't have a SkCodec yet, create one from the stream On 2017/04/24 20:25:07, scroggo_chromium wrote: > nit: This comment is probably not necessary. Done. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:151: // the frame rect, we set the frame rect to be the image's full size. On 2017/04/24 20:25:07, scroggo_chromium wrote: > Maybe add a comment that the original frame rect is not used anyway? Done. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:174: if (!codec_) On 2017/04/24 20:25:07, scroggo_chromium wrote: > This seems redundant. We cannot have a codec if we have Failed. How about: > > void GIFImageDecoder::Decode(size_t index) { > if (!codec) > return; > DCHECK(!Failed()); > ... Done. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:181: // It is a fatal error if all data is received and we have decoded all On 2017/04/24 20:25:07, scroggo_chromium wrote: > It surprised me that this got moved up front. It seems like we could know there > is a failure at this point, but this will also change behavior, won't it? > Previously we would have decoded frames that |index| depends on, and now we > won't. But I'm not sure that will have an effect on what the user sees. I think I see what you're saying. Actually, this branch should never be entered anyway, right? We won't call Decode(index) without first learning that there are more frames and increasing the size of frame_buffer_cache_. As is, I think this whole branch is useless. The original branch checked if we're within known index bounds, all data is received, but the parse still hasn't completed. That last bit is the part that matters here. If we have all the data but the parse is incomplete then the file is truncated. That is the case we should be handling here. That is also the case we handle down below, inside the switch for incrementalDecode()'s result. So I think I should just delete this branch. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:247: PostDecodeProcessing(index); On 2017/04/24 20:25:07, scroggo_chromium wrote: > Should we check the return value? (At least to assert?) I think we might not need that. The returned bool only indicates whether the frame was fully decoded. It doesn't indicate whether PostDecodeProcessing() succeeded or failed. And we already know the frame was fully decoded. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:259: frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_infos_[index].fAlphaType)); On 2017/04/24 20:25:07, scroggo_chromium wrote: > Maybe add a comment that ZeroFillFrameRect changed has_alpha_ to true, but we > want to leave it whatever it was set by InitializeNewFrame? Done. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:64: decoder->FrameBufferAtIndex(0); On 2017/04/24 20:25:07, scroggo_chromium wrote: > Are you sure this is necessary? I guess it isn't, huh. RepetitionCount() will end up forcing the parse. I'll remove it. https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:361: EXPECT_EQ(kPremul_SkAlphaType, premul_frame->Bitmap().alphaType()); On 2017/04/24 20:25:07, scroggo_chromium wrote: > Did this need to change? (Did git cl format change it?) It didn't have to change. I can put it in a separate CL. While debugging just now I noticed it printed out like this: expected: premul_frame->Bitmap().alphaType() value: 0 actually was: kPremul_SkAlphaType value: 1
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:181: // It is a fatal error if all data is received and we have decoded all On 2017/04/24 21:35:53, cblume wrote: > On 2017/04/24 20:25:07, scroggo_chromium wrote: > > It surprised me that this got moved up front. It seems like we could know > there > > is a failure at this point, but this will also change behavior, won't it? > > Previously we would have decoded frames that |index| depends on, and now we > > won't. But I'm not sure that will have an effect on what the user sees. > > I think I see what you're saying. > Actually, this branch should never be entered anyway, right? We won't call > Decode(index) without first learning that there are more frames and increasing > the size of frame_buffer_cache_. Agreed. > As is, I think this whole branch is useless. Maybe there should be a DCHECK(index < frame_buffer_cache_.size()) ? (Or I think there is a LessThan variant of DCHECK.) > > The original branch checked if we're within known index bounds, all data is > received, but the parse still hasn't completed. That last bit is the part that > matters here. > If we have all the data but the parse is incomplete then the file is truncated. > That is the case we should be handling here. > > That is also the case we handle down below, inside the switch for > incrementalDecode()'s result. > > So I think I should just delete this branch. sgtm https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:33: position_ += bytes_advanced; UpdateInternals also updates the position. Why not make this UpdateInternals(position_ + bytes_advanced); ? https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:79: // clamp inside the bounds of the buffer size This comment looks to be out of date. Might be unnecessary anyway https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:85: void SegmentStream::UpdateInternals(size_t new_position) { "UpdateInternals" sounds vague to me. What if we called it something like SetPosition(Internal) Seek(Internal) It is used to set the position (and update the other fields that depend on the position). The one case where we're not modifying the position is SetReader. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:44: // We want to keep the buffer alive for the same duration as the SegmentStream I'm not sure this comment is necessary. I guess it's the motivation for this struct? Maybe a clearer comment is "This struct ties the lifetimes of its buffer and segment_stream"? But I don't think you need to, anyway. You use the buffer to create a SharedBuffer (which copies it), and then you never use it again. So I don't think you need this struct at all. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:46: ReadyToReadSegmentStream() = default; I think this would be clearer if you keep the defaults together and the deletes together. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:56: void CreateReadableSegmentStream(ReadyToReadSegmentStream& readable_stream); Many of the above methods are pretty clear about what they do, but this one is a mystery to me. Maybe do one or more of the following? - put the definition here - add a comment - better name - It is called "Create", but it doesn't create anything (it modifies a passed in object) - does it put a particular amount into the buffer? https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:93: TEST(SegmentStreamTest, SetReaderShouldUnsetIsAtEnd) { "SetReaderShouldUnsetIsAtEnd" seems misleading - it only unsets IsAtEnd because the position was 0 and the reader is non-null with a size > 0 (I assume). https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:123: TEST(SegmentStreamTest, SetReaderShouldSetIsAtEndWhenSetToNull) { Some of these tests seem like they could be combined. e.g. SetReaderShouldUnsetIsAtEnd is the same as the first ASSERT here. Do we really need both? I would imagine the argument for having both is that each test should test only one thing? And this test only duplicates the first one because it sets up (and verifies) the state for the actual thing it wants to test? As a counter-argument, separating them means - duplicated code - more code to look through - harder (at least IMO) to verify that all things were tested I don't necessarily think all the SegmentStreamTests should be combined into one, but maybe this would be better with one Test that covers isAtEnd? TEST(SegmentStreamTest, IsAtEnd) { ReadyToReadSegmentStream readable_stream; ASSERT_TRUE(IsAtEnd(readable_stream.segment_stream)); // DefaultConstructorShouldSetIsAtEnd CreateReadableSegmentStream(readable_stream); ASSERT_FALSE(IsAtEnd(readable_stream.segment_stream)); // SetReaderShouldUnsetIsAtEnd readable_stream.segment_stream.SetReader(nullptr); ASSERT_TRUE(IsAtEnd(readable_stream.segment_stream)); // This one ... // ReadShouldUpdateIsAtEnd, etc } Then we can find all the IsAtEnd tests and quickly see if we've missed something. (Or have I missed some testing guideline?) https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:139: ASSERT_EQ(amount_read, kInsideBufferPosition); I think the expected value goes first (isn't that why you changed the order of https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... ?) https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:160: const auto read_buffer = WTF::MakeUnique<char[]>(kInsideBufferPosition); Why not put this on the stack? char read_buffer[kInsideBufferPosition]; Same for others. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:257: TEST(SegmentStreamTest, PeekShouldCconsumeBuffer) { Consume* (not Cconsume) https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:580: readable_stream.buffer = WTF::MakeUnique<char[]>(kBufferAllocationSize); As I mention above, you never use this buffer again. So you can just put it on the stack. i.e. struct ReadyToReadSegmentStream { ... char buffer[kBufferAllocationSize]; ... }; https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:582: auto shared_buffer = blink::SharedBuffer::Create(readable_stream.buffer.get(), I've been advised not to use auto here, since this method returns a PassRefPtr [1]. (Or is this okay because you're always going to move it immediately?) [1] https://codereview.chromium.org/2754003008/diff/1/third_party/WebKit/Source/p... https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:584: auto segment_reader = Same concern about auto here. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:586: readable_stream.segment_stream.SetReader(segment_reader.Get()); This makes me wonder if SetReader should take a PassRefPtr. I'm guessing it takes a raw pointer because onSetData does?
https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:181: // It is a fatal error if all data is received and we have decoded all On 2017/04/25 15:31:40, scroggo_chromium wrote: > On 2017/04/24 21:35:53, cblume wrote: > > On 2017/04/24 20:25:07, scroggo_chromium wrote: > > > It surprised me that this got moved up front. It seems like we could know > > there > > > is a failure at this point, but this will also change behavior, won't it? > > > Previously we would have decoded frames that |index| depends on, and now we > > > won't. But I'm not sure that will have an effect on what the user sees. > > > > I think I see what you're saying. > > Actually, this branch should never be entered anyway, right? We won't call > > Decode(index) without first learning that there are more frames and increasing > > the size of frame_buffer_cache_. > > Agreed. > > > As is, I think this whole branch is useless. > > Maybe there should be a DCHECK(index < frame_buffer_cache_.size()) ? (Or I think > there is a LessThan variant of DCHECK.) > > > > > The original branch checked if we're within known index bounds, all data is > > received, but the parse still hasn't completed. That last bit is the part that > > matters here. > > If we have all the data but the parse is incomplete then the file is > truncated. > > That is the case we should be handling here. > > > > That is also the case we handle down below, inside the switch for > > incrementalDecode()'s result. > > > > So I think I should just delete this branch. > > sgtm Done. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:33: position_ += bytes_advanced; On 2017/04/25 15:31:40, scroggo_chromium wrote: > UpdateInternals also updates the position. Why not make this > > UpdateInternals(position_ + bytes_advanced); > > ? Done. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:79: // clamp inside the bounds of the buffer size On 2017/04/25 15:31:40, scroggo_chromium wrote: > This comment looks to be out of date. Might be unnecessary anyway Done. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:85: void SegmentStream::UpdateInternals(size_t new_position) { On 2017/04/25 15:31:40, scroggo_chromium wrote: > "UpdateInternals" sounds vague to me. What if we called it something like > > SetPosition(Internal) > Seek(Internal) > > It is used to set the position (and update the other fields that depend on the > position). The one case where we're not modifying the position is SetReader. I agree that there could be a better name. I am tempted to name it something like OnPositionChanged() but that sounds a bit like it is a callback. I'll go with UpdatePositionState(). https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:44: // We want to keep the buffer alive for the same duration as the SegmentStream On 2017/04/25 15:31:41, scroggo_chromium wrote: > I'm not sure this comment is necessary. I guess it's the motivation for this > struct? Maybe a clearer comment is > > "This struct ties the lifetimes of its buffer and segment_stream"? > > But I don't think you need to, anyway. You use the buffer to create a > SharedBuffer (which copies it), and then you never use it again. So I don't > think you need this struct at all. You are right. I didn't realize SharedBuffer copied the buffer contents. I wish it didn't. I would prefer if it was like a rope and the owner of the original buffers had to keep their lifetime in check. Oh well. Anyway, now that I realize it copies the buffer contents you are right that we don't need this type. This will simplify things a bunch. I'll go through and clean it up. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:56: void CreateReadableSegmentStream(ReadyToReadSegmentStream& readable_stream); On 2017/04/25 15:31:40, scroggo_chromium wrote: > Many of the above methods are pretty clear about what they do, but this one is a > mystery to me. Maybe do one or more of the following? > > - put the definition here > - add a comment > - better name > - It is called "Create", but it doesn't create anything (it modifies a passed > in object) > - does it put a particular amount into the buffer? Done. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:93: TEST(SegmentStreamTest, SetReaderShouldUnsetIsAtEnd) { On 2017/04/25 15:31:41, scroggo_chromium wrote: > "SetReaderShouldUnsetIsAtEnd" seems misleading - it only unsets IsAtEnd because > the position was 0 and the reader is non-null with a size > 0 (I assume). I think the position being 0 doesn't matter. But you have a really good point about the size > 0. The Google style is to name test functions like: <function being tested>Should<expected behavior> When there is a special case for that function, the name is followed by: When<special condition> https://engdoc.corp.google.com/eng/doc/devguide/coding/testing/unit-testing-b... Below here, you will see: SetReaderShouldSetIsAtEndWhenSetToNull this function ^^ is the special case of the function we are commenting on here. The test we are commenting on here should handle the scenario where there is a typical reader being set. A position of 0 should be fine. If we do position 10 & reader size 20 we'll want call to SetReader after the position is set. But they should both be testing the same case. Reader size > 0 is interesting though. If we set the position to 20 and then call SetReader for the first time with a buffer of size 5 then we are still at the end. I'll add a special case test for this. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:123: TEST(SegmentStreamTest, SetReaderShouldSetIsAtEndWhenSetToNull) { On 2017/04/25 15:31:40, scroggo_chromium wrote: > Some of these tests seem like they could be combined. e.g. > SetReaderShouldUnsetIsAtEnd is the same as the first ASSERT here. Do we really > need both? I completely agree. Ideally, only one test fails when one thing goes wrong. That way the test can point directly to the problem. So you are right about why I did this. What I *wish* we had is a way for one test to depend on another test. For example, if test A failed don't even run test B because test B needs to be able to assume A's behavior works. Unfortunately, that isn't what we have. So my idealist separation can't provide that benefit. I actually started writing changes to gtest but couldn't find an email group to see if they would accept it or if I had missed something. What I wanted to do was create a sort of ASSUME_TRUE(OtherTest(segment_stream)); which would leave the segment_stream in the state I want. I think there might be a happy medium here. I could probably create OtherTestHelper() so we reduce code duplication & still have any given test failure point to the correct reason for failure. There would be multiple test failures when 1 bad change happens. But at least they would all point to the same reason. Additionally, an alternative that follows your idea is to have 1 SetReader with multiple tests IsAtEnd, IsCleared, ... That would probably fit better since the setup would be the same for all of those. And since the idealistic one-test-per-failure-condition can't happen, might as well make it easy to read. I'll start with this last idea (similar to yours) and pull out setup where I can. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:139: ASSERT_EQ(amount_read, kInsideBufferPosition); On 2017/04/25 15:31:41, scroggo_chromium wrote: > I think the expected value goes first (isn't that why you changed the order of > https://codereview.chromium.org/2565323003/diff/1620001/third_party/WebKit/So... > ?) Oops, yeah. I caused this test to fail to see the results: Value of: kInsideBufferPosition Actual: 10 Expected: amount_read - 1 Which is: 9 I'll go make sure the expected value comes first on all these tests. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:160: const auto read_buffer = WTF::MakeUnique<char[]>(kInsideBufferPosition); On 2017/04/25 15:31:40, scroggo_chromium wrote: > Why not put this on the stack? > > char read_buffer[kInsideBufferPosition]; > > Same for others. Done. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:257: TEST(SegmentStreamTest, PeekShouldCconsumeBuffer) { On 2017/04/25 15:31:40, scroggo_chromium wrote: > Consume* (not Cconsume) Thank you https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:580: readable_stream.buffer = WTF::MakeUnique<char[]>(kBufferAllocationSize); On 2017/04/25 15:31:40, scroggo_chromium wrote: > As I mention above, you never use this buffer again. So you can just put it on > the stack. i.e. > > struct ReadyToReadSegmentStream { > ... > char buffer[kBufferAllocationSize]; > ... > }; Done. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:582: auto shared_buffer = blink::SharedBuffer::Create(readable_stream.buffer.get(), On 2017/04/25 15:31:41, scroggo_chromium wrote: > I've been advised not to use auto here, since this method returns a PassRefPtr > [1]. (Or is this okay because you're always going to move it immediately?) > > [1] > https://codereview.chromium.org/2754003008/diff/1/third_party/WebKit/Source/p... Done. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:584: auto segment_reader = On 2017/04/25 15:31:40, scroggo_chromium wrote: > Same concern about auto here. Done. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:586: readable_stream.segment_stream.SetReader(segment_reader.Get()); On 2017/04/25 15:31:40, scroggo_chromium wrote: > This makes me wonder if SetReader should take a PassRefPtr. I'm guessing it > takes a raw pointer because onSetData does? Correct. I used a raw pointer because that is what I was given. We aren't actually given ownership, either. So we can't just make a RefPtr out of it. We are only given a view into someone else's RefPtr. However, that is probably something we could change in a separate CL. The base class ImageDecoder::SetData() takes a PassRefPtr, stores it locally, and then calls OnSetData() with a raw pointer view into the pointer it owns. So from within the derived image decoders, we cannot create a ref count -- it will be deleted twice with two separate ref counts. I suspect it could continue to pass ownership on to OnSetData() though. But what *could* happen is create a protected RefPtr<SegmentReader> ImageDecoder::GetData() { return data_; }
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...)
https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:123: TEST(SegmentStreamTest, SetReaderShouldSetIsAtEndWhenSetToNull) { On 2017/04/26 10:09:44, cblume wrote: > On 2017/04/25 15:31:40, scroggo_chromium wrote: > > Some of these tests seem like they could be combined. e.g. > > SetReaderShouldUnsetIsAtEnd is the same as the first ASSERT here. Do we really > > need both? > > I completely agree. > Ideally, only one test fails when one thing goes wrong. That way the test can > point directly to the problem. So you are right about why I did this. > > What I *wish* we had is a way for one test to depend on another test. For > example, if test A failed don't even run test B because test B needs to be able > to assume A's behavior works. Unfortunately, that isn't what we have. So my > idealist separation can't provide that benefit. > > I actually started writing changes to gtest but couldn't find an email group to > see if they would accept it or if I had missed something. What I wanted to do > was create a sort of ASSUME_TRUE(OtherTest(segment_stream)); which would leave > the segment_stream in the state I want. > > I think there might be a happy medium here. I could probably create > OtherTestHelper() so we reduce code duplication & still have any given test > failure point to the correct reason for failure. There would be multiple test > failures when 1 bad change happens. But at least they would all point to the > same reason. > > Additionally, an alternative that follows your idea is to have 1 SetReader with > multiple tests IsAtEnd, IsCleared, ... > That would probably fit better since the setup would be the same for all of > those. And since the idealistic one-test-per-failure-condition can't happen, > might as well make it easy to read. > > I'll start with this last idea (similar to yours) and pull out setup where I > can. I began working on this and realized it is not what we want. Having 1 test that checks multiple behaviors (since the setup is the same) violates a few things. For one, it makes it hard to follow the naming guidelines. I could name the test SetReader...HasCorrectBehavior?? But more clearly, from https://engdoc.corp.google.com/eng/doc/devguide/coding/testing/unit-testing-b... adding new behavior should mean not needing to change any existing tests. It specifically calls out this example where one method tests multiple behaviors. Check the second paragraph. So I think we want to keep this long, exhaustive list. But I think I can still refactor it a bit to reuse the common setup. That should make things easier. And I might be able to do it in a way that clearly states "This test failed when trying to do the setup. The real failure is over there." I'll work on this now.
https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:85: void SegmentStream::UpdateInternals(size_t new_position) { On 2017/04/26 10:09:42, cblume wrote: > On 2017/04/25 15:31:40, scroggo_chromium wrote: > > "UpdateInternals" sounds vague to me. What if we called it something like > > > > SetPosition(Internal) > > Seek(Internal) > > > > It is used to set the position (and update the other fields that depend on the > > position). The one case where we're not modifying the position is SetReader. > > I agree that there could be a better name. > > I am tempted to name it something like OnPositionChanged() but that sounds a bit > like it is a callback. > I'll go with UpdatePositionState(). That sounds better. What do you think of SetPositionState? https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:586: readable_stream.segment_stream.SetReader(segment_reader.Get()); On 2017/04/26 10:09:48, cblume wrote: > On 2017/04/25 15:31:40, scroggo_chromium wrote: > > This makes me wonder if SetReader should take a PassRefPtr. I'm guessing it > > takes a raw pointer because onSetData does? > > Correct. I used a raw pointer because that is what I was given. > We aren't actually given ownership, either. So we can't just make a RefPtr out > of it. We are only given a view into someone else's RefPtr. > > However, that is probably something we could change in a separate CL. > > The base class ImageDecoder::SetData() takes a PassRefPtr, stores it locally, > and then calls OnSetData() with a raw pointer view into the pointer it owns. So > from within the derived image decoders, we cannot create a ref count -- it will > be deleted twice with two separate ref counts. > > I suspect it could continue to pass ownership on to OnSetData() though. > > But what *could* happen is create a protected RefPtr<SegmentReader> > ImageDecoder::GetData() { return data_; } I'm not sure I follow. Don't we already make a RefPtr out of the raw pointer inside the SegmentStream? This is safe because this is an invasive ref-counting system (notice SegmentReader inherits from ThreadSafeRefCounted, whose base class includes the ref count). Creating a new RefPtr does not create a separate ref count. https://codereview.chromium.org/2565323003/diff/1660001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1660001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:49: void MakeSegmentStreamReadable(blink::SegmentStream* segment_stream) { I guess you named it this because it will make read() succeed? It sounds to me like it changes it from opaque to readable, but really it populates it with data (although really, it's uninitialized data, but I think that should be fine so long as you don't try to do any comparisons?). What about something like: - PrepareSegmentStream - InitSegmentStream - PopulateSegmentStream - SupplySegmentReader
https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:85: void SegmentStream::UpdateInternals(size_t new_position) { On 2017/04/26 19:02:22, scroggo_chromium wrote: > On 2017/04/26 10:09:42, cblume wrote: > > On 2017/04/25 15:31:40, scroggo_chromium wrote: > > > "UpdateInternals" sounds vague to me. What if we called it something like > > > > > > SetPosition(Internal) > > > Seek(Internal) > > > > > > It is used to set the position (and update the other fields that depend on > the > > > position). The one case where we're not modifying the position is SetReader. > > > > I agree that there could be a better name. > > > > I am tempted to name it something like OnPositionChanged() but that sounds a > bit > > like it is a callback. > > I'll go with UpdatePositionState(). > > That sounds better. What do you think of SetPositionState? SetPositionState sounds great. I'll go with that. https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:586: readable_stream.segment_stream.SetReader(segment_reader.Get()); On 2017/04/26 19:02:22, scroggo_chromium wrote: > On 2017/04/26 10:09:48, cblume wrote: > > On 2017/04/25 15:31:40, scroggo_chromium wrote: > > > This makes me wonder if SetReader should take a PassRefPtr. I'm guessing it > > > takes a raw pointer because onSetData does? > > > > Correct. I used a raw pointer because that is what I was given. > > We aren't actually given ownership, either. So we can't just make a RefPtr out > > of it. We are only given a view into someone else's RefPtr. > > > > However, that is probably something we could change in a separate CL. > > > > The base class ImageDecoder::SetData() takes a PassRefPtr, stores it locally, > > and then calls OnSetData() with a raw pointer view into the pointer it owns. > So > > from within the derived image decoders, we cannot create a ref count -- it > will > > be deleted twice with two separate ref counts. > > > > I suspect it could continue to pass ownership on to OnSetData() though. > > > > But what *could* happen is create a protected RefPtr<SegmentReader> > > ImageDecoder::GetData() { return data_; } > > I'm not sure I follow. Don't we already make a RefPtr out of the raw pointer > inside the SegmentStream? This is safe because this is an invasive ref-counting > system (notice SegmentReader inherits from ThreadSafeRefCounted, whose base > class includes the ref count). Creating a new RefPtr does not create a separate > ref count. OHHHHH it is invasive! Okay yeah, in that case I agree -- it makes sense for SetReader() to take a PassRefPtr. I'll do that. https://codereview.chromium.org/2565323003/diff/1660001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1660001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:49: void MakeSegmentStreamReadable(blink::SegmentStream* segment_stream) { On 2017/04/26 19:02:23, scroggo_chromium wrote: > I guess you named it this because it will make read() succeed? It sounds to me > like it changes it from opaque to readable, but really it populates it with data > (although really, it's uninitialized data, but I think that should be fine so > long as you don't try to do any comparisons?). What about something like: > > - PrepareSegmentStream > - InitSegmentStream > - PopulateSegmentStream > - SupplySegmentReader I just unblocked moving for SkNoncopyable types, which means I can now make this even more simple: I can return the created SegmentStream. But you are right, "readable" isn't quite right. I like "Populate". This will become CreatePopulatedSegmentStream(), which I think will make things much easier.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/1700001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1700001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:49: blink::SegmentStream CreatePopulatedSegmentStreamReadable(); Oops. This wasn't supposed to end in ___Readable(). Please disregard.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
Description was changed from ========== Move gif image decoder to SkCodec SkCodec is gaining advantages over Blink's image decoders. scroggo@ has a nice summary: - Skia already has some features that Chromium is interested in: - decoding scaled versions of images - decoding subsets (i.e. issue 468914) - SIMD optimizations for writing pixels - In general, sharing code means that new features/bug fixes in Android benefit Chromium and vice versa - Skia's API is designed to allow the client to handle caching, so that the client that knows the bigger picture can make caching decisions in a more informed way than ImageDecoder can. BUG=663569 ========== to ========== Move GIFImageDecoder to SkCodec SkCodec is gaining advantages over Blink's image decoders. scroggo@ has a nice summary: - Skia already has some features that Chromium is interested in: - decoding scaled versions of images - decoding subsets (i.e. issue 468914) - SIMD optimizations for writing pixels - In general, sharing code means that new features/bug fixes in Android benefit Chromium and vice versa - Skia's API is designed to allow the client to handle caching, so that the client that knows the bigger picture can make caching decisions in a more informed way than ImageDecoder can. BUG=715812 ==========
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_clang on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_clang/builds/...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp (right): https://codereview.chromium.org/2565323003/diff/1640001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStreamTest.cpp:123: TEST(SegmentStreamTest, SetReaderShouldSetIsAtEndWhenSetToNull) { On 2017/04/26 18:17:52, cblume wrote: > On 2017/04/26 10:09:44, cblume wrote: > > On 2017/04/25 15:31:40, scroggo_chromium wrote: > > > Some of these tests seem like they could be combined. e.g. > > > SetReaderShouldUnsetIsAtEnd is the same as the first ASSERT here. Do we > really > > > need both? > > > > I completely agree. > > Ideally, only one test fails when one thing goes wrong. That way the test can > > point directly to the problem. So you are right about why I did this. > > > > What I *wish* we had is a way for one test to depend on another test. For > > example, if test A failed don't even run test B because test B needs to be > able > > to assume A's behavior works. Unfortunately, that isn't what we have. So my > > idealist separation can't provide that benefit. > > > > I actually started writing changes to gtest but couldn't find an email group > to > > see if they would accept it or if I had missed something. What I wanted to do > > was create a sort of ASSUME_TRUE(OtherTest(segment_stream)); which would leave > > the segment_stream in the state I want. > > > > I think there might be a happy medium here. I could probably create > > OtherTestHelper() so we reduce code duplication & still have any given test > > failure point to the correct reason for failure. There would be multiple test > > failures when 1 bad change happens. But at least they would all point to the > > same reason. > > > > Additionally, an alternative that follows your idea is to have 1 SetReader > with > > multiple tests IsAtEnd, IsCleared, ... > > That would probably fit better since the setup would be the same for all of > > those. And since the idealistic one-test-per-failure-condition can't happen, > > might as well make it easy to read. > > > > I'll start with this last idea (similar to yours) and pull out setup where I > > can. > > I began working on this and realized it is not what we want. > > Having 1 test that checks multiple behaviors (since the setup is the same) > violates a few things. > For one, it makes it hard to follow the naming guidelines. I could name the test > SetReader...HasCorrectBehavior?? > But more clearly, from > https://engdoc.corp.google.com/eng/doc/devguide/coding/testing/unit-testing-b... > adding new behavior should mean not needing to change any existing tests. It > specifically calls out this example where one method tests multiple behaviors. > Check the second paragraph. > > So I think we want to keep this long, exhaustive list. > > But I think I can still refactor it a bit to reuse the common setup. That should > make things easier. And I might be able to do it in a way that clearly states > "This test failed when trying to do the setup. The real failure is over there." > I'll work on this now. I have cleaned this up as much as I think makes sense. The tests are in the form: <setup> __blank_line__ <thing_being_tested> __blank_line__ <assert expected result> The bulk majority of the tests only need 1 line for each of those sections now. The ones which don't are doing something unique.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
Could you add some more details to the commit message and/or the bug? They mention a few things that don't change without follow-on work (caching benefits, subset decoding, scaled decoding), but what I'd really like to see is how does this benefit the code right now? You showed me some performance results a while ago; it would be good to provide links to those (along with the benchmarks themselves) to demonstrate how the performance has improved. (I think you only used a few images, so it would be good to run over a larger set, including animated images. Plus the code has changed a bit since then, including some bug fixes, so using the current patch set would be helpful.) This also doesn't mention that it drops alpha correction. What effect does that have on draw time? How prevalent are images that need to be corrected? I did some testing on a somewhat arbitrary selection of GIFs, and found the following: GIF totals: trans index (in at least one frame)? yes: 224 no: 299 ignore valid trans index (in at least one frame): 61 Frame totals: trans index? yes: 901 no: 465 ignore valid trans index: 217 So a little under half of the GIFs I tested had a transparent index, and of those less than a third ignored it. If we look at it on a frame basis, roughly two thirds had a transparent index, and of those around a quarter ignored it. For those frames that we could have corrected, do they slow down at draw time? Could they have been marked independent if we had done the correction? What effect does that have on caching? For the future work, it would also be helpful to provide links to design documents.
On 2017/04/27 14:50:49, scroggo_chromium wrote: > Could you add some more details to the commit message and/or the bug? They > mention a few things that don't change without follow-on work (caching benefits, > subset decoding, scaled decoding), but what I'd really like to see is how does > this benefit the code right now? You showed me some performance results a while > ago; it would be good to provide links to those (along with the benchmarks > themselves) to demonstrate how the performance has improved. (I think you only > used a few images, so it would be good to run over a larger set, including > animated images. Plus the code has changed a bit since then, including some bug > fixes, so using the current patch set would be helpful.) > > This also doesn't mention that it drops alpha correction. What effect does that > have on draw time? How prevalent are images that need to be corrected? I did > some testing on a somewhat arbitrary selection of GIFs, and found the following: > > GIF totals: > trans index (in at least one frame)? yes: 224 no: 299 > ignore valid trans index (in at least one frame): 61 > Frame totals: > trans index? yes: 901 no: 465 > ignore valid trans index: 217 > > So a little under half of the GIFs I tested had a transparent index, and of > those less than a third ignored it. > If we look at it on a frame basis, roughly two thirds had a transparent index, > and of those around a quarter ignored it. > > For those frames that we could have corrected, do they slow down at draw time? > Could they have been marked independent if we had done the correction? What > effect does that have on caching? > > For the future work, it would also be helpful to provide links to design > documents. I'll create a new doc with new timings (including more content) and link it in the bug. I'll also explain how to recreate those timings there. I'll also explain what future work needs to be done and what it might look like. Removing the special case for alpha is happening in a separate patch: https://codereview.chromium.org/2756463003/ If we are currently removing the special case inside SkCodec, we might want to undo that and add it as a separate patch as well? It is a separate concern. And if it is already landed in SkCodec then a post-launch investigation would only indicate we should have reverted it anyway, right? Anyway, on that other bug is where I'll compare timing and occurrence. From your investigation, it seems common enough in images that we probably do care if there is a measurable difference one way or the other.
On 2017/04/27 15:37:00, cblume wrote: > On 2017/04/27 14:50:49, scroggo_chromium wrote: > > Could you add some more details to the commit message and/or the bug? They > > mention a few things that don't change without follow-on work (caching > benefits, > > subset decoding, scaled decoding), but what I'd really like to see is how does > > this benefit the code right now? You showed me some performance results a > while > > ago; it would be good to provide links to those (along with the benchmarks > > themselves) to demonstrate how the performance has improved. (I think you only > > used a few images, so it would be good to run over a larger set, including > > animated images. Plus the code has changed a bit since then, including some > bug > > fixes, so using the current patch set would be helpful.) > > > > This also doesn't mention that it drops alpha correction. What effect does > that > > have on draw time? How prevalent are images that need to be corrected? I did > > some testing on a somewhat arbitrary selection of GIFs, and found the > following: > > > > GIF totals: > > trans index (in at least one frame)? yes: 224 no: 299 > > ignore valid trans index (in at least one frame): 61 > > Frame totals: > > trans index? yes: 901 no: 465 > > ignore valid trans index: 217 > > > > So a little under half of the GIFs I tested had a transparent index, and of > > those less than a third ignored it. > > If we look at it on a frame basis, roughly two thirds had a transparent index, > > and of those around a quarter ignored it. > > > > For those frames that we could have corrected, do they slow down at draw time? > > Could they have been marked independent if we had done the correction? What > > effect does that have on caching? > > > > For the future work, it would also be helpful to provide links to design > > documents. > > > I'll create a new doc with new timings (including more content) and link it in > the bug. I'll also explain how to recreate those timings there. > I'll also explain what future work needs to be done and what it might look like. > > Removing the special case for alpha is happening in a separate patch: > https://codereview.chromium.org/2756463003/ Okay, I'll wait for an update there. > If we are currently removing the special case inside SkCodec, we might want to > undo that and add it as a separate patch as well? It is a separate concern. > And if it is already landed in SkCodec then a post-launch investigation would > only indicate we should have reverted it anyway, right? SkCodec has not corrected for alpha for a long time. When we removed the alpha detection/correction, the only client was Android, and SkCodec did not support animation yet. So compositing frames was not considered, and the typical use case was decoding PNG assets. The assets are reencoded when creating the apk anyway, so we made the reencode step smarter - PNG assets only claim to have transparency if they actually use it. > Anyway, on that other bug is where I'll compare timing and occurrence. From your > investigation, it seems common enough in images that we probably do care if > there is a measurable difference one way or the other.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-simulator on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator/bui...) mac_chromium_compile_dbg_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_comp...) mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-device-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-device-xcode-...) ios-simulator on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator/bui...) ios-simulator-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator-xco...)
Is there a Skia config for iOS + libjpeg-turbo?
https://codereview.chromium.org/2565323003/diff/1800001/skia/BUILD.gn File skia/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/1800001/skia/BUILD.gn#newcode118 skia/BUILD.gn:118: "//third_party/skia/third_party/libjpeg-turbo", > Is there a Skia config for iOS + libjpeg-turbo? Is the problem that Skia isn't finding jpeg? Shouldn't this depend on the target used by Chromium elsewhere?
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-simulator-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator-xco...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-simulator on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator/bui...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-simulator-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator-xco...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-simulator-xcode-clang on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator-xco...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
Description was changed from ========== Move GIFImageDecoder to SkCodec SkCodec is gaining advantages over Blink's image decoders. scroggo@ has a nice summary: - Skia already has some features that Chromium is interested in: - decoding scaled versions of images - decoding subsets (i.e. issue 468914) - SIMD optimizations for writing pixels - In general, sharing code means that new features/bug fixes in Android benefit Chromium and vice versa - Skia's API is designed to allow the client to handle caching, so that the client that knows the bigger picture can make caching decisions in a more informed way than ImageDecoder can. BUG=715812 ========== to ========== Move GIFImageDecoder to SkCodec SkCodec is gaining advantages over Blink's image decoders. scroggo@ has a nice summary: - Skia already has some features that Chromium is interested in: - decoding scaled versions of images - decoding subsets (i.e. issue 468914) - SIMD optimizations for writing pixels - In general, sharing code means that new features/bug fixes in Android benefit Chromium and vice versa - Skia's API is designed to allow the client to handle caching, so that the client that knows the bigger picture can make caching decisions in a more informed way than ImageDecoder can. Performance measurements of this patch can be found here (requires @google.com account): https://docs.google.com/a/chromium.org/spreadsheets/d/18xPfE241z4-1hQdTBdFEJz... A roadmap of improvements for image decoders (including improvements unlocked by using SkCodec) can be found here (requires @chromium.org account): https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... BUG=715812 ==========
> Could you add some more details to the commit message and/or the bug? They > mention a few things that don't change without follow-on work (caching benefits, > subset decoding, scaled decoding), but what I'd really like to see is how does > this benefit the code right now? You showed me some performance results a while > ago; it would be good to provide links to those (along with the benchmarks > themselves) to demonstrate how the performance has improved. Done. I also cleaned up the timing doc a bit to make things easier to follow. I will add more files (with less detail per file) to that doc as well. https://codereview.chromium.org/2565323003/diff/1800001/skia/BUILD.gn File skia/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/1800001/skia/BUILD.gn#newcode118 skia/BUILD.gn:118: "//third_party/skia/third_party/libjpeg-turbo", On 2017/05/18 15:55:56, scroggo_chromium wrote: > > Is there a Skia config for iOS + libjpeg-turbo? > > Is the problem that Skia isn't finding jpeg? Shouldn't this depend on the target > used by Chromium elsewhere? Done.
Description was changed from ========== Move GIFImageDecoder to SkCodec SkCodec is gaining advantages over Blink's image decoders. scroggo@ has a nice summary: - Skia already has some features that Chromium is interested in: - decoding scaled versions of images - decoding subsets (i.e. issue 468914) - SIMD optimizations for writing pixels - In general, sharing code means that new features/bug fixes in Android benefit Chromium and vice versa - Skia's API is designed to allow the client to handle caching, so that the client that knows the bigger picture can make caching decisions in a more informed way than ImageDecoder can. Performance measurements of this patch can be found here (requires @google.com account): https://docs.google.com/a/chromium.org/spreadsheets/d/18xPfE241z4-1hQdTBdFEJz... A roadmap of improvements for image decoders (including improvements unlocked by using SkCodec) can be found here (requires @chromium.org account): https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... BUG=715812 ========== to ========== Move GIFImageDecoder to SkCodec SkCodec is gaining advantages over Blink's image decoders. scroggo@ has a nice summary: - Skia already has some features that Chromium is interested in: - decoding scaled versions of images - decoding subsets (i.e. issue 468914) - SIMD optimizations for writing pixels - In general, sharing code means that new features/bug fixes in Android benefit Chromium and vice versa - Skia's API is designed to allow the client to handle caching, so that the client that knows the bigger picture can make caching decisions in a more informed way than ImageDecoder can. Performance measurements of this patch can be found here (requires @google.com account): https://docs.google.com/a/chromium.org/spreadsheets/d/18xPfE241z4-1hQdTBdFEJz... Summary: Decoder creation time is sometimes slightly slower. Decode time is consistently faster (sometimes by as much as 19%). A roadmap of improvements for image decoders (including improvements unlocked by using SkCodec) can be found here (requires @chromium.org account): https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... BUG=715812 ==========
https://codereview.chromium.org/2565323003/diff/1920001/skia/BUILD.gn File skia/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/1920001/skia/BUILD.gn#newcode47 skia/BUILD.gn:47: include_dirs += [ "//third_party/skia/include/codec" ] So we're not using SkCodec on ios? What happens there? We fail to decode? https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp (right): https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:86: bool copied = false; nit: My preference would be to drop this local variable, and instead do something like if (!bitmap_.tryAllocPixels(info)) { return false; } status_ = kFrameAllocated; return other.bitmap_readPixels(...); That seems simpler to me. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:89: status_ = kFrameAllocated; So if we fail in readPixels, we'll still have the kFrameAllocated status, but it seems there's no way to differentiate that. I suppose it does not matter though; if this returns false we'll SetFailed, so noone will care about that status. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:140: frame_infos_ = codec_->getFrameInfo(); We talked about calling codec_->getFrameCount() and then the version of SkCodec::getFrameInfo() that takes an index to avoid copying the vector. Are you planning to include that here? https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:164: // The disposal method is not required any more, but is left in place Sorry, I still need to add this to SkCodec. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: SkImageInfo image_info = codec_->getInfo().makeColorType(kN32_SkColorType); I think you also want to modify this to use ColorSpaceForSkImages() i.e. image_info = codec_->getInfo().makeColorType(kN32_SkColorType) .makeColorSpace(ColorSpaceForSkImages()); That way we tell the codec to decode into the color space that the ImageFrame's SkBitmap claims to be. msarett@, am I understanding that correctly? https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:243: SetFailed(); I think we may want to do this *after* zeroing the remaining lines, because I think we may continue to show a frame after calling SetFailed(). https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:247: IntRect remaining_rect = frame.OriginalFrameRect(); It seems weird to start with OriginalFrameRect. If it was returning what it claims to, this would be wrong. I would start with the dimensions of Size() https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:250: frame.ZeroFillFrameRect(remaining_rect); We only need to do this the first time we get an incomplete decode. The second time, it will already be zeroed. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:251: // ZeroFillFrameRect() resets the alpha to true. I think you had a patch that changed this (but maybe it was rolled into another CL)? Would it make sense to change it so we don't need this line + comments? https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:253: frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_infos_[index].fAlphaType)); Maybe it would be clearer that we're setting it back if we did somehting like: bool oldAlpha = frame.HasAlpha(); frame.ZeroFillFrameRect(remaining_rect); frame.setHasAlpha(oldAlpha); Or, since we'll only ever change it back to false: bool actuallyOpaque = frame.HasAlpha(); frame.ZeroFillFrameRect(remaining_rect); if (actuallyOpaque) { frame.setHasAlpha(false); }
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_compile_dbg_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_comp...)
https://codereview.chromium.org/2565323003/diff/1920001/skia/BUILD.gn File skia/BUILD.gn (right): https://codereview.chromium.org/2565323003/diff/1920001/skia/BUILD.gn#newcode47 skia/BUILD.gn:47: include_dirs += [ "//third_party/skia/include/codec" ] On 2017/06/05 19:00:53, scroggo_chromium wrote: > So we're not using SkCodec on ios? What happens there? We fail to decode? That is correct, we do not use SkCodec on iOS. Nor do we use the Blink decoders, as far as I can tell. The iOS app store requires that we do not provide our own browser / javascript engine and must instead use the platform's WebKit. So Chrome on iOS is a WebKit skin with other Chrome features such as account sync. A while ago, I thought we still built all of Chrome for iOS even though we don't ship it all (we only ship parts). But since I see a lot of build files asserting that iOS shouldn't include a given part (which includes libjpeg_turbo [1]), I think the image decoders aren't built for iOS. [1] https://cs.chromium.org/chromium/src/build/secondary/third_party/libjpeg_turb... https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp (right): https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:86: bool copied = false; On 2017/06/05 19:00:53, scroggo_chromium wrote: > nit: My preference would be to drop this local variable, and instead do > something like > > if (!bitmap_.tryAllocPixels(info)) { > return false; > } > > status_ = kFrameAllocated; > return other.bitmap_readPixels(...); > > That seems simpler to me. Done. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/ImageFrame.cpp:89: status_ = kFrameAllocated; On 2017/06/05 19:00:53, scroggo_chromium wrote: > So if we fail in readPixels, we'll still have the kFrameAllocated status, but it > seems there's no way to differentiate that. I suppose it does not matter though; > if this returns false we'll SetFailed, so noone will care about that status. Right. At that point we *have* allocated (with tryAllocPixels). https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:140: frame_infos_ = codec_->getFrameInfo(); On 2017/06/05 19:00:53, scroggo_chromium wrote: > We talked about calling codec_->getFrameCount() and then the version of > SkCodec::getFrameInfo() that takes an index to avoid copying the vector. Are you > planning to include that here? Oh, I missed that patch to SkCodec::getFrameInfo() had landed. I'll add it now. Hrmmm a small problem with these get* functions doing work is I *may* need to maintain local state about the value. For example, if we enter a failed state and someone requests the frame count, I don't want to do any more work parsing for that frame count. So any function of mine which might fail also needs to first update a local copy of the frame count. That way when if (Failed()) I can return that local frame count without doing work. I think in this case it is actually fine. I think after we call SetFailed() we don't call DecodeFrameCount(). But it still leaves two issues: When segment_stream_ is cleared and FrameIsCompleteAtIndex(), which is const. I can still call codec_->getFrameCount() inside the const function because the pointer is const but the pointed-to object isn't const. In a way, it is like the member is marked mutable. None of the tests fail when I try avoiding local state (which is my preference). I'll go with that. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:164: // The disposal method is not required any more, but is left in place On 2017/06/05 19:00:53, scroggo_chromium wrote: > Sorry, I still need to add this to SkCodec. No problem. Even without it, fRequiredFrame allows us to maintain existing behavior. This comment is specifically about Blink::ImageFrame and why we still set a disposal method there. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: SkImageInfo image_info = codec_->getInfo().makeColorType(kN32_SkColorType); On 2017/06/05 19:00:53, scroggo_chromium wrote: > I think you also want to modify this to use ColorSpaceForSkImages() i.e. > > image_info = codec_->getInfo().makeColorType(kN32_SkColorType) > > .makeColorSpace(ColorSpaceForSkImages()); > > That way we tell the codec to decode into the color space that the ImageFrame's > SkBitmap claims to be. > > msarett@, am I understanding that correctly? Done. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:243: SetFailed(); On 2017/06/05 19:00:53, scroggo_chromium wrote: > I think we may want to do this *after* zeroing the remaining lines, because I > think we may continue to show a frame after calling SetFailed(). Done. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:247: IntRect remaining_rect = frame.OriginalFrameRect(); On 2017/06/05 19:00:53, scroggo_chromium wrote: > It seems weird to start with OriginalFrameRect. If it was returning what it > claims to, this would be wrong. I would start with the dimensions of Size() Done. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:250: frame.ZeroFillFrameRect(remaining_rect); On 2017/06/05 19:00:53, scroggo_chromium wrote: > We only need to do this the first time we get an incomplete decode. The second > time, it will already be zeroed. Done. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:251: // ZeroFillFrameRect() resets the alpha to true. On 2017/06/05 19:00:53, scroggo_chromium wrote: > I think you had a patch that changed this (but maybe it was rolled into another > CL)? Would it make sense to change it so we don't need this line + comments? Right. The CL is here: https://codereview.chromium.org/2762643004/ I haven't gotten back to it yet since it isn't a high priority. I think it might be best to land this patch as-is and update that CL separately after this lands. Maybe I'm misunderstanding what you're saying. https://codereview.chromium.org/2565323003/diff/1920001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:253: frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_infos_[index].fAlphaType)); On 2017/06/05 19:00:53, scroggo_chromium wrote: > Maybe it would be clearer that we're setting it back if we did somehting like: > > bool oldAlpha = frame.HasAlpha(); > frame.ZeroFillFrameRect(remaining_rect); > frame.setHasAlpha(oldAlpha); > > Or, since we'll only ever change it back to false: > > bool actuallyOpaque = frame.HasAlpha(); > frame.ZeroFillFrameRect(remaining_rect); > if (actuallyOpaque) { > frame.setHasAlpha(false); > } Done. I went with the first version which I think is easier to immediately understand. But I'm happy to do the other if you want.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:219: options.fPriorFrame = SkCodec::kNone; This is supposed to be set to the previous frame. https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: if (!index) { If we encounter an error in frame 5, do we display the partial frame 5? Currently, the only partial frame we display in this patch is the first frame. https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:267: codec_->getFrameInfo(index, &frame_info); I think these 2 lines don't do anything and should be removed. It is a leftover from when the code was: SkCodec::FrameInfo frame_info; codec_->getFrameInfo(index, &frame_info); frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_info.fAlphaType)); I'm going to remove it.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: if (!index) { On 2017/07/07 22:11:58, cblume wrote: > If we encounter an error in frame 5, do we display the partial frame 5? My guess would be no. Let's say frame 5 is overlayed onto frame 4. (5 is subset and/or has transparency.) If we decode only the first half of 5, and then there is an error, we'll end up with a frame that displays the top of 5 and the bottom of 4. That said, I don't see the code that would do anything to prevent showing such a broken frame. > > Currently, the only partial frame we display in this patch is the first frame. If a frame beyond the first is not believed to be complete, you are correct, we will not attempt to decode/display it. But if there's an error in frame 5, I don't know what prevents us from drawing it. https://codereview.chromium.org/2565323003/diff/2220001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:222: for (size_t i = required_previous_frame_index; i < index; i++) { Should we search backwards, given that we most likely decoded the prior frame before this one? https://codereview.chromium.org/2565323003/diff/2220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:227: break; I think you want this to be continue, so that we'll skip over this one. It cannot be used as the prior frame, but another one could.
https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: if (!index) { On 2017/07/10 17:45:57, scroggo_chromium wrote: > On 2017/07/07 22:11:58, cblume wrote: > > If we encounter an error in frame 5, do we display the partial frame 5? > > My guess would be no. Let's say frame 5 is overlayed onto frame 4. (5 is subset > and/or has transparency.) If we decode only the first half of 5, and then there > is an error, we'll end up with a frame that displays the top of 5 and the bottom > of 4. > > That said, I don't see the code that would do anything to prevent showing such a > broken frame. The if (!index) { will enforce that only the first frame gets zero filled. (Note to self: I need to do this in the startIncrementalDecode handler as well) Later, if (... error_in_input) { calls SetFailed(); I'm looking into the "prevent showing a broken frame" bit now... Perhaps you are saying that even thought the index isn't 0 and the frame isn't marked as complete, it will be displayed? At this point, the frame should be marked as kFramePartial. It seems like the existing ImageFrameGenerator didn't handle partial frames differently [1]. And the DeferredImageDecoder would still try to create a SkBitmap [2]. Both have special handling for if the frame is marked empty though. Maybe I should make it as empty? The existing Blink gif decoder would exit early when there was an error in decoding, which left the frame marked as empty. This seems to be the right path. One thing to note is at that point we have already taken or copied the previous bitmap. This is part of the new design, currently. This is probably okay since we likely would have purged that frame anyway. [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... [2] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > > Currently, the only partial frame we display in this patch is the first frame. > > If a frame beyond the first is not believed to be complete, you are correct, we > will not attempt to decode/display it. But if there's an error in frame 5, I > don't know what prevents us from drawing it. I think perhaps the frame being marked as empty prevents it (see my links / description above). https://codereview.chromium.org/2565323003/diff/2220001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:222: for (size_t i = required_previous_frame_index; i < index; i++) { On 2017/07/10 17:45:57, scroggo_chromium wrote: > Should we search backwards, given that we most likely decoded the prior frame > before this one? I was thinking about exactly this, actually. And I'm quite torn on a number of reasons (when we fix the caching, thing will change...and with contiguous memory & linear searches & prefetching & a small list, it is unlikely our measurement tools will even detect anything). Since I am torn, I'm happy to search backwards. But the frame we keep in Blink isn't the previous frame. It is the frame marked as the required frame. SkCodec marks the earliest frame, which means a forward search is most likely to hit it first. https://codereview.chromium.org/2565323003/diff/2220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:227: break; On 2017/07/10 17:45:57, scroggo_chromium wrote: > I think you want this to be continue, so that we'll skip over this one. It > cannot be used as the prior frame, but another one could. Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: if (!index) { On 2017/07/11 00:23:37, cblume wrote: > On 2017/07/10 17:45:57, scroggo_chromium wrote: > > On 2017/07/07 22:11:58, cblume wrote: > > > If we encounter an error in frame 5, do we display the partial frame 5? > > > > My guess would be no. Let's say frame 5 is overlayed onto frame 4. (5 is > subset > > and/or has transparency.) If we decode only the first half of 5, and then > there > > is an error, we'll end up with a frame that displays the top of 5 and the > bottom > > of 4. > > > > That said, I don't see the code that would do anything to prevent showing such > a > > broken frame. > > > The if (!index) { will enforce that only the first frame gets zero filled. > (Note to self: I need to do this in the startIncrementalDecode handler as well) > Later, if (... error_in_input) { calls SetFailed(); > > > I'm looking into the "prevent showing a broken frame" bit now... > > Perhaps you are saying that even thought the index isn't 0 and the frame isn't > marked as complete, it will be displayed? > > At this point, the frame should be marked as kFramePartial. > > It seems like the existing ImageFrameGenerator didn't handle partial frames > differently [1]. It doesn't really handle empty frames differently either. Notice that it will return false if the frame is marked FrameEmpty, but is_decode_complete will also be set to false (and then returned) if the frame is not FrameComplete (and !all_data_received). The return value is just used to determine whether we can free memory (by deleting the decoder, for a single frame image, or clearing cache for a multi-frame image). > And the DeferredImageDecoder would still try to create a > SkBitmap [2]. Both have special handling for if the frame is marked empty > though. Maybe I should make it as empty? Agreed, DeferredImageDecoder handles Empty differently. > > The existing Blink gif decoder would exit early when there was an error in > decoding, which left the frame marked as empty. This seems to be the right path. Are you sure? In the old code, the status gets bumped to FramePartial in InitFrameBuffer. I think a test would be the best way to verify this. > > One thing to note is at that point we have already taken or copied the previous > bitmap. This is part of the new design, currently. And part of the old design, right? I don't think that has changed. > This is probably okay since we likely would have purged that frame anyway. What do you mean? > > [1] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > [2] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > > > > > Currently, the only partial frame we display in this patch is the first > frame. > > > > If a frame beyond the first is not believed to be complete, you are correct, > we > > will not attempt to decode/display it. But if there's an error in frame 5, I > > don't know what prevents us from drawing it. > > I think perhaps the frame being marked as empty prevents it (see my links / > description above). https://codereview.chromium.org/2565323003/diff/2220001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:222: for (size_t i = required_previous_frame_index; i < index; i++) { On 2017/07/11 00:23:37, cblume wrote: > On 2017/07/10 17:45:57, scroggo_chromium wrote: > > Should we search backwards, given that we most likely decoded the prior frame > > before this one? > > I was thinking about exactly this, actually. And I'm quite torn on a number of > reasons (when we fix the caching, thing will change...and with contiguous memory > & linear searches & prefetching & a small list, it is unlikely our measurement > tools will even detect anything). > > Since I am torn, I'm happy to search backwards. > > But the frame we keep in Blink isn't the previous frame. It is the frame marked > as the required frame. SkCodec marks the earliest frame, which means a forward > search is most likely to hit it first. Ah, good point. https://codereview.chromium.org/2565323003/diff/2280001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2280001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:261: case SkCodec::kIncompleteInput: I think you'll need to zero the frame, here, too, which is part of the reason I suggested you might just always zero initially (like we do today), and then perhaps follow up with a change to only zero when decoding is incomplete.
https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: if (!index) { On 2017/07/11 17:41:28, scroggo_chromium wrote: > On 2017/07/11 00:23:37, cblume wrote: > > The existing Blink gif decoder would exit early when there was an error in > > decoding, which left the frame marked as empty. This seems to be the right > path. > > Are you sure? In the old code, the status gets bumped to FramePartial in > InitFrameBuffer. I think a test would be the best way to verify this. Pretty sure. It seems like before InitFrameBuffer gets called (in fact, before any decoding happens) GIFImageDecoder::Decode() tries to parse [1]. If the parse itself fails, it calls SetFailed() and then GIFImageDecoder::Decode() exits early (before calling InitFrameBuffer) [2]. It seems like InitFrameBuffer is only called from two places: OutputRow and FrameComplete. Both are only called after the parse, in the reader->Decode() part of [1]. So it seems like an error in input would leave the frame marked as FrameEmpty, which cues both ImageFrameGenerator and DeferredImageDecoder to use their special treatment of FrameEmpty. But I think you are right -- we can put this in a separate CL. I'll add the tests there, too. For now, I think always zero initializing is the play. [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... [2] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > One thing to note is at that point we have already taken or copied the > previous > > bitmap. This is part of the new design, currently. > > And part of the old design, right? I don't think that has changed. I think this has changed. As I described above, the old GIF decoder would exit early if it found a failure in parsing. Specifically, it would exit before calling InitFrameBuffer(). InitFrameBuffer() is where we would take / copy the old frame [3]. Even though SkGifCodec parses at an earlier step (when the data is provided) and the error is potentially encountered earlier, that error isn't reported until we call startIncrementalDecode() or incrementalDecode(). The only way for the client to know there was an error in the input is after the frame has been taken. But this is probably fine, as I describe below. [3] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > This is probably okay since we likely would have purged that frame anyway. > > What do you mean? What I mean is suppose we steal frame 10 and then hit an error in decode. So even though we stole the frame, we cannot display it. And we loop back to the beginning of the animation. Upon hitting frame 10 again in the next animation iteration, we now need to decode it again since the frame was stolen. But we would have needed to decode it again anyway, since Blink purges after every decode. Note that this only matters if the frame was not displayed (IE when fast-forwarding an animation to catch up). When a frame is displayed, it is marked immutable and we can no longer steal it in the first place. So basically, this is not a common situation and even when it does happen it probably doesn't matter.
https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: if (!index) { On 2017/07/11 18:25:02, cblume wrote: > On 2017/07/11 17:41:28, scroggo_chromium wrote: > > On 2017/07/11 00:23:37, cblume wrote: > > > The existing Blink gif decoder would exit early when there was an error in > > > decoding, which left the frame marked as empty. This seems to be the right > > path. > > > > Are you sure? In the old code, the status gets bumped to FramePartial in > > InitFrameBuffer. I think a test would be the best way to verify this. > > Pretty sure. > > It seems like before InitFrameBuffer gets called (in fact, before any decoding > happens) GIFImageDecoder::Decode() tries to parse [1]. If the parse itself > fails, it calls SetFailed() and then GIFImageDecoder::Decode() exits early > (before calling InitFrameBuffer) [2]. > > It seems like InitFrameBuffer is only called from two places: OutputRow and > FrameComplete. > Both are only called after the parse, in the reader->Decode() part of [1]. Except that parsing isn't the only case where we can see an error. There can also be an error in decoding. (i.e. in doLZW) > > So it seems like an error in input would leave the frame marked as FrameEmpty, > which cues both ImageFrameGenerator and DeferredImageDecoder to use their > special treatment of FrameEmpty. Again, ImageFrameGenerator doesn't treat Empty differently from Partial (besides deciding whether to call SetHasAlpha). > > > But I think you are right -- we can put this in a separate CL. I'll add the > tests there, too. For now, I think always zero initializing is the play. Great. Sgtm. > > > [1] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > [2] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > One thing to note is at that point we have already taken or copied the > > previous > > > bitmap. This is part of the new design, currently. > > > > And part of the old design, right? I don't think that has changed. > > I think this has changed. > > As I described above, the old GIF decoder would exit early if it found a failure > in parsing. Specifically, it would exit before calling InitFrameBuffer(). > InitFrameBuffer() is where we would take / copy the old frame [3]. > > Even though SkGifCodec parses at an earlier step (when the data is provided) and > the error is potentially encountered earlier, that error isn't reported until we > call startIncrementalDecode() or incrementalDecode(). > > The only way for the client to know there was an error in the input is after the > frame has been taken. But this is probably fine, as I describe below. > > [3] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... > > > > > This is probably okay since we likely would have purged that frame anyway. > > > > What do you mean? > > What I mean is suppose we steal frame 10 and then hit an error in decode. So > even though we stole the frame, we cannot display it. And we loop back to the > beginning of the animation. Upon hitting frame 10 again in the next animation > iteration, we now need to decode it again since the frame was stolen. > > But we would have needed to decode it again anyway, since Blink purges after > every decode. > > > Note that this only matters if the frame was not displayed (IE when > fast-forwarding an animation to catch up). When a frame is displayed, it is > marked immutable and we can no longer steal it in the first place. > > So basically, this is not a common situation and even when it does happen it > probably doesn't matter. Ah, I hadn't been considering an error during parse.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_android on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/cast_shell_a...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...)
https://codereview.chromium.org/2565323003/diff/2300001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2300001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:207: frame.ZeroFillPixelData(); I'd recommend following the example of InitFrameBuffer [1], which calls this only in the case where there is no required frame. [1] https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... https://codereview.chromium.org/2565323003/diff/2300001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:208: frame.SetPixelsChanged(true); No need to call SetPixelsChanged, which should only really be necessary if we've already drawn the bitmap once and then changed it. (Again, notice that InitFrameBuffer does not call it.)
https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: if (!index) { On 2017/07/11 20:36:39, scroggo_chromium wrote: > Except that parsing isn't the only case where we can see an error. There can > also be an error in decoding. (i.e. in doLZW) Ah, right. Good call. I spoke to vmpstr@ to find where Blink makes the decision whether or not to paint a frame that is partial for an animation. It looks like it is here: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... It only seems to check if a frame is complete in an animation, meaning we can leave it marked as partial and that is fine. It is more correct as well. > Again, ImageFrameGenerator doesn't treat Empty differently from Partial (besides > deciding whether to call SetHasAlpha). You're right.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: if (!index) { On 2017/07/12 21:22:41, cblume wrote: > On 2017/07/11 20:36:39, scroggo_chromium wrote: > > Except that parsing isn't the only case where we can see an error. There can > > also be an error in decoding. (i.e. in doLZW) > > Ah, right. Good call. > > I spoke to vmpstr@ to find where Blink makes the decision whether or not to > paint a frame that is partial for an animation. > It looks like it is here: > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > > It only seems to check if a frame is complete in an animation, meaning we can > leave it marked as partial and that is fine. It is more correct as well. That code is misleading, though. Although the method is named FrameIsCompleteAtIndex, it is really just checking to see whether the parse marked it as complete. If there is an error in the frame, I don't think we handle that case. Now that we zero all the memory ahead of time, it's not as big of a deal, since we are not showing uninitialized memory, but if I'm right, we might show part of the old frame and part of the new one. This behavior doesn't change with your patch (now that it zeroes the memory first), though. > > > Again, ImageFrameGenerator doesn't treat Empty differently from Partial > (besides > > deciding whether to call SetHasAlpha). > > You're right.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_x64_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_x64_...)
As we discussed, the code is now handling the cache clearing to keep a frame in cache if DisposePrevious is encountered. https://codereview.chromium.org/2565323003/diff/2300001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2300001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:207: frame.ZeroFillPixelData(); On 2017/07/12 17:18:01, scroggo_chromium wrote: > I'd recommend following the example of InitFrameBuffer [1], which calls this > only in the case where there is no required frame. > > [1] > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... Done. https://codereview.chromium.org/2565323003/diff/2300001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:208: frame.SetPixelsChanged(true); On 2017/07/12 17:18:01, scroggo_chromium wrote: > No need to call SetPixelsChanged, which should only really be necessary if we've > already drawn the bitmap once and then changed it. (Again, notice that > InitFrameBuffer does not call it.) Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
Description was changed from ========== Move GIFImageDecoder to SkCodec SkCodec is gaining advantages over Blink's image decoders. scroggo@ has a nice summary: - Skia already has some features that Chromium is interested in: - decoding scaled versions of images - decoding subsets (i.e. issue 468914) - SIMD optimizations for writing pixels - In general, sharing code means that new features/bug fixes in Android benefit Chromium and vice versa - Skia's API is designed to allow the client to handle caching, so that the client that knows the bigger picture can make caching decisions in a more informed way than ImageDecoder can. Performance measurements of this patch can be found here (requires @google.com account): https://docs.google.com/a/chromium.org/spreadsheets/d/18xPfE241z4-1hQdTBdFEJz... Summary: Decoder creation time is sometimes slightly slower. Decode time is consistently faster (sometimes by as much as 19%). A roadmap of improvements for image decoders (including improvements unlocked by using SkCodec) can be found here (requires @chromium.org account): https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... BUG=715812 ========== to ========== Use SkCodec internally in GIFImageDecoder Previously, GIFImageDecoder used GIFImageReader internally. SkCodec uses a modified version of that class (SkGifImageReader; adapted in crrev.com/2045293002). SkCodec provides the following benefits: - SIMD optimized code for writing pixels - an API that allows the client to handle caching - flexibility regarding the required frame to use - the ability to decode scaled versions of images - subset decoding (i.e. issue 468914) (not fully implemented for GIF) In addition, this patch enables sharing code between Android, Skia, and Chromium. This means that new features/bug fixes in Android benefit Chromium and vice versa. For larger images (above ~60x60), the SIMD optimizations show a much bigger benefit (up to 24% in one case). For most images, decoding speed is about the same. Images with many frames that contain tiny update regions are a hair slower. The mean decode time across all tested images showed an improvement. Raw performance numbers can be found here: https://docs.google.com/spreadsheets/d/1JqCPdYmbasOwKRdvuG6ZI4gwq9dPvnJA9oDqu... GIFImageDecoder still handles the cached frames currently, but this change will allow future changes in Blink to make wiser caching Decisions (such as keeping all frames of a 3-frame animation if those frames are small). Full road map: https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... This results in some behavior changes: - SkCodec does not check the alpha of each pixel during decode (for speed and simplicity). As a result, GIFImageDecoder no longer corrects opacity or the required frame after decoding a frame. No performance penalty has been observed for incorrectly leaving a frame marked as having transparency. - SkCodec guesses transparency based on the presence of a transparent index (in addition to being subset) and uses this to potentially determine an earlier required frame. BUG=715812 ==========
https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:145: size_t GIFImageDecoder::ClearCacheExceptFrame(size_t index) { First off, I'll acknowledge that this is intended to be temporary behavior, since you plan to move caching into the compositor. Further, I know that this is in line with how GIF, WEBP and APNG work today. That said, this method seems much more complicated than necessary. If there is only one frame, we do nothing, which is fine. It only gets interesting when we get to the second frame. The first loop checks whether FrameStatusSufficientForSuccessors, which means true if the Frame is FramePartial or FrameComplete. Will that ever return false? ClearCacheExceptFrame is called in two ways: - clear all the frames - clear all except for the one we just decoded Now, relying on this fact is arguably spaghetti code, but is there any other sensible way to call it? The first case bypasses the loops and does the right thing, so let's just look at the second case (i.e. index != kNotFound). In that case, I argue that the Frame in question will always be complete (unless there was an error, in which case we don't bother decoding future frames, so keeping around the cache is moot). - The client only ever asks for frames that have received all their data. Relying on this might again be spaghetti code, but... - GIF is currently the only multi-frame decoder that even reports that a frame exists if it is not yet complete (besides the first frame). It seems logical to bring it in line with APNG and WEBP, which only report latter frames if they're complete. Then it's safe to assume that |index| is complete (or there was an error, or it's the first frame so there's nothing to do). The interesting bit is if the frame is kDisposeOverwritePrevious. In that case, we really want to also save the frame that |index+1| depends on, but we're finding the frame that |index| depends on. It seems like we could check to see if |index+1| has been received yet, and if it has, look at whatever frame it depends on. That is a change in behavior, but it seems weird to mostly rewrite (with a couple meaningful differences) the method while preserving some of its oddities. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:147: // [required_ptrevious_frame, index). Other Blink image decoders instead would required_previous_frame (no 't') https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:148: // have marked the last index in that range. This comment is confusing: - It doesn't really describe what the range is - Other Blink image decoders don't do as much as SkCodec to find the earliest required frame, but they do not necessarily select the most recent frame (see FindRequiredPreviousFrame). That said, after decoding, they do correct to potentially end up with an earlier required frame. I think you want to say something more like: "SkCodec attempts to report the earliest possible required frame, but it is possible that frame has been evicted, while a later frame (which could also be used as the required frame) is still cached. Try to preserve a frame that is still cached." https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:150: // When we clear the cache, we preserve a previous frame if the frame at This tells what, but not why. I think it can be eliminated. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:153: // This means SkCodec-based decoders must override ClearCacheExceptFrame() to This line seems redundant - this is inside the overriding version of ClearCacheExceptFrame. Obviously we needed to override it or this code wouldn't be here. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:155: // kDisposePrevious. They should instead keep the last frame in the range, Don't necessarily need the last frame in the range. We just want to preserve the frame that is still cached (if any). I think this is covered by my proposed comment on line 148. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:163: if (!FrameStatusSufficientForSuccessors(index) || Since this is only called here (i.e. not by the base class), you could have left the base class version of this method alone and called a private method. (Are you thinking that other SkCodec based ImageDecoders will want to share the same version of FrameStatusSufficientForSuccessors, so we might as well update it?) https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:171: !FrameStatusSufficientForSuccessors(index2)) { This call makes sense in theory, although I suspect in practice if the required frame isn't there we'll be going back to the beginning of the image. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:225: frame.SetStatus(ImageFrame::kFrameEmpty); Why is this line necessary? Doesn't frame start off as kFrameEmpty? https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:327: // Any frame in the range [required_previous_frame_index, index) which nit: required_previous_frame_index hasn't been used yet, but maybe it's clear that you mean frame_buffer_cache_[index].RequiredPreviousFrameIndex() (It's certainly more concise!) https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:70: size_t GetPreviousReferenceFrame(size_t index) const; Add a comment, describing what this does? The particular interesting bit is that it returns a reference frame that is FrameComplete. (Maybe there's a concise way to work that into the name somehow... GetViableStartingFrame?)
vmpstr@chromium.org changed reviewers: + vmpstr@chromium.org
(drive-by) I mostly have nits. From the other comments, it seems that this patch is getting close to landing. Is that correct? I think we should start moving in that directly and maybe file follow-up TODOs to clean up awkward-but-working things. WDYT? https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:11: position_(0), nit: you can initialize these in the header and = default here. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:15: SegmentStream::SegmentStream(SegmentStream&& rhs) nit: = default; https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:22: reader_ = rhs.reader_; nit: reader_ = std::move(rhs.reader_); https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:59: size_t peek_position = position_; nit: I don't think you really need this, GetSomeData can just take total_bytes_peeked + position_? https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:93: absolute_position = std::max(absolute_position, 0l); Should this just be a DCHECK absolute_position > 0? Do we have users of this that might move to a negative position? https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:103: is_cleared_ = position_ > reader_size; nit: This seems like a small difference between has_read_all_contents_ and is_cleared_. Can you comment why there is a off-by-one difference here? On a related note, can we use has_read_all_contents_ instead of is_cleared_ throughout here or is this off by one important? https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:64: codec_.reset(SkCodec::NewFromStream(segment_stream_, It seems kind of awkward that a codec_ takes ownership of the stream, but doesn't (publicly) expose the stream as a getter, meaning we need to play around with raw memory... https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:82: segment_stream_ = nullptr; nit: SetFailed will do this? https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:83: SetFailed(); SetFailed DCHECKs that codec_ exists, but it's not too clear to me that that's true in all cases, since we might have got a nullptr from the codec, no? https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:105: case 0: { Please leave a comment about the fact that SkCodec will report 0 for both static images and images that need to only play once https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:70: size_t GetPreviousReferenceFrame(size_t index) const; On 2017/07/17 20:07:56, scroggo_chromium wrote: > Add a comment, describing what this does? > > The particular interesting bit is that it returns a reference frame that is > FrameComplete. (Maybe there's a concise way to work that into the name > somehow... GetViableStartingFrame?) I think a comment is definitely the way to go here (even with a different function name) https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:73: // segment_stream_ is a raw pointer because it passes ownership to Maybe just: |codec_| owns the SegmentStream, but we need a reference to it to append more data as it arrives.
On 2017/07/17 21:28:41, vmpstr wrote: > (drive-by) I mostly have nits. > > From the other comments, it seems that this patch is getting close to landing. > Is that correct? I think we should start moving in that directly and maybe file > follow-up TODOs to clean up awkward-but-working things. WDYT? > moving in that direction*
Description was changed from ========== Use SkCodec internally in GIFImageDecoder Previously, GIFImageDecoder used GIFImageReader internally. SkCodec uses a modified version of that class (SkGifImageReader; adapted in crrev.com/2045293002). SkCodec provides the following benefits: - SIMD optimized code for writing pixels - an API that allows the client to handle caching - flexibility regarding the required frame to use - the ability to decode scaled versions of images - subset decoding (i.e. issue 468914) (not fully implemented for GIF) In addition, this patch enables sharing code between Android, Skia, and Chromium. This means that new features/bug fixes in Android benefit Chromium and vice versa. For larger images (above ~60x60), the SIMD optimizations show a much bigger benefit (up to 24% in one case). For most images, decoding speed is about the same. Images with many frames that contain tiny update regions are a hair slower. The mean decode time across all tested images showed an improvement. Raw performance numbers can be found here: https://docs.google.com/spreadsheets/d/1JqCPdYmbasOwKRdvuG6ZI4gwq9dPvnJA9oDqu... GIFImageDecoder still handles the cached frames currently, but this change will allow future changes in Blink to make wiser caching Decisions (such as keeping all frames of a 3-frame animation if those frames are small). Full road map: https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... This results in some behavior changes: - SkCodec does not check the alpha of each pixel during decode (for speed and simplicity). As a result, GIFImageDecoder no longer corrects opacity or the required frame after decoding a frame. No performance penalty has been observed for incorrectly leaving a frame marked as having transparency. - SkCodec guesses transparency based on the presence of a transparent index (in addition to being subset) and uses this to potentially determine an earlier required frame. BUG=715812 ========== to ========== Use SkCodec internally in GIFImageDecoder Previously, GIFImageDecoder used GIFImageReader internally. SkCodec uses a modified version of that class (SkGifImageReader; adapted in crrev.com/2045293002). SkCodec provides the following benefits: - SIMD optimized code for writing pixels - an API that allows the client to handle caching - flexibility regarding the required frame to use - the ability to decode scaled versions of images - subset decoding (i.e. issue 468914) (not fully implemented for GIF) In addition, this patch enables sharing code between Android, Skia, and Chromium. This means that new features/bug fixes in Android benefit Chromium and vice versa. For larger images (above ~60x60), the SIMD optimizations show a much bigger benefit (up to 24% in one case). For most images, decoding speed is about the same. Images with many frames that contain tiny update regions are a hair slower. The mean decode time across all tested images showed an improvement. Raw performance numbers can be found here: https://docs.google.com/spreadsheets/d/1JqCPdYmbasOwKRdvuG6ZI4gwq9dPvnJA9oDqu... GIFImageDecoder still handles the cached frames currently, but this change will allow future changes in Blink to make wiser caching Decisions (such as keeping all frames of a 3-frame animation if those frames are small). Full road map: https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... This results in some behavior changes: - SkCodec does not check the alpha of each pixel during decode (for speed and simplicity). As a result, GIFImageDecoder no longer corrects opacity or the required frame after decoding a frame. No performance penalty has been observed for incorrectly leaving a frame marked as having transparency. - SkCodec guesses transparency based on the presence of a transparent index (in addition to being subset) and uses this to potentially determine an earlier required frame. BUG=715812 ==========
> From the other comments, it seems that this patch is getting close to landing. > Is that correct? I think we should start moving in that directly and maybe file > follow-up TODOs to clean up awkward-but-working things. WDYT? I would like that very much. The only TODO bits left are behavioral changes I believe. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:11: position_(0), On 2017/07/17 21:28:40, vmpstr wrote: > nit: you can initialize these in the header and = default here. Done. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:15: SegmentStream::SegmentStream(SegmentStream&& rhs) On 2017/07/17 21:28:40, vmpstr wrote: > nit: = default; No can do on this one. This class derives from SkStream, which derives from SkNoncopyable. SkNoncopyable provides an explicitly defaulted move constructor. However, SkStream does not provide any implicit copy / move constructors. So the implicitly generated move constructor would rely on the deleted copy constructor. Implicitly defaulted move != explicitly defaulted move. It ends up giving this error: error: defaulting this move constructor would delete it after its first declaration If we want, we can solve this by adding an explicitly defaulted move constructor in SkStream. What I provided here is the alternative. Notice I'm not calling : SkStream(std::move(rhs)), ... https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:22: reader_ = rhs.reader_; On 2017/07/17 21:28:40, vmpstr wrote: > nit: reader_ = std::move(rhs.reader_); Done. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:59: size_t peek_position = position_; On 2017/07/17 21:28:40, vmpstr wrote: > nit: I don't think you really need this, GetSomeData can just take > total_bytes_peeked + position_? Done. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:93: absolute_position = std::max(absolute_position, 0l); On 2017/07/17 21:28:40, vmpstr wrote: > Should this just be a DCHECK absolute_position > 0? Do we have users of this > that might move to a negative position? It has been a while. I'll change this to a DCHECK and see if it passes the tests. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:103: is_cleared_ = position_ > reader_size; On 2017/07/17 21:28:40, vmpstr wrote: > nit: This seems like a small difference between has_read_all_contents_ and > is_cleared_. Can you comment why there is a off-by-one difference here? On a > related note, can we use has_read_all_contents_ instead of is_cleared_ > throughout here or is this off by one important? The off-by-one is indeed important. Having read the last byte is covered by the >= for has_read_all_contents_. If we get to the end of the buffer, that doesn't mean the buffer has been cleared. When we change the reader's size to be smaller (and thus the old position is now past the end), the stream has been cleared. We need to know when this happens so we can exit early when attempting to decode. When you said "Can you comment" did you mean here or adding a comment into the code, since this is not perfectly clear? FWIW, this is covered by tests. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:64: codec_.reset(SkCodec::NewFromStream(segment_stream_, On 2017/07/17 21:28:40, vmpstr wrote: > It seems kind of awkward that a codec_ takes ownership of the stream, but > doesn't (publicly) expose the stream as a getter, meaning we need to play around > with raw memory... I agree. One option would be a shared ownership. I'm not a fan of shared ownership if it is avoidable. Another option is a getter, like you said. scroggo@ Can we add this to SkCodec? https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:82: segment_stream_ = nullptr; On 2017/07/17 21:28:40, vmpstr wrote: > nit: SetFailed will do this? Done. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:83: SetFailed(); On 2017/07/17 21:28:40, vmpstr wrote: > SetFailed DCHECKs that codec_ exists, but it's not too clear to me that that's > true in all cases, since we might have got a nullptr from the codec, no? You are right. The call to SetFailed() still needs to happen (and the call further up, to ImageDecoder::SetFailed()). I think the DCHECK should go. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:105: case 0: { On 2017/07/17 21:28:40, vmpstr wrote: > Please leave a comment about the fact that SkCodec will report 0 for both static > images and images that need to only play once Done. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:145: size_t GIFImageDecoder::ClearCacheExceptFrame(size_t index) { On 2017/07/17 20:07:56, scroggo_chromium wrote: > That said, this method seems much more complicated than necessary. I may agree. I kept it as similar as possible to the existing code to minimize friction of landing this patch. > The first loop checks whether FrameStatusSufficientForSuccessors, which means > true if the Frame is FramePartial or FrameComplete. Will that ever return false? That depends on the client behavior. If we assume the current client behavior which calls ClearCacheExceptFrame(frame_we_just_decoded) then it will never return false. But the client is free to pass in whatever index they want. For example, ClearCacheExceptFrame(some_frame_we_just_cleared); In an effort to not rely on behavior at a distance, I wouldn't recommend changing that. > - GIF is currently the only multi-frame decoder that even reports that a frame > exists if it is not yet complete (besides the first frame). It seems logical to > bring it in line with APNG and WEBP, which only report latter frames if they're > complete. Then it's safe to assume that |index| is complete (or there was an > error, or it's the first frame so there's nothing to do). I really like this idea. But I believe it belongs in the refactor phase. My goal here is to make as few changes as possible to ease SkCodec adoption. Even small behavior changes like no longer correcting opaque frames has added months to this. > The interesting bit is if the frame is kDisposeOverwritePrevious. In that case, > we really want to also save the frame that |index+1| depends on, but we're > finding the frame that |index| depends on. You're completely correct about this. If |index| is marked as kDisposeOverwritePrevious then we need some prior frame. And rather than guess which prior frame, we could just ask SkCodec. If we have that information, it is silly that we don't use it. Like the issue above, the effects of this change are visible outside of GIFImageDecoder. They are behavior changes. Changing this would be an improvement (a big one!) if the incorrect frame is guessed. I would LOVE to fix this. But since the same code would need the same approvals and buy-in, there is an advantage to landing SkCodec without this improvement and then adding it right after. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:147: // [required_ptrevious_frame, index). Other Blink image decoders instead would On 2017/07/17 20:07:56, scroggo_chromium wrote: > required_previous_frame (no 't') Done. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:148: // have marked the last index in that range. On 2017/07/17 20:07:56, scroggo_chromium wrote: > This comment is confusing: > - It doesn't really describe what the range is > - Other Blink image decoders don't do as much as SkCodec to find the earliest > required frame, but they do not necessarily select the most recent frame (see > FindRequiredPreviousFrame). That said, after decoding, they do correct to > potentially end up with an earlier required frame. > > I think you want to say something more like: > "SkCodec attempts to report the earliest possible required frame, but it is > possible that frame has been evicted, while a later frame (which could also be > used as the required frame) is still cached. Try to preserve a frame that is > still cached." > Done. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:150: // When we clear the cache, we preserve a previous frame if the frame at On 2017/07/17 20:07:56, scroggo_chromium wrote: > This tells what, but not why. I think it can be eliminated. Done. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:153: // This means SkCodec-based decoders must override ClearCacheExceptFrame() to On 2017/07/17 20:07:56, scroggo_chromium wrote: > This line seems redundant - this is inside the overriding version of > ClearCacheExceptFrame. Obviously we needed to override it or this code wouldn't > be here. Done. I wanted to illustrate that it was really for SkCodec and not for gif. After a second SkCodec-based decoder lands I want to refactor out common code like this. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:155: // kDisposePrevious. They should instead keep the last frame in the range, On 2017/07/17 20:07:56, scroggo_chromium wrote: > Don't necessarily need the last frame in the range. I removed this comment and went with what you put on line 148. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:163: if (!FrameStatusSufficientForSuccessors(index) || On 2017/07/17 20:07:56, scroggo_chromium wrote: > Since this is only called here (i.e. not by the base class), you could have left > the base class version of this method alone and called a private method. > > (Are you thinking that other SkCodec based ImageDecoders will want to share the > same version of FrameStatusSufficientForSuccessors, so we might as well update > it?) The base class version needed to be updated. It only checked for != Empty. But it also needs to consider != Allocated now (which this patch added). Other decoders happened to not ever set to Allocated, so it happened to work. But it wasn't really saying what it needed. In fact, "!= Empty" wasn't previously saying what it needed, either. It needed == Complete || == Partial. There were only the 3 values, so it happened to cover the same thing. But it wasn't future-proof. If any other status was added, it would also cause a similar situation. So really, the base version was updated to just be more accurate / descriptive / not rely on distant code being just-so. It is less fragile now. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:171: !FrameStatusSufficientForSuccessors(index2)) { On 2017/07/17 20:07:56, scroggo_chromium wrote: > This call makes sense in theory, although I suspect in practice if the required > frame isn't there we'll be going back to the beginning of the image. I think you are right. I think the call to GetPreviousReferenceFrame() (now renamed) in the previous loop takes care of everything here. I don't think we need this second loop. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:225: frame.SetStatus(ImageFrame::kFrameEmpty); On 2017/07/17 20:07:56, scroggo_chromium wrote: > Why is this line necessary? Doesn't frame start off as kFrameEmpty? Done. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:327: // Any frame in the range [required_previous_frame_index, index) which On 2017/07/17 20:07:56, scroggo_chromium wrote: > nit: required_previous_frame_index hasn't been used yet, but maybe it's clear > that you mean > > frame_buffer_cache_[index].RequiredPreviousFrameIndex() > > (It's certainly more concise!) I moved the declaration of required_previous_Frame_index above the comment. At first, I worried that it would be awkward. But I think it actually makes the code (and this comment) a bit easier to digest. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:70: size_t GetPreviousReferenceFrame(size_t index) const; On 2017/07/17 21:28:41, vmpstr wrote: > On 2017/07/17 20:07:56, scroggo_chromium wrote: > > Add a comment, describing what this does? > > > > The particular interesting bit is that it returns a reference frame that is > > FrameComplete. (Maybe there's a concise way to work that into the name > > somehow... GetViableStartingFrame?) > > I think a comment is definitely the way to go here (even with a different > function name) Done. The comment (and new name) I think better describe why the function exists and what it does. I think it does this succinctly. There is also a comment in the implementation explaining it in greater detail. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:73: // segment_stream_ is a raw pointer because it passes ownership to On 2017/07/17 21:28:41, vmpstr wrote: > Maybe just: |codec_| owns the SegmentStream, but we need a reference to it to > append more data as it arrives. Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:15: SegmentStream::SegmentStream(SegmentStream&& rhs) On 2017/07/18 10:23:48, cblume wrote: > On 2017/07/17 21:28:40, vmpstr wrote: > > nit: = default; > > No can do on this one. > > This class derives from SkStream, which derives from SkNoncopyable. > SkNoncopyable provides an explicitly defaulted move constructor. > > However, SkStream does not provide any implicit copy / move constructors. So the > implicitly generated move constructor would rely on the deleted copy > constructor. > > Implicitly defaulted move != explicitly defaulted move. > It ends up giving this error: > error: defaulting this move constructor would delete it after its first > declaration > > If we want, we can solve this by adding an explicitly defaulted move constructor > in SkStream. > What I provided here is the alternative. Notice I'm not calling : > SkStream(std::move(rhs)), ... *However, SkStream does not provide any *EXPLICIT* copy / cove constructors...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-simulator on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator/bui...)
https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2140001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:257: if (!index) { On 2017/07/13 12:31:06, scroggo_chromium wrote: > On 2017/07/12 21:22:41, cblume wrote: > > On 2017/07/11 20:36:39, scroggo_chromium wrote: > > > Except that parsing isn't the only case where we can see an error. There can > > > also be an error in decoding. (i.e. in doLZW) > > > > Ah, right. Good call. > > > > I spoke to vmpstr@ to find where Blink makes the decision whether or not to > > paint a frame that is partial for an animation. > > It looks like it is here: > > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/graph... > > > > It only seems to check if a frame is complete in an animation, meaning we can > > leave it marked as partial and that is fine. It is more correct as well. > > That code is misleading, though. Although the method is named > FrameIsCompleteAtIndex, it is really just checking to see whether the parse > marked it as complete. If there is an error in the frame, I don't think we > handle that case. Now that we zero all the memory ahead of time, it's not as big > of a deal, since we are not showing uninitialized memory, but if I'm right, we > might show part of the old frame and part of the new one. This behavior doesn't > change with your patch (now that it zeroes the memory first), though. > > > > > > Again, ImageFrameGenerator doesn't treat Empty differently from Partial > > (besides > > > deciding whether to call SetHasAlpha). > > > > You're right. > Filed https://bugs.chromium.org/p/chromium/issues/detail?id=745819 https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:93: absolute_position = std::max(absolute_position, 0l); On 2017/07/18 10:23:48, cblume wrote: > On 2017/07/17 21:28:40, vmpstr wrote: > > Should this just be a DCHECK absolute_position > 0? Do we have users of this > > that might move to a negative position? > > It has been a while. I'll change this to a DCHECK and see if it passes the > tests. SkGifCodec won't call move at all (it uses seek instead). https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:64: codec_.reset(SkCodec::NewFromStream(segment_stream_, On 2017/07/18 10:23:49, cblume wrote: > On 2017/07/17 21:28:40, vmpstr wrote: > > It seems kind of awkward that a codec_ takes ownership of the stream, but > > doesn't (publicly) expose the stream as a getter, meaning we need to play > around > > with raw memory... > > I agree. > > One option would be a shared ownership. I'm not a fan of shared ownership if it > is avoidable. SkStream was originally meant to be something you couldn't add more data to later, which is why it doesn't allow shared ownership. I've been thinking about some other type of input for SkCodec, where it would make more sense to have some kind of shared ownership... > Another option is a getter, like you said. > > scroggo@ Can we add this to SkCodec? See https://skia-review.googlesource.com/c/24402/ https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:145: size_t GIFImageDecoder::ClearCacheExceptFrame(size_t index) { On 2017/07/18 10:23:49, cblume wrote: > On 2017/07/17 20:07:56, scroggo_chromium wrote: > > That said, this method seems much more complicated than necessary. > > I may agree. I kept it as similar as possible to the existing code to minimize > friction of landing this patch. Makes sense. > > > The first loop checks whether FrameStatusSufficientForSuccessors, which means > > true if the Frame is FramePartial or FrameComplete. Will that ever return > false? > > That depends on the client behavior. > If we assume the current client behavior which calls > ClearCacheExceptFrame(frame_we_just_decoded) then it will never return false. > > But the client is free to pass in whatever index they want. > For example, ClearCacheExceptFrame(some_frame_we_just_cleared); > > In an effort to not rely on behavior at a distance, I wouldn't recommend > changing that. That seems okay... Maybe add a TODO, bug, or at least a note? (Perhaps the better fix is not fix this exact situation until we move caching out of ImageDecoder entirely? I thought about suggesting that ClearCacheExceptFrame be called *by* Decode, which would match the current behavior (mostly) and we wouldn't need to worry about behavior at a distance...) > > > > - GIF is currently the only multi-frame decoder that even reports that a frame > > exists if it is not yet complete (besides the first frame). It seems logical > to > > bring it in line with APNG and WEBP, which only report latter frames if > they're > > complete. Then it's safe to assume that |index| is complete (or there was an > > error, or it's the first frame so there's nothing to do). > > I really like this idea. Filed crbug.com/745820 > > But I believe it belongs in the refactor phase. > My goal here is to make as few changes as possible to ease SkCodec adoption. > Even small behavior changes like no longer correcting opaque frames has added > months to this. > > > > > The interesting bit is if the frame is kDisposeOverwritePrevious. In that > case, > > we really want to also save the frame that |index+1| depends on, but we're > > finding the frame that |index| depends on. > > You're completely correct about this. > If |index| is marked as kDisposeOverwritePrevious then we need some prior frame. > And rather than guess which prior frame, we could just ask SkCodec. If we have > that information, it is silly that we don't use it. > > Like the issue above, the effects of this change are visible outside of > GIFImageDecoder. They are behavior changes. > Changing this would be an improvement (a big one!) if the incorrect frame is > guessed. > I would LOVE to fix this. But since the same code would need the same approvals > and buy-in, there is an advantage to landing SkCodec without this improvement > and then adding it right after. Add a TODO, note, bug? https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:171: !FrameStatusSufficientForSuccessors(index2)) { On 2017/07/18 10:23:49, cblume wrote: > On 2017/07/17 20:07:56, scroggo_chromium wrote: > > This call makes sense in theory, although I suspect in practice if the > required > > frame isn't there we'll be going back to the beginning of the image. > > I think you are right. > > I think the call to GetPreviousReferenceFrame() (now renamed) in the previous > loop takes care of everything here. I don't think we need this second loop. Now that I look at it more closely, if nothing in the range was sufficient, GetPreviousReferenceFrame returns kNotFound. So the second loop wouldn't do anything. But if we're not relying on the fact that ClearCacheExceptFrame is always called after decoding the frame that is passed in as a parameter, it is theoretically possible that the frame that |index| depends on is not there, but the frame it depends on is. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:73: // segment_stream_ is a raw pointer because it passes ownership to On 2017/07/18 10:23:49, cblume wrote: > On 2017/07/17 21:28:41, vmpstr wrote: > > Maybe just: |codec_| owns the SegmentStream, but we need a reference to it to > > append more data as it arrives. > > Done. I think Chris originally used the word "reference", but I discouraged it because it sounded like it was reference counted, and it is not. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:144: size_t GIFImageDecoder::ClearCacheExceptFrame(size_t index) { Since we're overriding this method, please remove the comments on the baseclass suggesting that it's virtual just for a test method and we should remove that: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:326: DCHECK(dependent_index); I think this DCHECK might fire in the following situation: - decode all frames of an animated image - loop, and decode frame 0 - call ClearCacheExceptFrame(0) ClearCacheExceptFrame checks for frame_buffer_cache_.size() <= 1, but not for index == 0
https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:15: SegmentStream::SegmentStream(SegmentStream&& rhs) On 2017/07/18 10:45:57, cblume wrote: > On 2017/07/18 10:23:48, cblume wrote: > > On 2017/07/17 21:28:40, vmpstr wrote: > > > nit: = default; > > > > No can do on this one. > > > > This class derives from SkStream, which derives from SkNoncopyable. > > SkNoncopyable provides an explicitly defaulted move constructor. > > > > However, SkStream does not provide any implicit copy / move constructors. So > the > > implicitly generated move constructor would rely on the deleted copy > > constructor. Acknowledged. I think there is no implicitly generated move ctor here at all due to there being a dtor. The code you have is fine as is, but SkStream should probably provide a defaulted move ctor since it's meant to be derived from with a possibility of being moved (just for code health reasons). > > > > Implicitly defaulted move != explicitly defaulted move. > > It ends up giving this error: > > error: defaulting this move constructor would delete it after its first > > declaration > > > > If we want, we can solve this by adding an explicitly defaulted move > constructor > > in SkStream. > > What I provided here is the alternative. Notice I'm not calling : > > SkStream(std::move(rhs)), ... > > *However, SkStream does not provide any *EXPLICIT* copy / cove constructors.. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:103: is_cleared_ = position_ > reader_size; On 2017/07/18 10:23:48, cblume wrote: > On 2017/07/17 21:28:40, vmpstr wrote: > > nit: This seems like a small difference between has_read_all_contents_ and > > is_cleared_. Can you comment why there is a off-by-one difference here? On a > > related note, can we use has_read_all_contents_ instead of is_cleared_ > > throughout here or is this off by one important? > > The off-by-one is indeed important. > > Having read the last byte is covered by the >= for has_read_all_contents_. > If we get to the end of the buffer, that doesn't mean the buffer has been > cleared. > > When we change the reader's size to be smaller (and thus the old position is now > past the end), the stream has been cleared. > We need to know when this happens so we can exit early when attempting to > decode. > > When you said "Can you comment" did you mean here or adding a comment into the > code, since this is not perfectly clear? > FWIW, this is covered by tests. Yeah I meant can you leave a comment somewhere in the code? Either here, or in the header where these variables are declared just so it's obvious that this distinction is important.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: android_compile_dbg on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/android_comp...)
> SkCodec provides the following benefits: > - SIMD optimized code for writing pixels > - an API that allows the client to handle caching > - flexibility regarding the required frame to use > - the ability to decode scaled versions of images > - subset decoding (i.e. issue 468914) > (not fully implemented for GIF) One benefit I forgot to mention - SkCodec supports F16 already. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.h (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.h:24: bool IsCleared() const { return is_cleared_; } Add a comment describing what this means? https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:108: if (IsAllDataReceived() && frame_count == 1) nit: No need for this variable. This could be: if (IsAllDataReceived() && codec_->getFrameCount() == 1) https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:140: codec_ = nullptr; nit: I prefer codec_.reset(); here. It's more obvious that it's deleting something. (At least to me.) https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:157: // We need to preserve a previous frame This comment probably doesn't add much. (Tells a what, but not a why. And ClearCacheExceptTwoFrames makes it clear we're preserving this one.) https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:167: return 0; In the old code, we returned the existing number of frames if we had failed. Since SetFailed() deletes codec_, we'll never reach the below code if we've failed. I think we want something like: if (!codec_ || segment_stream_->IsCleared()) return frame_buffer_cache_.size(); return codec_->getFrameCount(); https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:187: codec_->getFrameInfo(index, &frame_info); This method returns a boolean. We should probably check its return value. That said, looking at its implementation, it should only fail if index is out of range, or if we haven't parsed enough data to know the stuff you're about to put on the ImageFrame. The only way that would currently happen is if it's the first frame (later frames we don't report until they at least have this much data received). I have posted a fix in codec here: https://skia-review.googlesource.com/c/24405/ https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:241: bool oldAlpha = frame.HasAlpha(); I know we've gone back and forth on this, but maybe add a comment about why you have to do this? Something like: // ZeroFillPixelData() sets HasAlpha to true, but there are other // safeguards that ensure that a Partial ImageFrame doesn't claim to // be opaque. Set it back to the intended state, which was set by // InitializeNewFrame That said, if we have to go through so much work to go against ZeroFillPixelData's desires, why not just re-set it once the ImageFrame is Complete? That seems like it would be simpler. // Once complete... FrameInfo frame_info; codec_->getFrameInfo(index, &frame_info); frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_info.fAlphaType)); https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:250: ImageFrame& previous_frame = frame_buffer_cache_[previous_frame_index]; It seems like this could be a part of the prior if statement. We'll only need to decode the prior frame if GetViableReferenceFrameIndex returned kNotFound, right? https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:268: if (frame.GetStatus() == ImageFrame::kFrameAllocated) { It took me a while to get on board with kFrameAllocated. I see why you added it, but I kept thinking there might be a simpler way. But as I look at TakeBitmapDataIfWritable and CopyBitmapData, I see that they used to result in the frame having different states! Copy left it kFrameEmpty, and Take left it kFrameComplete (neither of which seems correct). So now you've unified them and they share the same Status :) https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:282: } nit: Add a newline after this? That way we separate the different stages in this method: if Empty -> do this <new line> (already present) if Allocated -> do this <new line> decode https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:283: int rows_decoded = 0; Now that we zero pixels ahead of time, this variable is not used. Please remove. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:296: return; These returns feel arbitrary to me. If you left this one out, we'd fall through to break, and there's nothing left after the switch statement. So I'd remove it. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:302: return; I support putting either a break or a return here, but given that the method is over, I'd push for break. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:330: for (size_t i = dependent_index - 1; i != required_previous_frame_index; Specify why we go in reverse? https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:39: class PLATFORM_EXPORT GIFImageDecoder final : public blink::ImageDecoder { Why did you add "blink::"? https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:62: void InitializeNewFrame(size_t index) override; nit: It's been recommended to me to leave "index" out of these declarations. We were already inconsistent about this, though.
Description was changed from ========== Use SkCodec internally in GIFImageDecoder Previously, GIFImageDecoder used GIFImageReader internally. SkCodec uses a modified version of that class (SkGifImageReader; adapted in crrev.com/2045293002). SkCodec provides the following benefits: - SIMD optimized code for writing pixels - an API that allows the client to handle caching - flexibility regarding the required frame to use - the ability to decode scaled versions of images - subset decoding (i.e. issue 468914) (not fully implemented for GIF) In addition, this patch enables sharing code between Android, Skia, and Chromium. This means that new features/bug fixes in Android benefit Chromium and vice versa. For larger images (above ~60x60), the SIMD optimizations show a much bigger benefit (up to 24% in one case). For most images, decoding speed is about the same. Images with many frames that contain tiny update regions are a hair slower. The mean decode time across all tested images showed an improvement. Raw performance numbers can be found here: https://docs.google.com/spreadsheets/d/1JqCPdYmbasOwKRdvuG6ZI4gwq9dPvnJA9oDqu... GIFImageDecoder still handles the cached frames currently, but this change will allow future changes in Blink to make wiser caching Decisions (such as keeping all frames of a 3-frame animation if those frames are small). Full road map: https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... This results in some behavior changes: - SkCodec does not check the alpha of each pixel during decode (for speed and simplicity). As a result, GIFImageDecoder no longer corrects opacity or the required frame after decoding a frame. No performance penalty has been observed for incorrectly leaving a frame marked as having transparency. - SkCodec guesses transparency based on the presence of a transparent index (in addition to being subset) and uses this to potentially determine an earlier required frame. BUG=715812 ========== to ========== Use SkCodec internally in GIFImageDecoder Previously, GIFImageDecoder used GIFImageReader internally. SkCodec uses a modified version of that class (SkGifImageReader; adapted in crrev.com/2045293002). SkCodec provides the following benefits: - SIMD optimized code for writing pixels - an API that allows the client to handle caching - flexibility regarding the required frame to use - the ability to decode scaled versions of images - subset decoding (i.e. issue 468914) (not fully implemented for GIF) - the ability to decode to half-width float format In addition, this patch enables sharing code between Android, Skia, and Chromium. This means that new features/bug fixes in Android benefit Chromium and vice versa. For larger images (above ~60x60), the SIMD optimizations show a much bigger benefit (up to 24% in one case). For most images, decoding speed is about the same. Images with many frames that contain tiny update regions are a hair slower. The mean decode time across all tested images showed an improvement. Raw performance numbers can be found here: https://docs.google.com/spreadsheets/d/1JqCPdYmbasOwKRdvuG6ZI4gwq9dPvnJA9oDqu... GIFImageDecoder still handles the cached frames currently, but this change will allow future changes in Blink to make wiser caching Decisions (such as keeping all frames of a 3-frame animation if those frames are small). Full road map: https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... This results in some behavior changes: - SkCodec does not check the alpha of each pixel during decode (for speed and simplicity). As a result, GIFImageDecoder no longer corrects opacity or the required frame after decoding a frame. No performance penalty has been observed for incorrectly leaving a frame marked as having transparency. - SkCodec guesses transparency based on the presence of a transparent index (in addition to being subset) and uses this to potentially determine an earlier required frame. BUG=715812 ==========
https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:15: SegmentStream::SegmentStream(SegmentStream&& rhs) On 2017/07/18 18:02:48, vmpstr wrote: > On 2017/07/18 10:45:57, cblume wrote: > > On 2017/07/18 10:23:48, cblume wrote: > > > On 2017/07/17 21:28:40, vmpstr wrote: > > > > nit: = default; > > > > > > No can do on this one. > > > > > > This class derives from SkStream, which derives from SkNoncopyable. > > > SkNoncopyable provides an explicitly defaulted move constructor. > > > > > > However, SkStream does not provide any implicit copy / move constructors. So > > the > > > implicitly generated move constructor would rely on the deleted copy > > > constructor. > > Acknowledged. I think there is no implicitly generated move ctor here at all due > to there being a dtor. The code you have is fine as is, but SkStream should > probably provide a defaulted move ctor since it's meant to be derived from with > a possibility of being moved (just for code health reasons). > > > > > > > Implicitly defaulted move != explicitly defaulted move. > > > It ends up giving this error: > > > error: defaulting this move constructor would delete it after its first > > > declaration > > > > > > If we want, we can solve this by adding an explicitly defaulted move > > constructor > > > in SkStream. > > > What I provided here is the alternative. Notice I'm not calling : > > > SkStream(std::move(rhs)), ... > > > > *However, SkStream does not provide any *EXPLICIT* copy / cove constructors.. I made a Skia patch for it: https://skia-review.googlesource.com/c/24580/ Once that lands, I'll update this. That's the much nicer, cleaner approach. :) https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:93: absolute_position = std::max(absolute_position, 0l); On 2017/07/18 17:52:41, scroggo_chromium wrote: > On 2017/07/18 10:23:48, cblume wrote: > > On 2017/07/17 21:28:40, vmpstr wrote: > > > Should this just be a DCHECK absolute_position > 0? Do we have users of this > > > that might move to a negative position? > > > > It has been a while. I'll change this to a DCHECK and see if it passes the > > tests. > > SkGifCodec won't call move at all (it uses seek instead). Do you happen to know about the other decoders? This class should end up being used by them all. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:103: is_cleared_ = position_ > reader_size; On 2017/07/18 18:02:48, vmpstr wrote: > On 2017/07/18 10:23:48, cblume wrote: > > On 2017/07/17 21:28:40, vmpstr wrote: > > > nit: This seems like a small difference between has_read_all_contents_ and > > > is_cleared_. Can you comment why there is a off-by-one difference here? On a > > > related note, can we use has_read_all_contents_ instead of is_cleared_ > > > throughout here or is this off by one important? > > > > The off-by-one is indeed important. > > > > Having read the last byte is covered by the >= for has_read_all_contents_. > > If we get to the end of the buffer, that doesn't mean the buffer has been > > cleared. > > > > When we change the reader's size to be smaller (and thus the old position is > now > > past the end), the stream has been cleared. > > We need to know when this happens so we can exit early when attempting to > > decode. > > > > When you said "Can you comment" did you mean here or adding a comment into the > > code, since this is not perfectly clear? > > FWIW, this is covered by tests. > > Yeah I meant can you leave a comment somewhere in the code? Either here, or in > the header where these variables are declared just so it's obvious that this > distinction is important. I'll put a comment in the header. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:145: size_t GIFImageDecoder::ClearCacheExceptFrame(size_t index) { On 2017/07/18 17:52:42, scroggo_chromium wrote: > On 2017/07/18 10:23:49, cblume wrote: > > On 2017/07/17 20:07:56, scroggo_chromium wrote: > > > That said, this method seems much more complicated than necessary. > > > > I may agree. I kept it as similar as possible to the existing code to minimize > > friction of landing this patch. > > Makes sense. > > > > > > The first loop checks whether FrameStatusSufficientForSuccessors, which > means > > > true if the Frame is FramePartial or FrameComplete. Will that ever return > > false? > > > > That depends on the client behavior. > > If we assume the current client behavior which calls > > ClearCacheExceptFrame(frame_we_just_decoded) then it will never return false. > > > > But the client is free to pass in whatever index they want. > > For example, ClearCacheExceptFrame(some_frame_we_just_cleared); > > > > In an effort to not rely on behavior at a distance, I wouldn't recommend > > changing that. > > That seems okay... Maybe add a TODO, bug, or at least a note? (Perhaps the > better fix is not fix this exact situation until we move caching out of > ImageDecoder entirely? I thought about suggesting that ClearCacheExceptFrame be > called *by* Decode, which would match the current behavior (mostly) and we > wouldn't need to worry about behavior at a distance...) Yeah, I think this will just disappear once the cache is moved out of the ImageDecoder. I filed https://crbug.com/745967 (specifically, for the situation below) which should cover this. I suspect the path would be: - land this - land a patch for this new bug, since it should be quick & easy (but a behavior change) - later, move the cache out of the ImageDecoder (at which point we can use the cleaned up code) > > > - GIF is currently the only multi-frame decoder that even reports that a > frame > > > exists if it is not yet complete (besides the first frame). It seems logical > > to > > > bring it in line with APNG and WEBP, which only report latter frames if > > they're > > > complete. Then it's safe to assume that |index| is complete (or there was an > > > error, or it's the first frame so there's nothing to do). > > > > I really like this idea. > > Filed crbug.com/745820 > > > > > But I believe it belongs in the refactor phase. > > My goal here is to make as few changes as possible to ease SkCodec adoption. > > Even small behavior changes like no longer correcting opaque frames has added > > months to this. > > > > > > > > > The interesting bit is if the frame is kDisposeOverwritePrevious. In that > > case, > > > we really want to also save the frame that |index+1| depends on, but we're > > > finding the frame that |index| depends on. > > > > You're completely correct about this. > > If |index| is marked as kDisposeOverwritePrevious then we need some prior > frame. > > And rather than guess which prior frame, we could just ask SkCodec. If we have > > that information, it is silly that we don't use it. > > > > Like the issue above, the effects of this change are visible outside of > > GIFImageDecoder. They are behavior changes. > > Changing this would be an improvement (a big one!) if the incorrect frame is > > guessed. > > I would LOVE to fix this. But since the same code would need the same > approvals > > and buy-in, there is an advantage to landing SkCodec without this improvement > > and then adding it right after. > > Add a TODO, note, bug? Filed https://crbug.com/745967 https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:171: !FrameStatusSufficientForSuccessors(index2)) { On 2017/07/18 17:52:42, scroggo_chromium wrote: > On 2017/07/18 10:23:49, cblume wrote: > > On 2017/07/17 20:07:56, scroggo_chromium wrote: > > > This call makes sense in theory, although I suspect in practice if the > > required > > > frame isn't there we'll be going back to the beginning of the image. > > > > I think you are right. > > > > I think the call to GetPreviousReferenceFrame() (now renamed) in the previous > > loop takes care of everything here. I don't think we need this second loop. > > Now that I look at it more closely, if nothing in the range was sufficient, > GetPreviousReferenceFrame returns kNotFound. So the second loop wouldn't do > anything. Right. I actually removed the second loop. The second loop made sense before I created GetPreviousReferenceFrame(). That function essentially contained the second loop. > But if we're not relying on the fact that ClearCacheExceptFrame is always called > after decoding the frame that is passed in as a parameter, it is theoretically > possible that the frame that |index| depends on is not there, but the frame it > depends on is. I think it is fair that if the client provides an index to a cleared frame we effectively don't save anything. We're really preserving the second frame secretly to speed up future decodes. But the API itself sounds like it purges all but 1 frame, not all but 2. If the frame the client attempted to preserve is already purged, I think it is okay that we do not preserve its reference frame. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:73: // segment_stream_ is a raw pointer because it passes ownership to On 2017/07/18 17:52:42, scroggo_chromium wrote: > On 2017/07/18 10:23:49, cblume wrote: > > On 2017/07/17 21:28:41, vmpstr wrote: > > > Maybe just: |codec_| owns the SegmentStream, but we need a reference to it > to > > > append more data as it arrives. > > > > Done. > > I think Chris originally used the word "reference", but I discouraged it because > it sounded like it was reference counted, and it is not. Done. I changed vmpstr@'s suggested comment to say "...but we need access to it..." to remove the word "reference". https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.h (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.h:24: bool IsCleared() const { return is_cleared_; } On 2017/07/18 21:10:09, scroggo_chromium wrote: > Add a comment describing what this means? Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:108: if (IsAllDataReceived() && frame_count == 1) On 2017/07/18 21:10:10, scroggo_chromium wrote: > nit: No need for this variable. This could be: > > if (IsAllDataReceived() && codec_->getFrameCount() == 1) Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:140: codec_ = nullptr; On 2017/07/18 21:10:09, scroggo_chromium wrote: > nit: I prefer > > codec_.reset(); > > here. It's more obvious that it's deleting something. (At least to me.) I hadn't considered this before. I like it. Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:144: size_t GIFImageDecoder::ClearCacheExceptFrame(size_t index) { On 2017/07/18 17:52:42, scroggo_chromium wrote: > Since we're overriding this method, please remove the comments on the baseclass > suggesting that it's virtual just for a test method and we should remove that: > > https://cs.chromium.org/chromium/src/third_party/WebKit/Source/platform/image... Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:157: // We need to preserve a previous frame On 2017/07/18 21:10:09, scroggo_chromium wrote: > This comment probably doesn't add much. (Tells a what, but not a why. And > ClearCacheExceptTwoFrames makes it clear we're preserving this one.) Yeah, I agree. I think this comment is useless. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:167: return 0; On 2017/07/18 21:10:10, scroggo_chromium wrote: > In the old code, we returned the existing number of frames if we had failed. > Since SetFailed() deletes codec_, we'll never reach the below code if we've > failed. > > I think we want something like: > > if (!codec_ || segment_stream_->IsCleared()) > return frame_buffer_cache_.size(); > > return codec_->getFrameCount(); Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:187: codec_->getFrameInfo(index, &frame_info); On 2017/07/18 21:10:10, scroggo_chromium wrote: > This method returns a boolean. We should probably check its return value. > > That said, looking at its implementation, it should only fail if index is out of > range, or if we haven't parsed enough data to know the stuff you're about to put > on the ImageFrame. > > The only way that would currently happen is if it's the first frame (later > frames we don't report until they at least have this much data received). > > I have posted a fix in codec here: https://skia-review.googlesource.com/c/24405/ Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:241: bool oldAlpha = frame.HasAlpha(); On 2017/07/18 21:10:09, scroggo_chromium wrote: > I know we've gone back and forth on this, but maybe add a comment about why you > have to do this? Something like: > > // ZeroFillPixelData() sets HasAlpha to true, but there are other > // safeguards that ensure that a Partial ImageFrame doesn't claim to > // be opaque. Set it back to the intended state, which was set by > // InitializeNewFrame > > That said, if we have to go through so much work to go against > ZeroFillPixelData's desires, why not just re-set it once the ImageFrame is > Complete? That seems like it would be simpler. > > // Once complete... > FrameInfo frame_info; > codec_->getFrameInfo(index, &frame_info); > frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_info.fAlphaType)); I'm pretty sure we tried SkAlphaTypeIsOpaque() before and ran into a problem. I tested it out just now and it causes tests to fail. It looks like opaque images are being marked as transparent. I can look into this more if you want but I'm pretty sure I already did this. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:250: ImageFrame& previous_frame = frame_buffer_cache_[previous_frame_index]; On 2017/07/18 21:10:10, scroggo_chromium wrote: > It seems like this could be a part of the prior if statement. We'll only need to > decode the prior frame if GetViableReferenceFrameIndex returned kNotFound, > right? Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:268: if (frame.GetStatus() == ImageFrame::kFrameAllocated) { On 2017/07/18 21:10:10, scroggo_chromium wrote: > It took me a while to get on board with kFrameAllocated. I see why you added it, > but I kept thinking there might be a simpler way. > > But as I look at TakeBitmapDataIfWritable and CopyBitmapData, I see that they > used to result in the frame having different states! Copy left it kFrameEmpty, > and Take left it kFrameComplete (neither of which seems correct). So now you've > unified them and they share the same Status :) :) I would be happy to adopt a simpler way if we know of one. In my mind, the allocated status is the correct thing after Take & Copy. The status could be partial, which just happens to have 0 updated rows. So it feels like that might be the simpler way. But really, that is only correct from the client's point of view. From the internal point of view, we haven't even begun to partially decode. We can't resume where we left off, for example. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:282: } On 2017/07/18 21:10:10, scroggo_chromium wrote: > nit: Add a newline after this? That way we separate the different stages in this > method: > > if Empty -> do this > <new line> (already present) > if Allocated -> do this > <new line> > decode Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:283: int rows_decoded = 0; On 2017/07/18 21:10:10, scroggo_chromium wrote: > Now that we zero pixels ahead of time, this variable is not used. Please remove. Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:296: return; On 2017/07/18 21:10:10, scroggo_chromium wrote: > These returns feel arbitrary to me. If you left this one out, we'd fall through > to break, and there's nothing left after the switch statement. So I'd remove it. This was actually intentional, but I'm happy to remove it. My thinking was future changes. When someone comes and adds more code below, I don't want them to have missed the cases which relied on there not being more code. You'll notice the kSuccess case breaks, but the error case here immediately returns and doesn't attempt to do any more work. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:302: return; On 2017/07/18 21:10:10, scroggo_chromium wrote: > I support putting either a break or a return here, but given that the method is > over, I'd push for break. Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:326: DCHECK(dependent_index); On 2017/07/18 17:52:42, scroggo_chromium wrote: > I think this DCHECK might fire in the following situation: > > - decode all frames of an animated image > - loop, and decode frame 0 > - call ClearCacheExceptFrame(0) > > ClearCacheExceptFrame checks for frame_buffer_cache_.size() <= 1, but not for > index == 0 I think you are right. I think we can drop it. Frame 0 should have a previous frame of kNotFound, and thus shouldn't even enter the loop below. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:330: for (size_t i = dependent_index - 1; i != required_previous_frame_index; On 2017/07/18 21:10:10, scroggo_chromium wrote: > Specify why we go in reverse? Done. I put in a fairly general comment rather than describing the implementation details of SkCodec and whatnot. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:39: class PLATFORM_EXPORT GIFImageDecoder final : public blink::ImageDecoder { On 2017/07/18 21:10:10, scroggo_chromium wrote: > Why did you add "blink::"? Hrmmm I'm guessing I accidentally removed the namespace blink { at some point? Done. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:62: void InitializeNewFrame(size_t index) override; On 2017/07/18 21:10:10, scroggo_chromium wrote: > nit: It's been recommended to me to leave "index" out of these declarations. We > were already inconsistent about this, though. Yeah. For example, a few lines above I leave it out for ClearCacheExceptFrame(). I'm personally not a fan of that. The parameter type is "size_t" -- maybe that means initialize a new frame of this many bytes. I need to go in and figure out that it actually means index. This is the exact problem that the variable name solves. :D There are times that leaving it out makes the code easier to read. Imagine it was: void InitializeNewFrame(ImageFrame& frame); That name "frame" is useless. But I'm really torn on the index here. I'll remove it if that's what we want. But I just want to state that I think it is useful. :)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: android_arm64_dbg_recipe on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/android_arm6...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_rel_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_chromium_compile_dbg_ng on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_...)
https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:93: absolute_position = std::max(absolute_position, 0l); On 2017/07/19 23:27:04, cblume wrote: > On 2017/07/18 17:52:41, scroggo_chromium wrote: > > On 2017/07/18 10:23:48, cblume wrote: > > > On 2017/07/17 21:28:40, vmpstr wrote: > > > > Should this just be a DCHECK absolute_position > 0? Do we have users of > this > > > > that might move to a negative position? > > > > > > It has been a while. I'll change this to a DCHECK and see if it passes the > > > tests. > > > > SkGifCodec won't call move at all (it uses seek instead). > > Do you happen to know about the other decoders? > This class should end up being used by them all. Oh, oops, SkGifCodec does use it. nvm. https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:171: !FrameStatusSufficientForSuccessors(index2)) { On 2017/07/19 23:27:04, cblume wrote: > On 2017/07/18 17:52:42, scroggo_chromium wrote: > > On 2017/07/18 10:23:49, cblume wrote: > > > On 2017/07/17 20:07:56, scroggo_chromium wrote: > > > > This call makes sense in theory, although I suspect in practice if the > > > required > > > > frame isn't there we'll be going back to the beginning of the image. > > > > > > I think you are right. > > > > > > I think the call to GetPreviousReferenceFrame() (now renamed) in the > previous > > > loop takes care of everything here. I don't think we need this second loop. > > > > Now that I look at it more closely, if nothing in the range was sufficient, > > GetPreviousReferenceFrame returns kNotFound. So the second loop wouldn't do > > anything. > > Right. I actually removed the second loop. The second loop made sense before I > created GetPreviousReferenceFrame(). That function essentially contained the > second loop. Not quite - GetPreviousReferenceFrame() goes back to the reported required frame, and no further. If no frames in that range are Complete, it returns kNotFound. The old second loop would see if the required frame was cached, and if not, it would return its required frame, potentially going back to the beginning of the image. In practice, with our knowledge of how this method is called, we know that that is not fruitful, because we would have already cleared those frames. > > > > But if we're not relying on the fact that ClearCacheExceptFrame is always > called > > after decoding the frame that is passed in as a parameter, it is theoretically > > possible that the frame that |index| depends on is not there, but the frame it > > depends on is. > > I think it is fair that if the client provides an index to a cleared frame we > effectively don't save anything. We're really preserving the second frame > secretly to speed up future decodes. But the API itself sounds like it purges > all but 1 frame, not all but 2. If the frame the client attempted to preserve is > already purged, I think it is okay that we do not preserve its reference frame. sgtm. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:241: bool oldAlpha = frame.HasAlpha(); On 2017/07/19 23:27:05, cblume wrote: > On 2017/07/18 21:10:09, scroggo_chromium wrote: > > I know we've gone back and forth on this, but maybe add a comment about why > you > > have to do this? Something like: > > > > // ZeroFillPixelData() sets HasAlpha to true, but there are other > > // safeguards that ensure that a Partial ImageFrame doesn't claim to > > // be opaque. Set it back to the intended state, which was set by > > // InitializeNewFrame > > > > That said, if we have to go through so much work to go against > > ZeroFillPixelData's desires, why not just re-set it once the ImageFrame is > > Complete? That seems like it would be simpler. > > > > // Once complete... > > FrameInfo frame_info; > > codec_->getFrameInfo(index, &frame_info); > > frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_info.fAlphaType)); > > I'm pretty sure we tried SkAlphaTypeIsOpaque() before and ran into a problem. > > I tested it out just now and it causes tests to fail. It looks like opaque > images are being marked as transparent. I can look into this more if you want > but I'm pretty sure I already did this. That doesn't make sense - SkAlphaTypeIsOpaque is how you computed this (look at line 189). That seems worth investigating. If you leave the code as is, please add a comment explaining it. https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:62: void InitializeNewFrame(size_t index) override; On 2017/07/19 23:27:05, cblume wrote: > On 2017/07/18 21:10:10, scroggo_chromium wrote: > > nit: It's been recommended to me to leave "index" out of these declarations. > We > > were already inconsistent about this, though. > > Yeah. For example, a few lines above I leave it out for ClearCacheExceptFrame(). > I'm personally not a fan of that. The parameter type is "size_t" -- maybe that > means initialize a new frame of this many bytes. I need to go in and figure out > that it actually means index. This is the exact problem that the variable name > solves. :D > > There are times that leaving it out makes the code easier to read. Imagine it > was: > void InitializeNewFrame(ImageFrame& frame); > That name "frame" is useless. > > But I'm really torn on the index here. > > I'll remove it if that's what we want. But I just want to state that I think it > is useful. :) I don't have a strong preference. https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: if (codec_->getFrameInfo(index, &frame_info)) { Now that https://skia-review.googlesource.com/c/24405/ landed, I don't expect this to ever return false. So I think we should either DCHECK the result or call SetFailed if it fails. (Sorry, should've been more clear about what to do when you check it.) https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:244: if (previous_frame_index == kNotFound) { Sometimes you refer to WTF::kNotFound, and sometimes to kNotFound. Which one is more appropriate? Let's stick with one or the other. https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:246: if (frame_buffer_cache_[previous_frame_index].GetStatus() != Won't this always be true? If the frame was Complete, wouldn't GetViableReferenceFrameIndex have returned it instead of kNotFound?
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:241: bool oldAlpha = frame.HasAlpha(); On 2017/07/20 13:59:52, scroggo_chromium wrote: > On 2017/07/19 23:27:05, cblume wrote: > > On 2017/07/18 21:10:09, scroggo_chromium wrote: > > > I know we've gone back and forth on this, but maybe add a comment about why > > you > > > have to do this? Something like: > > > > > > // ZeroFillPixelData() sets HasAlpha to true, but there are other > > > // safeguards that ensure that a Partial ImageFrame doesn't claim to > > > // be opaque. Set it back to the intended state, which was set by > > > // InitializeNewFrame > > > > > > That said, if we have to go through so much work to go against > > > ZeroFillPixelData's desires, why not just re-set it once the ImageFrame is > > > Complete? That seems like it would be simpler. > > > > > > // Once complete... > > > FrameInfo frame_info; > > > codec_->getFrameInfo(index, &frame_info); > > > frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_info.fAlphaType)); > > > > I'm pretty sure we tried SkAlphaTypeIsOpaque() before and ran into a problem. > > > > I tested it out just now and it causes tests to fail. It looks like opaque > > images are being marked as transparent. I can look into this more if you want > > but I'm pretty sure I already did this. > > That doesn't make sense - SkAlphaTypeIsOpaque is how you computed this (look at > line 189). > > That seems worth investigating. > > If you leave the code as is, please add a comment explaining it. Oh sorry, I think I misread. I thought you meant using the ImageFrame's ImageInfo to find the alpha. It is updated separately from the ImageFrame's has_alpha_ which is set during ZeroFillPixelData(). By the time we reach the Complete state (codec_->incrementalDecode() returning kSuccess), something else has updated the alpha in the ImageFrame's ImageInfo. So at that point, we can't use it. But what you are suggesting is getting it from the codec again. This works. :) https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/2600001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:62: void InitializeNewFrame(size_t index) override; On 2017/07/20 13:59:52, scroggo_chromium wrote: > On 2017/07/19 23:27:05, cblume wrote: > > On 2017/07/18 21:10:10, scroggo_chromium wrote: > > > nit: It's been recommended to me to leave "index" out of these declarations. > > We > > > were already inconsistent about this, though. > > > > Yeah. For example, a few lines above I leave it out for > ClearCacheExceptFrame(). > > I'm personally not a fan of that. The parameter type is "size_t" -- maybe that > > means initialize a new frame of this many bytes. I need to go in and figure > out > > that it actually means index. This is the exact problem that the variable name > > solves. :D > > > > There are times that leaving it out makes the code easier to read. Imagine it > > was: > > void InitializeNewFrame(ImageFrame& frame); > > That name "frame" is useless. > > > > But I'm really torn on the index here. > > > > I'll remove it if that's what we want. But I just want to state that I think > it > > is useful. :) > > I don't have a strong preference. I'll leave variable name out for now. I thought about this and it is kind of a mixed bag. I originally thought I might put it back where it isn't clear. Sometimes it is clear, for example: FrameIsReceivedAtIndex(size_t) FrameDurationAtIndex(size_t) Sometimes it isn't clear but can be figured out: ClearCacheExceptFrame(size_t) (it takes a size_t, not a frame) CanReusePreviousFrameBuffer(size_t) GetViableReferenceFrameIndex(size_t) Other times, size_t doesn't always mean index. For example: InitializeNewFrame(size_t) (like we said before) I'm prepared to explain if someone questions it, but only adding it to one location feels weird. Adding it to the not-clear-but-can-be-figured-out locations could be argued as unimportant. I'll just leave it out. For future reference, the most amazing solution would be taking typedef size_t FrameIndex. https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:182: if (codec_->getFrameInfo(index, &frame_info)) { On 2017/07/20 13:59:52, scroggo_chromium wrote: > Now that https://skia-review.googlesource.com/c/24405/ landed, I don't expect > this to ever return false. So I think we should either DCHECK the result or call > SetFailed if it fails. (Sorry, should've been more clear about what to do when > you check it.) I'll DCHECK. https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:244: if (previous_frame_index == kNotFound) { On 2017/07/20 13:59:52, scroggo_chromium wrote: > Sometimes you refer to WTF::kNotFound, and sometimes to kNotFound. Which one is > more appropriate? Let's stick with one or the other. WTF::kNotFound is pulled into the global namespace in NotFound.h. I'll go with the non-namespace-specified one. https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:246: if (frame_buffer_cache_[previous_frame_index].GetStatus() != On 2017/07/20 13:59:52, scroggo_chromium wrote: > Won't this always be true? If the frame was Complete, wouldn't > GetViableReferenceFrameIndex have returned it instead of kNotFound? A frame could be Complete but DisposePrevious. GetViableReferenceFrameIndex() would not return it in that case. Although, it doesn't make sense to decode a DisposePrevious that we aren't about to display. If anything, this should be != DisposePrevious. Does SkCodec make sure it will not set the previous frame to a DisposePrevious frame?
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2780001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:246: if (frame_buffer_cache_[previous_frame_index].GetStatus() != On 2017/07/20 21:53:45, cblume wrote: > On 2017/07/20 13:59:52, scroggo_chromium wrote: > > Won't this always be true? If the frame was Complete, wouldn't > > GetViableReferenceFrameIndex have returned it instead of kNotFound? > > A frame could be Complete but DisposePrevious. GetViableReferenceFrameIndex() > would not return it in that case. > > Although, it doesn't make sense to decode a DisposePrevious that we aren't about > to display. If anything, this should be != DisposePrevious. > > Does SkCodec make sure it will not set the previous frame to a DisposePrevious > frame? Yes. https://codereview.chromium.org/2565323003/diff/2860001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2860001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:285: frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_info.fAlphaType)); With this here, there's no need to reset alpha after ZeroFillPixelData. For that matter, you can drop the the other call to SetAlpha, when you initialize the frame.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/2860001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2860001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:285: frame.SetHasAlpha(!SkAlphaTypeIsOpaque(frame_info.fAlphaType)); On 2017/07/21 13:55:27, scroggo_chromium wrote: > With this here, there's no need to reset alpha after ZeroFillPixelData. > > For that matter, you can drop the the other call to SetAlpha, when you > initialize the frame. Done.
https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:46: delete segment_stream_; I don't think we'll ever have a segment_stream_ here. The only time we create one is in OnSetData. Then we'll pass it to the codec_ creation, passing ownership. If we don't have a codec_ we won't have a segment_stream_. https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:101: int repetition_count = codec_->getRepetitionCount(); Do you need to make sure !segement_stream_->IsCleared() before you can call this? https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:123: if (static_cast<size_t>(codec_->getFrameCount()) <= index) Again, is this safe to call if IsCleared()? https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:332: previous_reference_frame_index = i; Why not return i; here, and then return kNotFound; at the end? Then you can drop this variable.
https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:46: delete segment_stream_; On 2017/07/21 17:49:01, scroggo_chromium wrote: > I don't think we'll ever have a segment_stream_ here. The only time we create > one is in OnSetData. Then we'll pass it to the codec_ creation, passing > ownership. > > If we don't have a codec_ we won't have a segment_stream_. Done. Side question: Skia is never used as a dynamic library, right? It is statically built into all the builds we need. Having the client allocate and Skia deallocate is a problem when the client & skia are build by different compilers / versions of compiler. https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:101: int repetition_count = codec_->getRepetitionCount(); On 2017/07/21 17:49:00, scroggo_chromium wrote: > Do you need to make sure !segement_stream_->IsCleared() before you can call > this? Done. https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:123: if (static_cast<size_t>(codec_->getFrameCount()) <= index) On 2017/07/21 17:49:00, scroggo_chromium wrote: > Again, is this safe to call if IsCleared()? Any SkCodec call that reads & advances would not be safe to call if IsCleared(). It would help if we had const getter functions (if we've already encountered the repetition count or image size, for example) and a separate "read until we find image size or hit the end of the buffer". Then this can be prevented by design. :)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:46: delete segment_stream_; On 2017/07/21 18:29:55, cblume wrote: > On 2017/07/21 17:49:01, scroggo_chromium wrote: > > I don't think we'll ever have a segment_stream_ here. The only time we create > > one is in OnSetData. Then we'll pass it to the codec_ creation, passing > > ownership. > > > > If we don't have a codec_ we won't have a segment_stream_. > > Done. > > Side question: Skia is never used as a dynamic library, right? It is statically > built into all the builds we need. > > Having the client allocate and Skia deallocate is a problem when the client & > skia are build by different compilers / versions of compiler. I'm not aware of any builds that use Skia as a dynamic library. But that is a good point to consider. https://codereview.chromium.org/2565323003/diff/2880001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:123: if (static_cast<size_t>(codec_->getFrameCount()) <= index) On 2017/07/21 18:29:55, cblume wrote: > On 2017/07/21 17:49:00, scroggo_chromium wrote: > > Again, is this safe to call if IsCleared()? > > Any SkCodec call that reads & advances would not be safe to call if IsCleared(). > > It would help if we had const getter functions (if we've already encountered the > repetition count or image size, for example) and a separate "read until we find > image size or hit the end of the buffer". Then this can be prevented by design. > :) The IsCleared case is kind of a strange one. As I understand it, it means that we created two DecodingImageGenerators with different amounts of data, and decode them out of order. (I'm not even sure it can happen anymore, now that we no longer decode lazily.) https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:81: if (!codec_ || segment_stream_->IsCleared()) The fact that segment_stream_ has been cleared doesn't necessarily mean we want to loop once, does it? Then shouldn't we be using the last value we sent? https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: if (!codec_ || segment_stream_->IsCleared()) I suspect if IsCleared we want to return some sort of cached value. If we go ahead and only report complete frames, we can return index < frame_buffer_cache_.size(); But we still need to treat the first frame differently. https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:202: if (!codec_ || segment_stream_->IsCleared()) You already check for this on line 209. I think it's fine to combine them here if you remove the later one.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_rel_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_rel_...)
https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:81: if (!codec_ || segment_stream_->IsCleared()) On 2017/07/21 19:01:41, scroggo_chromium wrote: > The fact that segment_stream_ has been cleared doesn't necessarily mean we want > to loop once, does it? Then shouldn't we be using the last value we sent? kAnimationLoopOnce is the value we return when we haven't found the actual repetition count. It is the default. But you're right, it would be strange to go back to the default. That would also be a point where we aren't actually hiding the fact that the stream was cleared. We could keep a cached copy that defaults to kAnimationLoopOnce. On each call to RepetitionCount() it can update the cached copy if it can and return that. I'll do that. Just a head's up: Because this method is const, I'll have to make the cached copy mutable. This isn't pretty and it actually violates a silent change in C++11 where const & mutable mean thread safe. The better solution will be to change the ImageDecoder API to be correct about const. (It was already arguably wrong since it made changes in the underlying readers.) An alternative would be to DCHECK() instead. But that gets back into the spaghetti design we want to move away from. So I'm happy with the mutable for now. https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:202: if (!codec_ || segment_stream_->IsCleared()) On 2017/07/21 19:01:41, scroggo_chromium wrote: > You already check for this on line 209. I think it's fine to combine them here > if you remove the later one. Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: if (!codec_ || segment_stream_->IsCleared()) On 2017/07/21 19:01:41, scroggo_chromium wrote: > I suspect if IsCleared we want to return some sort of cached value. > > If we go ahead and only report complete frames, we can > > return index < frame_buffer_cache_.size(); > > But we still need to treat the first frame differently. Done. I am keeping a cached copy and checking if the frame is fully received via SkCodec::FrameInfo::fFullyReceived. Interestingly, using fFullyReceived required a slight tweak to the test. It looks like once SkCodec reports that there is a second frame, it also reports the frame is fully received. Did we already land the only-report-fully-received-frames thing? I'm guessing so.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
Sorry for the slow replies... I am out this week https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: if (!codec_ || segment_stream_->IsCleared()) On 2017/07/24 05:54:47, cblume wrote: > On 2017/07/21 19:01:41, scroggo_chromium wrote: > > I suspect if IsCleared we want to return some sort of cached value. > > > > If we go ahead and only report complete frames, we can > > > > return index < frame_buffer_cache_.size(); > > > > But we still need to treat the first frame differently. > > Done. > I am keeping a cached copy and checking if the frame is fully received via > SkCodec::FrameInfo::fFullyReceived. > > Interestingly, using fFullyReceived required a slight tweak to the test. It > looks like once SkCodec reports that there is a second frame, it also reports > the frame is fully received. Did we already land the > only-report-fully-received-frames thing? I'm guessing so. Once it reports the second frame, the first frame will be reported as fully received. We had discussed a while ago making SkCodec report only complete frames, but ended up adding fFullyReceived instead. https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:83: if (codec_ || !segment_stream_->IsCleared()) { I think you want &&
https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:112: if (!codec_ || segment_stream_->IsCleared()) On 2017/07/25 19:02:00, scroggo_chromium wrote: > On 2017/07/24 05:54:47, cblume wrote: > > On 2017/07/21 19:01:41, scroggo_chromium wrote: > > > I suspect if IsCleared we want to return some sort of cached value. > > > > > > If we go ahead and only report complete frames, we can > > > > > > return index < frame_buffer_cache_.size(); > > > > > > But we still need to treat the first frame differently. > > > > Done. > > I am keeping a cached copy and checking if the frame is fully received via > > SkCodec::FrameInfo::fFullyReceived. > > > > Interestingly, using fFullyReceived required a slight tweak to the test. It > > looks like once SkCodec reports that there is a second frame, it also reports > > the frame is fully received. Did we already land the > > only-report-fully-received-frames thing? I'm guessing so. > > Once it reports the second frame, the first frame will be reported as fully > received. > > We had discussed a while ago making SkCodec report only complete frames, but > ended up adding fFullyReceived instead. Right right. That's not what I mean though. Once the second frame was reported at all, the *second* frame was marked as complete. (Along with the first) I'll make another comment on the test which I changed to explain what I mean. https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:83: if (codec_ || !segment_stream_->IsCleared()) { On 2017/07/25 19:02:00, scroggo_chromium wrote: > I think you want && Done. https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp (right): https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoderTest.cpp:208: EXPECT_FALSE(decoder->FrameIsReceivedAtIndex(1)); You see how I updated to - 34 a few lines above? And changed the frame count to 1? If you instead do - 33 then the second frame shows up in the frame count. BUT the second frame is also marked with fFullyReceived = true. So line 208 here fails the assertion as soon as the second frame is reported. There is no instance where the second frame in this test is reported without fFullyReceived being true.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: chromium_presubmit on master.tryserver.chromium.linux (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.linux/builders/chromium_presub...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: mac_chromium_compile_dbg_ng on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/mac_chromium_comp...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: android_n5x_swarming_rel on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/android_n5x_...)
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
(ping) Is there any more progress on this?
Leon was on vacation for the last two weeks. I believe he is back now.
On 2017/08/07 23:00:35, vmpstr wrote: > (ping) > > Is there any more progress on this? Just getting back from vacation, and will review ASAP
https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:87: DCHECK_GT(offset, 0); In [1], vmpstr@ suggested that you "DCHECK absolute_position > 0", where absolute_position = position_ + offset. This is a slightly different DCHECK. In practice, this statement is also always true, and I don't foresee it changing. If it does, I suppose this DCHECK_GT will catch us. [1] https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.h (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.h:19: virtual ~SegmentStream() {} Should this instead be: ~SegmentStream() override since it is overriding SkStream's destructor? Also, Chromium generally recommends not inlining destructors [1]. If you move this definition into the implementation file (along with that of getLength), can you change SegmentStream from an include to a forward declare? [1] https://www.chromium.org/developers/coding-style/cpp-dos-and-donts#TOC-Stop-i... https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:84: DCHECK(!Failed()); In the old code, Failed() could be true here, or perhaps it couldn't, but we still tried to handle it? Why did this change? https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:134: codec_->getFrameInfo(index, &frame_info); Here's the bug referenced in [1]. You probably meant to pass |i| here, rather than index. As written, if frame 0 is complete and frame 1 is partially available, and you call this method with 0, we'll go through this loop twice. Both times we'll set max_received_frame_index_ to i, and the second time it will set it (incorrectly) to 1. I don't think this method is what you want anyway. For one thing, you only need to check the last two frames (there won't be a frame that is incomplete followed by one that is incomplete). getFrameCount is also non-const and will parse more data if possible. What if you update max_received_frame_index_ in DecodeFrameCount? It would need to call getFrameInfo on the final frame, and know that the frame prior to the latest one is complete if the latest one is not. [1] https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:31: #include "platform/image-decoders/SegmentStream.h" Can this be a forward declare? https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:44: ~GIFImageDecoder() override {} Again, we shouldn't inline destructors. I think if you don't inline this, you can make SkCodec a forward declare instead of an include.
https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:87: DCHECK_GT(offset, 0); On 2017/08/08 19:39:25, scroggo_chromium wrote: > In [1], vmpstr@ suggested that you "DCHECK absolute_position > 0", where > absolute_position = position_ + offset. This is a slightly different DCHECK. In > practice, this statement is also always true, and I don't foresee it changing. > If it does, I suppose this DCHECK_GT will catch us. > > [1] > https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... position_ is a size_t and can never be negative. The only way absolute_position could have been <= 0 is if offset was <= 0. So although this line is different, it should be effectively the same. It asserts on the same condition. Note, position_ and offset could both be 0. This is valid although odd. My old code handled this. DCHECK absolute_position > 0 does not. The reason I went with this instead of DCHECK absolute_position > 0 is because of another comment in [1] you linked on line 59: https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... That comment requests getting rid of a named variable which is only there to give a name to the equation. It is very similar to absolute_position was here. I was trying to adopt the spirit of the comment. https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.h (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.h:19: virtual ~SegmentStream() {} On 2017/08/08 19:39:25, scroggo_chromium wrote: > Should this instead be: > ~SegmentStream() override > since it is overriding SkStream's destructor? Yes. On 2017/08/08 19:39:25, scroggo_chromium wrote: > Also, Chromium generally recommends not inlining destructors [1]. If you move > this definition into the implementation file (along with that of getLength), can > you change SegmentStream from an include to a forward declare? > > [1] > https://www.chromium.org/developers/coding-style/cpp-dos-and-donts#TOC-Stop-i... I strongly agree with this. *Especially* because it is virtual. Granted, now-a-days compilers do de-virtualization when they can. But still. I was following SkStream's style, from which this class derives: https://cs.chromium.org/chromium/src/third_party/skia/include/core/SkStream.h... But maybe Skia follows a different style guide or this is older code that wasn't updated since random style updates are not the best use of time. Anyway, fixed. :) https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:84: DCHECK(!Failed()); On 2017/08/08 19:39:25, scroggo_chromium wrote: > In the old code, Failed() could be true here, or perhaps it couldn't, but we > still tried to handle it? Why did this change? I think you are right, that this is incorrect. This came from a series of separate changes. I walked through the history to figure out how that happened. It was a mistake I made recently while trying to make the code more simple. The first relevant change was a code review comment you requested in Patch Set 81, Line 83 https://codereview.chromium.org/2565323003/diff2/1580001:1600001/third_party/... The code at the time was: if (!codec_ || Failed()) return kCAnimationLoopOnce; The thinking was SetFailed() would delete codec_ and thus the second part of this if was useless. That became if (!codec_) with a DCHECK(!Failed()) as suggested. This was fine because it would return early if the codec_ was deleted and not hit the DCHECK. Later, in Patch Set 146, Line 81 I added a check to see if the segment stream had been cleared to make sure we don't attempt to read from a cleared buffer. https://codereview.chromium.org/2565323003/diff2/2880001:2900001/third_party/... We had talked about caching a value instead of returning the default. In the very next Patch Set is when I introduced this problem. https://codereview.chromium.org/2565323003/diff2/2900001:2920001/third_party/... The thinking (ignoring the DCHECK) was that we update the cached value if we can. Then we return the cached value (updated or not). I think if we want to leave the DCHECK in, we want to return early if we cannot update the cached value. I'll do that now. https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:134: codec_->getFrameInfo(index, &frame_info); On 2017/08/08 19:39:25, scroggo_chromium wrote: > Here's the bug referenced in [1]. You probably meant to pass |i| here, rather > than index. As written, if frame 0 is complete and frame 1 is partially > available, and you call this method with 0, we'll go through this loop twice. > Both times we'll set max_received_frame_index_ to i, and the second time it will > set it (incorrectly) to 1. > > I don't think this method is what you want anyway. For one thing, you only need > to check the last two frames (there won't be a frame that is incomplete followed > by one that is incomplete). getFrameCount is also non-const and will parse more > data if possible. > > What if you update max_received_frame_index_ in DecodeFrameCount? It would need > to call getFrameInfo on the final frame, and know that the frame prior to the > latest one is complete if the latest one is not. > > [1] > https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... Thank you for finding this. :) Changing index back to i allowed me to put the unit test back to where it was. You are right -- this wasn't reporting only complete frames like I had thought. I understand what you are saying that the gif format provides one frame after another and so the only way we know frame i+1 exists is if frame i is completely downloaded. Ideally, I want to be able to reuse and share this code with other formats. And those other formats could specify in advance their frame count. So I was hoping not to rely on details of the gif file format if at all possible. I'll do what you suggest in DecodeFrameCount. It's definitely small and simple. https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:31: #include "platform/image-decoders/SegmentStream.h" On 2017/08/08 19:39:25, scroggo_chromium wrote: > Can this be a forward declare? Done. https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:44: ~GIFImageDecoder() override {} On 2017/08/08 19:39:25, scroggo_chromium wrote: > Again, we shouldn't inline destructors. I think if you don't inline this, you > can make SkCodec a forward declare instead of an include. Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:87: DCHECK_GT(offset, 0); On 2017/08/09 17:29:29, cblume wrote: > On 2017/08/08 19:39:25, scroggo_chromium wrote: > > In [1], vmpstr@ suggested that you "DCHECK absolute_position > 0", where > > absolute_position = position_ + offset. This is a slightly different DCHECK. > In > > practice, this statement is also always true, and I don't foresee it changing. > > If it does, I suppose this DCHECK_GT will catch us. > > > > [1] > > > https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... > > position_ is a size_t and can never be negative. > The only way absolute_position could have been <= 0 is if offset was <= 0. > So although this line is different, it should be effectively the same. It > asserts on the same condition. > > Note, position_ and offset could both be 0. This is valid although odd. My old > code handled this. DCHECK absolute_position > 0 does not. > > The reason I went with this instead of DCHECK absolute_position > 0 is because > of another comment in [1] you linked on line 59: > https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... > That comment requests getting rid of a named variable which is only there to > give a name to the equation. > It is very similar to absolute_position was here. I was trying to adopt the > spirit of the comment. sgtm https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.h (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.h:19: virtual ~SegmentStream() {} On 2017/08/09 17:29:29, cblume wrote: > On 2017/08/08 19:39:25, scroggo_chromium wrote: > > Should this instead be: > > ~SegmentStream() override > > since it is overriding SkStream's destructor? > > Yes. > > On 2017/08/08 19:39:25, scroggo_chromium wrote: > > Also, Chromium generally recommends not inlining destructors [1]. If you move > > this definition into the implementation file (along with that of getLength), > can > > you change SegmentStream from an include to a forward declare? > > > > [1] > > > https://www.chromium.org/developers/coding-style/cpp-dos-and-donts#TOC-Stop-i... > > I strongly agree with this. *Especially* because it is virtual. > Granted, now-a-days compilers do de-virtualization when they can. But still. > > I was following SkStream's style, from which this class derives: > https://cs.chromium.org/chromium/src/third_party/skia/include/core/SkStream.h... > But maybe Skia follows a different style guide or this is older code that wasn't > updated since random style updates are not the best use of time. Yeah, that code is pretty old. Skia's official style guide is at https://skia.org/dev/contrib/style, but it is far from exhaustive. > > Anyway, fixed. :) https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:134: codec_->getFrameInfo(index, &frame_info); On 2017/08/09 17:29:29, cblume wrote: > On 2017/08/08 19:39:25, scroggo_chromium wrote: > > Here's the bug referenced in [1]. You probably meant to pass |i| here, rather > > than index. As written, if frame 0 is complete and frame 1 is partially > > available, and you call this method with 0, we'll go through this loop twice. > > Both times we'll set max_received_frame_index_ to i, and the second time it > will > > set it (incorrectly) to 1. > > > > I don't think this method is what you want anyway. For one thing, you only > need > > to check the last two frames (there won't be a frame that is incomplete > followed > > by one that is incomplete). getFrameCount is also non-const and will parse > more > > data if possible. > > > > What if you update max_received_frame_index_ in DecodeFrameCount? It would > need > > to call getFrameInfo on the final frame, and know that the frame prior to the > > latest one is complete if the latest one is not. > > > > [1] > > > https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... > > Thank you for finding this. :) > Changing index back to i allowed me to put the unit test back to where it was. > You are right -- this wasn't reporting only complete frames like I had thought. > > I understand what you are saying that the gif format provides one frame after > another and so the only way we know frame i+1 exists is if frame i is completely > downloaded. Ideally, I want to be able to reuse and share this code with other > formats. And those other formats could specify in advance their frame count. So > I was hoping not to rely on details of the gif file format if at all possible. You're imagining a format where the frames might be interspersed (presumably to aid compression)? The three formats we support for animation don't support that, but I suppose that could be possible. In that case, how about the following implementation: { if (!codec_) { return false; } FrameInfo frame_info; return codec->getFrameInfo(index, &frame_info) && frame_info.fFullyReceived; } getFrameInfo will not read the stream any, and will just return its cached information. (And it will return false if index is out of range.) This is even simpler than caching the max received index. > > I'll do what you suggest in DecodeFrameCount. It's definitely small and simple.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: ios-simulator on master.tryserver.chromium.mac (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.mac/builders/ios-simulator/bui...)
https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:134: codec_->getFrameInfo(index, &frame_info); On 2017/08/09 17:50:48, scroggo_chromium wrote: > On 2017/08/09 17:29:29, cblume wrote: > > On 2017/08/08 19:39:25, scroggo_chromium wrote: > > > Here's the bug referenced in [1]. You probably meant to pass |i| here, > rather > > > than index. As written, if frame 0 is complete and frame 1 is partially > > > available, and you call this method with 0, we'll go through this loop > twice. > > > Both times we'll set max_received_frame_index_ to i, and the second time it > > will > > > set it (incorrectly) to 1. > > > > > > I don't think this method is what you want anyway. For one thing, you only > > need > > > to check the last two frames (there won't be a frame that is incomplete > > followed > > > by one that is incomplete). getFrameCount is also non-const and will parse > > more > > > data if possible. > > > > > > What if you update max_received_frame_index_ in DecodeFrameCount? It would > > need > > > to call getFrameInfo on the final frame, and know that the frame prior to > the > > > latest one is complete if the latest one is not. > > > > > > [1] > > > > > > https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... > > > > Thank you for finding this. :) > > Changing index back to i allowed me to put the unit test back to where it was. > > You are right -- this wasn't reporting only complete frames like I had > thought. > > > > I understand what you are saying that the gif format provides one frame after > > another and so the only way we know frame i+1 exists is if frame i is > completely > > downloaded. Ideally, I want to be able to reuse and share this code with other > > formats. And those other formats could specify in advance their frame count. > So > > I was hoping not to rely on details of the gif file format if at all possible. > > You're imagining a format where the frames might be interspersed (presumably to > aid compression)? The three formats we support for animation don't support that, > but I suppose that could be possible. > > In that case, how about the following implementation: > { > if (!codec_) { > return false; > } > > FrameInfo frame_info; > return codec->getFrameInfo(index, &frame_info) && frame_info.fFullyReceived; > } > getFrameInfo will not read the stream any, and will just return its cached > information. (And it will return false if index is out of range.) > > This is even simpler than caching the max received index. > > > > > I'll do what you suggest in DecodeFrameCount. It's definitely small and > simple. > I was thinking a format even more simple than that. file_header: --frame_count: 10 begin_frame... Although, maybe my thought process there is misguided. GIF doesn't work that way. APNG doesn't work that way. I've only looked at WebP a little bit but it seems like it also doesn't work that way. The only one which might is ICO? But it would be silly to write code around ICO's behavior rather than the common GIF/APNG/WebP behavior. I am happy with what we currently having, including the caching.
https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:134: codec_->getFrameInfo(index, &frame_info); On 2017/08/09 22:31:54, cblume wrote: > On 2017/08/09 17:50:48, scroggo_chromium wrote: > > On 2017/08/09 17:29:29, cblume wrote: > > > On 2017/08/08 19:39:25, scroggo_chromium wrote: > > > > Here's the bug referenced in [1]. You probably meant to pass |i| here, > > rather > > > > than index. As written, if frame 0 is complete and frame 1 is partially > > > > available, and you call this method with 0, we'll go through this loop > > twice. > > > > Both times we'll set max_received_frame_index_ to i, and the second time > it > > > will > > > > set it (incorrectly) to 1. > > > > > > > > I don't think this method is what you want anyway. For one thing, you only > > > need > > > > to check the last two frames (there won't be a frame that is incomplete > > > followed > > > > by one that is incomplete). getFrameCount is also non-const and will parse > > > more > > > > data if possible. > > > > > > > > What if you update max_received_frame_index_ in DecodeFrameCount? It would > > > need > > > > to call getFrameInfo on the final frame, and know that the frame prior to > > the > > > > latest one is complete if the latest one is not. > > > > > > > > [1] > > > > > > > > > > https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... > > > > > > Thank you for finding this. :) > > > Changing index back to i allowed me to put the unit test back to where it > was. > > > You are right -- this wasn't reporting only complete frames like I had > > thought. > > > > > > I understand what you are saying that the gif format provides one frame > after > > > another and so the only way we know frame i+1 exists is if frame i is > > completely > > > downloaded. Ideally, I want to be able to reuse and share this code with > other > > > formats. And those other formats could specify in advance their frame count. > > So > > > I was hoping not to rely on details of the gif file format if at all > possible. > > > > You're imagining a format where the frames might be interspersed (presumably > to > > aid compression)? The three formats we support for animation don't support > that, > > but I suppose that could be possible. > > > > In that case, how about the following implementation: > > { > > if (!codec_) { > > return false; > > } > > > > FrameInfo frame_info; > > return codec->getFrameInfo(index, &frame_info) && frame_info.fFullyReceived; > > } > > getFrameInfo will not read the stream any, and will just return its cached > > information. (And it will return false if index is out of range.) > > > > This is even simpler than caching the max received index. > > > > > > > > I'll do what you suggest in DecodeFrameCount. It's definitely small and > > simple. > > > > I was thinking a format even more simple than that. > file_header: > --frame_count: 10 > begin_frame... > > Although, maybe my thought process there is misguided. GIF doesn't work that > way. APNG doesn't work that way. I've only looked at WebP a little bit but it > seems like it also doesn't work that way. I don't follow what you mean here. APNG tells you the frame count before any frames appear. They still appear in the order you'd expect though. (You'll never see a complete frame 1 with an incomplete frame 0.) > The only one which might is ICO? But > it would be silly to write code around ICO's behavior rather than the common > GIF/APNG/WebP behavior. > > I am happy with what we currently having, including the caching. ICO is a more simple way of having complete frames out of order - the frames themselves are not necessarily in order. ICO is a weird case, but it does seem like the non-caching version covers it *and* is simpler.
https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:134: codec_->getFrameInfo(index, &frame_info); On 2017/08/10 19:04:41, scroggo_chromium wrote: > On 2017/08/09 22:31:54, cblume wrote: > > On 2017/08/09 17:50:48, scroggo_chromium wrote: > > > On 2017/08/09 17:29:29, cblume wrote: > > > > On 2017/08/08 19:39:25, scroggo_chromium wrote: > > > > > Here's the bug referenced in [1]. You probably meant to pass |i| here, > > > rather > > > > > than index. As written, if frame 0 is complete and frame 1 is partially > > > > > available, and you call this method with 0, we'll go through this loop > > > twice. > > > > > Both times we'll set max_received_frame_index_ to i, and the second time > > it > > > > will > > > > > set it (incorrectly) to 1. > > > > > > > > > > I don't think this method is what you want anyway. For one thing, you > only > > > > need > > > > > to check the last two frames (there won't be a frame that is incomplete > > > > followed > > > > > by one that is incomplete). getFrameCount is also non-const and will > parse > > > > more > > > > > data if possible. > > > > > > > > > > What if you update max_received_frame_index_ in DecodeFrameCount? It > would > > > > need > > > > > to call getFrameInfo on the final frame, and know that the frame prior > to > > > the > > > > > latest one is complete if the latest one is not. > > > > > > > > > > [1] > > > > > > > > > > > > > > > https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... > > > > > > > > Thank you for finding this. :) > > > > Changing index back to i allowed me to put the unit test back to where it > > was. > > > > You are right -- this wasn't reporting only complete frames like I had > > > thought. > > > > > > > > I understand what you are saying that the gif format provides one frame > > after > > > > another and so the only way we know frame i+1 exists is if frame i is > > > completely > > > > downloaded. Ideally, I want to be able to reuse and share this code with > > other > > > > formats. And those other formats could specify in advance their frame > count. > > > So > > > > I was hoping not to rely on details of the gif file format if at all > > possible. > > > > > > You're imagining a format where the frames might be interspersed (presumably > > to > > > aid compression)? The three formats we support for animation don't support > > that, > > > but I suppose that could be possible. > > > > > > In that case, how about the following implementation: > > > { > > > if (!codec_) { > > > return false; > > > } > > > > > > FrameInfo frame_info; > > > return codec->getFrameInfo(index, &frame_info) && > frame_info.fFullyReceived; > > > } > > > getFrameInfo will not read the stream any, and will just return its cached > > > information. (And it will return false if index is out of range.) > > > > > > This is even simpler than caching the max received index. > > > > > > > > > > > I'll do what you suggest in DecodeFrameCount. It's definitely small and > > > simple. > > > > > > > I was thinking a format even more simple than that. > > file_header: > > --frame_count: 10 > > begin_frame... > > > > Although, maybe my thought process there is misguided. GIF doesn't work that > > way. APNG doesn't work that way. I've only looked at WebP a little bit but it > > seems like it also doesn't work that way. > > I don't follow what you mean here. APNG tells you the frame count before any > frames appear. They still appear in the order you'd expect though. (You'll never > see a complete frame 1 with an incomplete frame 0.) Ah okay. If APNG reports frame count early than that is exactly the scenario I was coding for. I'm not worried about the order of the frames as they arrive. I'm worried about when we report the frame count. Right now SkPNGCodec does not handle APNG but suppose it did. If we get the frame count from the header, I cannot assume frame 9 is complete just because the frame count is 10 -- I cannot only care about the last two frames. If we only received the header then no frames are complete yet. (Alternatively, we could not report what the header says and only report frames as we parse them. This has its own pros and cons.) Since these calls can happen in any order, I also cannot assume that the last known complete frame (say 2) will be followed by a request about frame 3. The best I could do is loop from frame 2 to the requested frame if I am going to maintain a cache. This loop which these comments are on starts at 0 instead of the last known complete frame. This is because the last known complete frame could be kNotFound. The added complexity of checking for this didn't seem worth it. But anyway, we can cross that bridge when we get to SkPNGCodec. For now, this code works for me for this patch. > > > The only one which might is ICO? But > > it would be silly to write code around ICO's behavior rather than the common > > GIF/APNG/WebP behavior. > > > > I am happy with what we currently having, including the caching. > > ICO is a more simple way of having complete frames out of order - the frames > themselves are not necessarily in order. ICO is a weird case, but it does seem > like the non-caching version covers it *and* is simpler. I previously incorrectly thought the old Blink decoders didn't cache these values (repetition & frame count). But they do -- there is a mutable int repetition_count_ in GIFImageDecoder. So keeping the cache might be keeping existing behavior. It is probably more correct for now, since this is supposed to be a shim. Major behavioral differences should happen elsewhere. You had wisely pointed out over a month ago that I had lost this caching behavior: [1] https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... [2] https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... Given that the IsCleared() case may not even happen any more (I have encountered it in our unit tests but not in normal Chrome code), we could potentially get rid of the caching and go back to the old code I had which was more simple. We could even further simplify things if we get rid of the IsCleared() case. But I feel like we should add that as a TODO/new bug and get to it in the refactor phase. I would like to land this patch. Getting rid of the caching fits well with getting rid of the IsCleared() state. Those probably go hand-in-hand. And it is probably best to keep that major behavior change out of this patch.
https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:134: codec_->getFrameInfo(index, &frame_info); On 2017/08/10 20:32:17, cblume wrote: > On 2017/08/10 19:04:41, scroggo_chromium wrote: > > On 2017/08/09 22:31:54, cblume wrote: > > > On 2017/08/09 17:50:48, scroggo_chromium wrote: > > > > On 2017/08/09 17:29:29, cblume wrote: > > > > > On 2017/08/08 19:39:25, scroggo_chromium wrote: > > > > > > Here's the bug referenced in [1]. You probably meant to pass |i| here, > > > > rather > > > > > > than index. As written, if frame 0 is complete and frame 1 is > partially > > > > > > available, and you call this method with 0, we'll go through this loop > > > > twice. > > > > > > Both times we'll set max_received_frame_index_ to i, and the second > time > > > it > > > > > will > > > > > > set it (incorrectly) to 1. > > > > > > > > > > > > I don't think this method is what you want anyway. For one thing, you > > only > > > > > need > > > > > > to check the last two frames (there won't be a frame that is > incomplete > > > > > followed > > > > > > by one that is incomplete). getFrameCount is also non-const and will > > parse > > > > > more > > > > > > data if possible. > > > > > > > > > > > > What if you update max_received_frame_index_ in DecodeFrameCount? It > > would > > > > > need > > > > > > to call getFrameInfo on the final frame, and know that the frame prior > > to > > > > the > > > > > > latest one is complete if the latest one is not. > > > > > > > > > > > > [1] > > > > > > > > > > > > > > > > > > > > > https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... > > > > > > > > > > Thank you for finding this. :) > > > > > Changing index back to i allowed me to put the unit test back to where > it > > > was. > > > > > You are right -- this wasn't reporting only complete frames like I had > > > > thought. > > > > > > > > > > I understand what you are saying that the gif format provides one frame > > > after > > > > > another and so the only way we know frame i+1 exists is if frame i is > > > > completely > > > > > downloaded. Ideally, I want to be able to reuse and share this code with > > > other > > > > > formats. And those other formats could specify in advance their frame > > count. > > > > So > > > > > I was hoping not to rely on details of the gif file format if at all > > > possible. > > > > > > > > You're imagining a format where the frames might be interspersed > (presumably > > > to > > > > aid compression)? The three formats we support for animation don't support > > > that, > > > > but I suppose that could be possible. > > > > > > > > In that case, how about the following implementation: > > > > { > > > > if (!codec_) { > > > > return false; > > > > } > > > > > > > > FrameInfo frame_info; > > > > return codec->getFrameInfo(index, &frame_info) && > > frame_info.fFullyReceived; > > > > } > > > > getFrameInfo will not read the stream any, and will just return its cached > > > > information. (And it will return false if index is out of range.) > > > > > > > > This is even simpler than caching the max received index. > > > > > > > > > > > > > > I'll do what you suggest in DecodeFrameCount. It's definitely small and > > > > simple. > > > > > > > > > > I was thinking a format even more simple than that. > > > file_header: > > > --frame_count: 10 > > > begin_frame... > > > > > > Although, maybe my thought process there is misguided. GIF doesn't work that > > > way. APNG doesn't work that way. I've only looked at WebP a little bit but > it > > > seems like it also doesn't work that way. > > > > I don't follow what you mean here. APNG tells you the frame count before any > > frames appear. They still appear in the order you'd expect though. (You'll > never > > see a complete frame 1 with an incomplete frame 0.) > > Ah okay. If APNG reports frame count early than that is exactly the scenario I > was coding for. I'm not worried about the order of the frames as they arrive. > I'm worried about when we report the frame count. > > Right now SkPNGCodec does not handle APNG but suppose it did. If we get the > frame count from the header, I cannot assume frame 9 is complete just because > the frame count is 10 -- I cannot only care about the last two frames. If we > only received the header then no frames are complete yet. (Alternatively, we > could not report what the header says and only report frames as we parse them. > This has its own pros and cons.) Today, Chromium's APNG logic only reports frames that have been received. (Actually, the first frame it reports immediately, but later frames are only reported once they're *fully* received, since we don't want to draw them if they're partial.) So I was assuming that behavior. (What are the cons?) > > Since these calls can happen in any order, I also cannot assume that the last > known complete frame (say 2) will be followed by a request about frame 3. The > best I could do is loop from frame 2 to the requested frame if I am going to > maintain a cache. This loop which these comments are on starts at 0 instead of > the last known complete frame. This is because the last known complete frame > could be kNotFound. The added complexity of checking for this didn't seem worth > it. > > But anyway, we can cross that bridge when we get to SkPNGCodec. For now, this > code works for me for this patch. > > > > > > The only one which might is ICO? But > > > it would be silly to write code around ICO's behavior rather than the common > > > GIF/APNG/WebP behavior. > > > > > > I am happy with what we currently having, including the caching. > > > > ICO is a more simple way of having complete frames out of order - the frames > > themselves are not necessarily in order. ICO is a weird case, but it does seem > > like the non-caching version covers it *and* is simpler. > > I previously incorrectly thought the old Blink decoders didn't cache these > values (repetition & frame count). But they do -- there is a mutable int > repetition_count_ in GIFImageDecoder. So keeping the cache might be keeping > existing behavior. > > It is probably more correct for now, since this is supposed to be a shim. Major > behavioral differences should happen elsewhere. You had wisely pointed out over > a month ago that I had lost this caching behavior: > [1] > https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... > [2] > https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... > > Given that the IsCleared() case may not even happen any more (I have encountered > it in our unit tests but not in normal Chrome code), we could potentially get > rid of the caching and go back to the old code I had which was more simple. We > could even further simplify things if we get rid of the IsCleared() case. > > But I feel like we should add that as a TODO/new bug and get to it in the > refactor phase. I would like to land this patch. Getting rid of the caching fits > well with getting rid of the IsCleared() state. Those probably go hand-in-hand. > And it is probably best to keep that major behavior change out of this patch. sgtm https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: max_received_frame_index_ = kNotFound; I don't think this is quite right. If we had *exactly* the entire first frame (or even part of the header for the second frame), this will incorrectly report that frame 0 is incomplete. Again, I think it would be simpler to just call getFrameInfo in FrameIsReceivedAtIndex.
https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:134: codec_->getFrameInfo(index, &frame_info); On 2017/08/10 23:31:26, scroggo_chromium wrote: > On 2017/08/10 20:32:17, cblume wrote: > > On 2017/08/10 19:04:41, scroggo_chromium wrote: > > > On 2017/08/09 22:31:54, cblume wrote: > > > > On 2017/08/09 17:50:48, scroggo_chromium wrote: > > > > > On 2017/08/09 17:29:29, cblume wrote: > > > > > > On 2017/08/08 19:39:25, scroggo_chromium wrote: > > > > > > > Here's the bug referenced in [1]. You probably meant to pass |i| > here, > > > > > rather > > > > > > > than index. As written, if frame 0 is complete and frame 1 is > > partially > > > > > > > available, and you call this method with 0, we'll go through this > loop > > > > > twice. > > > > > > > Both times we'll set max_received_frame_index_ to i, and the second > > time > > > > it > > > > > > will > > > > > > > set it (incorrectly) to 1. > > > > > > > > > > > > > > I don't think this method is what you want anyway. For one thing, > you > > > only > > > > > > need > > > > > > > to check the last two frames (there won't be a frame that is > > incomplete > > > > > > followed > > > > > > > by one that is incomplete). getFrameCount is also non-const and will > > > parse > > > > > > more > > > > > > > data if possible. > > > > > > > > > > > > > > What if you update max_received_frame_index_ in DecodeFrameCount? It > > > would > > > > > > need > > > > > > > to call getFrameInfo on the final frame, and know that the frame > prior > > > to > > > > > the > > > > > > > latest one is complete if the latest one is not. > > > > > > > > > > > > > > [1] > > > > > > > > > > > > > > > > > > > > > > > > > > > > https://codereview.chromium.org/2565323003/diff/2960001/third_party/WebKit/So... > > > > > > > > > > > > Thank you for finding this. :) > > > > > > Changing index back to i allowed me to put the unit test back to where > > it > > > > was. > > > > > > You are right -- this wasn't reporting only complete frames like I had > > > > > thought. > > > > > > > > > > > > I understand what you are saying that the gif format provides one > frame > > > > after > > > > > > another and so the only way we know frame i+1 exists is if frame i is > > > > > completely > > > > > > downloaded. Ideally, I want to be able to reuse and share this code > with > > > > other > > > > > > formats. And those other formats could specify in advance their frame > > > count. > > > > > So > > > > > > I was hoping not to rely on details of the gif file format if at all > > > > possible. > > > > > > > > > > You're imagining a format where the frames might be interspersed > > (presumably > > > > to > > > > > aid compression)? The three formats we support for animation don't > support > > > > that, > > > > > but I suppose that could be possible. > > > > > > > > > > In that case, how about the following implementation: > > > > > { > > > > > if (!codec_) { > > > > > return false; > > > > > } > > > > > > > > > > FrameInfo frame_info; > > > > > return codec->getFrameInfo(index, &frame_info) && > > > frame_info.fFullyReceived; > > > > > } > > > > > getFrameInfo will not read the stream any, and will just return its > cached > > > > > information. (And it will return false if index is out of range.) > > > > > > > > > > This is even simpler than caching the max received index. > > > > > > > > > > > > > > > > > I'll do what you suggest in DecodeFrameCount. It's definitely small > and > > > > > simple. > > > > > > > > > > > > > I was thinking a format even more simple than that. > > > > file_header: > > > > --frame_count: 10 > > > > begin_frame... > > > > > > > > Although, maybe my thought process there is misguided. GIF doesn't work > that > > > > way. APNG doesn't work that way. I've only looked at WebP a little bit but > > it > > > > seems like it also doesn't work that way. > > > > > > I don't follow what you mean here. APNG tells you the frame count before any > > > frames appear. They still appear in the order you'd expect though. (You'll > > never > > > see a complete frame 1 with an incomplete frame 0.) > > > > Ah okay. If APNG reports frame count early than that is exactly the scenario I > > was coding for. I'm not worried about the order of the frames as they arrive. > > I'm worried about when we report the frame count. > > > > Right now SkPNGCodec does not handle APNG but suppose it did. If we get the > > frame count from the header, I cannot assume frame 9 is complete just because > > the frame count is 10 -- I cannot only care about the last two frames. If we > > only received the header then no frames are complete yet. (Alternatively, we > > could not report what the header says and only report frames as we parse them. > > This has its own pros and cons.) > > Today, Chromium's APNG logic only reports frames that have been received. > (Actually, the first frame it reports immediately, but later frames are only > reported once they're *fully* received, since we don't want to draw them if > they're partial.) So I was assuming that behavior. (What are the cons?) During the first loop of the animation, we won't know information about the animation as a whole. So we cannot plan for it. For example, if I know in advance that this animation has 3 frames and takes 1.5 MB of memory. That might be small enough for Blink to say "You know, might as well just keep the whole thing cached rather than constantly decoding the the oldest frame again." But since I don't know that until the animation has looped once, we'll end up decoding one extra time. > > > > > Since these calls can happen in any order, I also cannot assume that the last > > known complete frame (say 2) will be followed by a request about frame 3. The > > best I could do is loop from frame 2 to the requested frame if I am going to > > maintain a cache. This loop which these comments are on starts at 0 instead of > > the last known complete frame. This is because the last known complete frame > > could be kNotFound. The added complexity of checking for this didn't seem > worth > > it. > > > > But anyway, we can cross that bridge when we get to SkPNGCodec. For now, this > > code works for me for this patch. > > > > > > > > > The only one which might is ICO? But > > > > it would be silly to write code around ICO's behavior rather than the > common > > > > GIF/APNG/WebP behavior. > > > > > > > > I am happy with what we currently having, including the caching. > > > > > > ICO is a more simple way of having complete frames out of order - the frames > > > themselves are not necessarily in order. ICO is a weird case, but it does > seem > > > like the non-caching version covers it *and* is simpler. > > > > I previously incorrectly thought the old Blink decoders didn't cache these > > values (repetition & frame count). But they do -- there is a mutable int > > repetition_count_ in GIFImageDecoder. So keeping the cache might be keeping > > existing behavior. > > > > It is probably more correct for now, since this is supposed to be a shim. > Major > > behavioral differences should happen elsewhere. You had wisely pointed out > over > > a month ago that I had lost this caching behavior: > > [1] > > > https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... > > [2] > > > https://codereview.chromium.org/2565323003/diff/2900001/third_party/WebKit/So... > > > > Given that the IsCleared() case may not even happen any more (I have > encountered > > it in our unit tests but not in normal Chrome code), we could potentially get > > rid of the caching and go back to the old code I had which was more simple. We > > could even further simplify things if we get rid of the IsCleared() case. > > > > But I feel like we should add that as a TODO/new bug and get to it in the > > refactor phase. I would like to land this patch. Getting rid of the caching > fits > > well with getting rid of the IsCleared() state. Those probably go > hand-in-hand. > > And it is probably best to keep that major behavior change out of this patch. > > sgtm https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: max_received_frame_index_ = kNotFound; On 2017/08/10 23:31:26, scroggo_chromium wrote: > I don't think this is quite right. If we had *exactly* the entire first frame > (or even part of the header for the second frame), this will incorrectly report > that frame 0 is incomplete. > > Again, I think it would be simpler to just call getFrameInfo in > FrameIsReceivedAtIndex. You're right. Well, I'm happy to do what you think is best. After all, I need your approval. :) I also agree that it would be more simple to just call getFrameInfo inFrameIsReceivedAtIndex. That is what I used to have. I'm trying to figure out if there is anything that I'm not grasping from our conversaion...I could go back to what we had and lose the caching. I think that's what we're after and we're on the same page. But just to confirm, that is what we had and went away from. We're okay losing the caching for more simple code. It sounds like we're okay going back to the old code, right?
https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: max_received_frame_index_ = kNotFound; On 2017/08/11 16:56:23, cblume wrote: > On 2017/08/10 23:31:26, scroggo_chromium wrote: > > I don't think this is quite right. If we had *exactly* the entire first frame > > (or even part of the header for the second frame), this will incorrectly > report > > that frame 0 is incomplete. > > > > Again, I think it would be simpler to just call getFrameInfo in > > FrameIsReceivedAtIndex. > > You're right. > > Well, I'm happy to do what you think is best. After all, I need your approval. > :) > I also agree that it would be more simple to just call getFrameInfo > inFrameIsReceivedAtIndex. That is what I used to have. > I'm trying to figure out if there is anything that I'm not grasping from our > conversaion...I could go back to what we had and lose the caching. I think > that's what we're after and we're on the same page. But just to confirm, that is > what we had and went away from. We're okay losing the caching for more simple > code. It sounds like we're okay going back to the old code, right? Also, are we talking about getting rid of both cached values (repetition count and max fully-received index)?
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: linux_android_rel_ng on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/linux_androi...)
https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: max_received_frame_index_ = kNotFound; On 2017/08/11 17:33:56, cblume wrote: > On 2017/08/11 16:56:23, cblume wrote: > > On 2017/08/10 23:31:26, scroggo_chromium wrote: > > > I don't think this is quite right. If we had *exactly* the entire first > frame > > > (or even part of the header for the second frame), this will incorrectly > > report > > > that frame 0 is incomplete. > > > > > > Again, I think it would be simpler to just call getFrameInfo in > > > FrameIsReceivedAtIndex. > > > > You're right. > > > > Well, I'm happy to do what you think is best. After all, I need your approval. > > :) > > I also agree that it would be more simple to just call getFrameInfo > > inFrameIsReceivedAtIndex. That is what I used to have. > > I'm trying to figure out if there is anything that I'm not grasping from our > > conversaion...I could go back to what we had and lose the caching. I think > > that's what we're after and we're on the same page. But just to confirm, that > is > > what we had and went away from. We're okay losing the caching for more simple > > code. It sounds like we're okay going back to the old code, right? > > Also, are we talking about getting rid of both cached values (repetition count > and max fully-received index)? I just uploaded a PS which calls getFrameInfo inside FrameIsReceivedAtIndex and without caching. I'm still unsure if this is what you had meant when you mentioned simplifying the code. But it certainly is more simple. I re-read your comments a few times and tried to think of different ways it could be interpreted. You mentioned here that we could call getFrameInfo from inside FrameIsReceivedAtIndex(). Earlier, you mentioned calling it from DecodeFrameCount(). [1] Maybe this was not what you meant? That could be why I'm unsure what you want. [1] https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So...
https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: max_received_frame_index_ = kNotFound; On 2017/08/11 16:56:23, cblume wrote: > On 2017/08/10 23:31:26, scroggo_chromium wrote: > > I don't think this is quite right. If we had *exactly* the entire first frame > > (or even part of the header for the second frame), this will incorrectly > report > > that frame 0 is incomplete. > > > > Again, I think it would be simpler to just call getFrameInfo in > > FrameIsReceivedAtIndex. > > You're right. > > Well, I'm happy to do what you think is best. After all, I need your approval. > :) > I also agree that it would be more simple to just call getFrameInfo > inFrameIsReceivedAtIndex. That is what I used to have. Almost - you called getFrameCount, which was unsafe to call if the SegmentStream had been cleared. I just mean to call getFrameInfo, which is safe (non-const, and does no reading of the stream). I've added a comment in patch set 162 that shows what I mean. > I'm trying to figure out if there is anything that I'm not grasping from our > conversaion...I could go back to what we had and lose the caching. SkCodec has already stored the information, which can be accessed with getFrameInfo. No need for an extra cache. > Also, are we talking about getting rid of both cached values (repetition count > and max fully-received index)? No, I think it makes sense to cache repetition count (as does the old code). > You mentioned here that we could call getFrameInfo from inside > FrameIsReceivedAtIndex(). > Earlier, you mentioned calling it from DecodeFrameCount(). [1] Maybe this was > not what you meant? That could be why I'm unsure what you want. You mostly wrote what I meant here, but after I proposed it, I realized calling getFrameInfo (without getFrameCount) in FrameIsReceivedAtIndex is simpler (and covers the case you were worried about). https://codereview.chromium.org/2565323003/diff/3220001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:121: if (static_cast<size_t>(codec_->getFrameCount()) <= index) This line is unnecessary, since getFrameInfo will check this for you. This method can be: SkCodec::FrameInfo frame_info; if (!codec_ || !codec_->getFrameInfo(index, &frameInfo)) return false; return frame_info.fFullyReceived; (Also, getFrameCount is non-const.) https://codereview.chromium.org/2565323003/diff/3220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:165: if (Failed() || segment_stream_->IsCleared()) The check for Failed() is redundant - codec_ will be null if we have Failed.
https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3180001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:172: max_received_frame_index_ = kNotFound; On 2017/08/14 18:26:06, scroggo_chromium wrote: > On 2017/08/11 16:56:23, cblume wrote: > > On 2017/08/10 23:31:26, scroggo_chromium wrote: > > > I don't think this is quite right. If we had *exactly* the entire first > frame > > > (or even part of the header for the second frame), this will incorrectly > > report > > > that frame 0 is incomplete. > > > > > > Again, I think it would be simpler to just call getFrameInfo in > > > FrameIsReceivedAtIndex. > > > > You're right. > > > > Well, I'm happy to do what you think is best. After all, I need your approval. > > :) > > I also agree that it would be more simple to just call getFrameInfo > > inFrameIsReceivedAtIndex. That is what I used to have. > > Almost - you called getFrameCount, which was unsafe to call if the SegmentStream > had been cleared. I just mean to call getFrameInfo, which is safe (non-const, > and does no reading of the stream). I've added a comment in patch set 162 that > shows what I mean. Oooohhh okay, I see now. I was confused because I was also calling getFrameInfo. But that was the safe call. You were suggesting only calling that, rather than both functions. Okay, fixed. > > I'm trying to figure out if there is anything that I'm not grasping from our > > conversaion...I could go back to what we had and lose the caching. > > SkCodec has already stored the information, which can be accessed with > getFrameInfo. No need for an extra cache. > > > Also, are we talking about getting rid of both cached values (repetition count > > and max fully-received index)? > > No, I think it makes sense to cache repetition count (as does the old code). I see now. It wouldn't make sense to have the whole system only cache one and not the other. We can cache both in the whole system, but use SkCodec's cache of frame received. So the Blink image decoder only needs to cache the repetition count in order for the whole system to cache both. > > You mentioned here that we could call getFrameInfo from inside > > FrameIsReceivedAtIndex(). > > Earlier, you mentioned calling it from DecodeFrameCount(). [1] Maybe this was > > not what you meant? That could be why I'm unsure what you want. > > You mostly wrote what I meant here, but after I proposed it, I realized calling > getFrameInfo (without getFrameCount) in FrameIsReceivedAtIndex is simpler (and > covers the case you were worried about). https://codereview.chromium.org/2565323003/diff/3220001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:121: if (static_cast<size_t>(codec_->getFrameCount()) <= index) On 2017/08/14 18:26:06, scroggo_chromium wrote: > This line is unnecessary, since getFrameInfo will check this for you. This > method can be: > > SkCodec::FrameInfo frame_info; > if (!codec_ || !codec_->getFrameInfo(index, &frameInfo)) > return false; > return frame_info.fFullyReceived; > > (Also, getFrameCount is non-const.) Done. https://codereview.chromium.org/2565323003/diff/3220001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:165: if (Failed() || segment_stream_->IsCleared()) On 2017/08/14 18:26:06, scroggo_chromium wrote: > The check for Failed() is redundant - codec_ will be null if we have Failed. Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:165: return 0; I think this should return frame_buffer_cache_.size(), so we don't underreport after a failure/while the stream is cleared. https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:82: mutable int cached_repetition_count_ = kAnimationLoopOnce; nit: This seems fine, but it also seems arbitrarily different from the old code, which calls it "repetition_count_" and initializes it in the constructor. I guess there is a slight difference - the new code may return it in the case of a cleared segment stream, in which case it's our cached value.
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:165: return 0; On 2017/08/14 20:49:41, scroggo_chromium wrote: > I think this should return frame_buffer_cache_.size(), so we don't underreport > after a failure/while the stream is cleared. Done. https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:82: mutable int cached_repetition_count_ = kAnimationLoopOnce; On 2017/08/14 20:49:41, scroggo_chromium wrote: > nit: This seems fine, but it also seems arbitrarily different from the old code, > which calls it "repetition_count_" and initializes it in the constructor. > > I guess there is a slight difference - the new code may return it in the case of > a cleared segment stream, in which case it's our cached value. A previous comment from vmpstr@ suggested initializing in the header rather than the ctor: https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... I'm okay with renaming away from "cached_*". I guess that is implied anyway by the fact that it is stored in a variable.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h (right): https://codereview.chromium.org/2565323003/diff/3240001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.h:82: mutable int cached_repetition_count_ = kAnimationLoopOnce; On 2017/08/14 23:07:26, cblume wrote: > On 2017/08/14 20:49:41, scroggo_chromium wrote: > > nit: This seems fine, but it also seems arbitrarily different from the old > code, > > which calls it "repetition_count_" and initializes it in the constructor. > > > > I guess there is a slight difference - the new code may return it in the case > of > > a cleared segment stream, in which case it's our cached value. > > A previous comment from vmpstr@ suggested initializing in the header rather than > the ctor: > https://codereview.chromium.org/2565323003/diff/2480001/third_party/WebKit/So... > > I'm okay with renaming away from "cached_*". I guess that is implied anyway by > the fact that it is stored in a variable. Yeah, that's especially helpful when you have multiple constructors. Maybe we should be consistent and initialize none or all here? This is fine though; just trying to understand why the change. https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:98: has_read_all_contents_ = position_ >= reader_size; Instead of computing this value each time we change the position, why not compute this once in isAtEnd? bool SegmentStream::isAtEnd() const { return !reader_ || position_ >= reader_->size(); } We can do the same for is_cleared_: bool SegmentStream::IsCleared() const { return !reader_ || position_ > reader_->size(); } Then we can drop SetPositionState, and set the position directly. https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.h (right): https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.h:9: #include "platform/image-decoders/SegmentReader.h" In [1] I suggested changing this to a forward declare (which requires not inlining getLength). Thoughts? [1] https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:58: segment_stream_->SetReader(PassRefPtr<SegmentReader>(data)); I think PassRefPtr is deprecated? https://docs.google.com/document/d/1POkqlkx59grmrlRmG-1Ir1dIO_r66aua0vQJ9Eds_... https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:318: if (required_previous_frame_index != kNotFound) { This is checking to see if the frame is independent. One of the callsites already does so. Should we make the other one check, too, and then simplify this method? DCHECK_NE(required_previous_frame_index, kNotFound); for (...) { ... if (frame.GetStatus() == ImageFrame::kFrameComplete) return i; }
https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:98: has_read_all_contents_ = position_ >= reader_size; On 2017/08/15 15:19:44, scroggo_chromium wrote: > Instead of computing this value each time we change the position, why not > compute this once in isAtEnd? > > bool SegmentStream::isAtEnd() const { > return !reader_ || position_ >= reader_->size(); > } > > We can do the same for is_cleared_: > > bool SegmentStream::IsCleared() const { > return !reader_ || position_ > reader_->size(); > } > > Then we can drop SetPositionState, and set the position directly. Done. https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.h (right): https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.h:9: #include "platform/image-decoders/SegmentReader.h" On 2017/08/15 15:19:44, scroggo_chromium wrote: > In [1] I suggested changing this to a forward declare (which requires not > inlining getLength). Thoughts? > > > [1] > https://codereview.chromium.org/2565323003/diff/3080001/third_party/WebKit/So... Oh sorry. I thought you had suggested forward declaring SegmentStream (in GIFImageDecoder.h) rather than SegmentReader (here). I've now done both. :) https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:58: segment_stream_->SetReader(PassRefPtr<SegmentReader>(data)); On 2017/08/15 15:19:44, scroggo_chromium wrote: > I think PassRefPtr is deprecated? > > https://docs.google.com/document/d/1POkqlkx59grmrlRmG-1Ir1dIO_r66aua0vQJ9Eds_... Done. https://codereview.chromium.org/2565323003/diff/3260001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:318: if (required_previous_frame_index != kNotFound) { On 2017/08/15 15:19:44, scroggo_chromium wrote: > This is checking to see if the frame is independent. One of the callsites > already does so. Should we make the other one check, too, and then simplify this > method? > > DCHECK_NE(required_previous_frame_index, kNotFound); > for (...) { > ... > if (frame.GetStatus() == ImageFrame::kFrameComplete) > return i; > } Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:14: : reader_(std::move(rhs.reader_)), position_(rhs.position_) {} Can this just be = default? https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:18: SegmentStream& SegmentStream::operator=(SegmentStream&& rhs) { Same question here. https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:34: if (IsCleared()) Oh yeah, I forgot that you used |is_cleared_| internally. So you've traded computing is_cleared_ once at the bottom of this method for computing the same twice (once here, and once inside peek()). That should be fine. I was wondering if we could DCHECK(!IsCleared()), since we shouldn't be calling these methods when cleared, but I suppose you didn't because the API allows a client to call even when cleared? https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:154: if (frame.GetStatus() != ImageFrame::kFrameEmpty && I think you mean if (frame.RequiredPreviousFrameIndex() != kNotFound && ? https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:330: previous_reference_frame_index = i; This variable is no longer needed. You can just return i;
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: This issue passed the CQ dry run.
https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:14: : reader_(std::move(rhs.reader_)), position_(rhs.position_) {} On 2017/08/15 19:57:31, scroggo_chromium wrote: > Can this just be = default? Yes. The C++11 standard [1] says in §12.8/15 "The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members." (Note, there is a difference between implicitly-declared and implicitly-defined. But in our case, this is implicitly-defined.) While we're at it, right now the fact that this class is non-copyable is rather hidden. I would like to explicitly delete the copy ctor/assign. I think it helps readability. [1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:34: if (IsCleared()) On 2017/08/15 19:57:31, scroggo_chromium wrote: > Oh yeah, I forgot that you used |is_cleared_| internally. So you've traded > computing is_cleared_ once at the bottom of this method for computing the same > twice (once here, and once inside peek()). > > That should be fine. > > I was wondering if we could DCHECK(!IsCleared()), since we shouldn't be calling > these methods when cleared, but I suppose you didn't because the API allows a > client to call even when cleared? Hrmmm we can. The GIF decoder has a bunch of checks for IsCleared() which end up preventing the calls to read/peek. I'll do this & get rid of the tests which covered this (since they would now trigger the DCHECK). https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp (right): https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:154: if (frame.GetStatus() != ImageFrame::kFrameEmpty && On 2017/08/15 19:57:31, scroggo_chromium wrote: > I think you mean > > if (frame.RequiredPreviousFrameIndex() != kNotFound && > > ? Whoa, yes. One of the items in the refactor phase is to use the type system to prevent things like this. Would be nice to catch my mistake at compile-time rather than in code review. :) Thank you. https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/gif/GIFImageDecoder.cpp:330: previous_reference_frame_index = i; On 2017/08/15 19:57:31, scroggo_chromium wrote: > This variable is no longer needed. You can just > > return i; Done.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_android on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/cast_shell_a...)
https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:34: if (IsCleared()) On 2017/08/16 07:49:16, cblume wrote: > On 2017/08/15 19:57:31, scroggo_chromium wrote: > > Oh yeah, I forgot that you used |is_cleared_| internally. So you've traded > > computing is_cleared_ once at the bottom of this method for computing the same > > twice (once here, and once inside peek()). > > > > That should be fine. > > > > I was wondering if we could DCHECK(!IsCleared()), since we shouldn't be > calling > > these methods when cleared, but I suppose you didn't because the API allows a > > client to call even when cleared? > > Hrmmm we can. > The GIF decoder has a bunch of checks for IsCleared() which end up preventing > the calls to read/peek. > I'll do this & get rid of the tests which covered this (since they would now > trigger the DCHECK). Other than this I think you've addressed my concerns.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: cast_shell_android on master.tryserver.chromium.android (JOB_FAILED, https://build.chromium.org/p/tryserver.chromium.android/builders/cast_shell_a...)
https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... File third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp (right): https://codereview.chromium.org/2565323003/diff/3340001/third_party/WebKit/So... third_party/WebKit/Source/platform/image-decoders/SegmentStream.cpp:34: if (IsCleared()) On 2017/08/16 13:46:40, scroggo_chromium wrote: > On 2017/08/16 07:49:16, cblume wrote: > > On 2017/08/15 19:57:31, scroggo_chromium wrote: > > > Oh yeah, I forgot that you used |is_cleared_| internally. So you've traded > > > computing is_cleared_ once at the bottom of this method for computing the > same > > > twice (once here, and once inside peek()). > > > > > > That should be fine. > > > > > > I was wondering if we could DCHECK(!IsCleared()), since we shouldn't be > > calling > > > these methods when cleared, but I suppose you didn't because the API allows > a > > > client to call even when cleared? > > > > Hrmmm we can. > > The GIF decoder has a bunch of checks for IsCleared() which end up preventing > > the calls to read/peek. > > I'll do this & get rid of the tests which covered this (since they would now > > trigger the DCHECK). > > Other than this I think you've addressed my concerns. Oops. Sorry. Not sure why that didn't get uploaded. It should be up now. BTW, the reason I wasn't using =default before is because it requires this patch which hasn't landed yet: https://skia-review.googlesource.com/c/24580
> BTW, the reason I wasn't using =default before is because it requires this patch > which hasn't landed yet: > https://skia-review.googlesource.com/c/24580 lgtm. I'm fine if you go back to not using =default.
The CQ bit was checked by cblume@chromium.org to run a CQ dry run
Dry run: CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
The CQ bit was unchecked by commit-bot@chromium.org
Dry run: Try jobs failed on following builders: win_chromium_rel_ng on master.tryserver.chromium.win (JOB_FAILED, http://build.chromium.org/p/tryserver.chromium.win/builders/win_chromium_rel_...)
The CQ bit was checked by cblume@chromium.org
The patchset sent to the CQ was uploaded after l-g-t-m from fmalita@chromium.org, msarett@google.com, ccameron@chromium.org, scroggo@chromium.org Link to the patchset: https://codereview.chromium.org/2565323003/#ps3500001 (title: "Explicitly specify move ctor / assignment until required patch lands.")
CQ is trying da patch. Follow status at: https://chromium-cq-status.appspot.com/v2/patch-status/codereview.chromium.or...
CQ is committing da patch. Bot data: {"patchset_id": 3500001, "attempt_start_ts": 1502989745378970, "parent_rev": "cae7bbbf50c2e33926a0d0a6a3c2e36d3777ba5b", "commit_rev": "0187ec140296e4430aa65e6aed9e00e80e9d586f"}
CQ is committing da patch. Bot data: {"patchset_id": 3500001, "attempt_start_ts": 1502989745378970, "parent_rev": "a89466ce1d4f5aac646b16c0c66a50cdeb8f228d", "commit_rev": "4fed3346549a90c0de40c02f6388e19e8151e92a"}
Message was sent while issue was closed.
Description was changed from ========== Use SkCodec internally in GIFImageDecoder Previously, GIFImageDecoder used GIFImageReader internally. SkCodec uses a modified version of that class (SkGifImageReader; adapted in crrev.com/2045293002). SkCodec provides the following benefits: - SIMD optimized code for writing pixels - an API that allows the client to handle caching - flexibility regarding the required frame to use - the ability to decode scaled versions of images - subset decoding (i.e. issue 468914) (not fully implemented for GIF) - the ability to decode to half-width float format In addition, this patch enables sharing code between Android, Skia, and Chromium. This means that new features/bug fixes in Android benefit Chromium and vice versa. For larger images (above ~60x60), the SIMD optimizations show a much bigger benefit (up to 24% in one case). For most images, decoding speed is about the same. Images with many frames that contain tiny update regions are a hair slower. The mean decode time across all tested images showed an improvement. Raw performance numbers can be found here: https://docs.google.com/spreadsheets/d/1JqCPdYmbasOwKRdvuG6ZI4gwq9dPvnJA9oDqu... GIFImageDecoder still handles the cached frames currently, but this change will allow future changes in Blink to make wiser caching Decisions (such as keeping all frames of a 3-frame animation if those frames are small). Full road map: https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... This results in some behavior changes: - SkCodec does not check the alpha of each pixel during decode (for speed and simplicity). As a result, GIFImageDecoder no longer corrects opacity or the required frame after decoding a frame. No performance penalty has been observed for incorrectly leaving a frame marked as having transparency. - SkCodec guesses transparency based on the presence of a transparent index (in addition to being subset) and uses this to potentially determine an earlier required frame. BUG=715812 ========== to ========== Use SkCodec internally in GIFImageDecoder Previously, GIFImageDecoder used GIFImageReader internally. SkCodec uses a modified version of that class (SkGifImageReader; adapted in crrev.com/2045293002). SkCodec provides the following benefits: - SIMD optimized code for writing pixels - an API that allows the client to handle caching - flexibility regarding the required frame to use - the ability to decode scaled versions of images - subset decoding (i.e. issue 468914) (not fully implemented for GIF) - the ability to decode to half-width float format In addition, this patch enables sharing code between Android, Skia, and Chromium. This means that new features/bug fixes in Android benefit Chromium and vice versa. For larger images (above ~60x60), the SIMD optimizations show a much bigger benefit (up to 24% in one case). For most images, decoding speed is about the same. Images with many frames that contain tiny update regions are a hair slower. The mean decode time across all tested images showed an improvement. Raw performance numbers can be found here: https://docs.google.com/spreadsheets/d/1JqCPdYmbasOwKRdvuG6ZI4gwq9dPvnJA9oDqu... GIFImageDecoder still handles the cached frames currently, but this change will allow future changes in Blink to make wiser caching Decisions (such as keeping all frames of a 3-frame animation if those frames are small). Full road map: https://docs.google.com/a/chromium.org/document/d/1T06pxiff3oy8KDqWGqWL-_nZqU... This results in some behavior changes: - SkCodec does not check the alpha of each pixel during decode (for speed and simplicity). As a result, GIFImageDecoder no longer corrects opacity or the required frame after decoding a frame. No performance penalty has been observed for incorrectly leaving a frame marked as having transparency. - SkCodec guesses transparency based on the presence of a transparent index (in addition to being subset) and uses this to potentially determine an earlier required frame. BUG=715812 Review-Url: https://codereview.chromium.org/2565323003 Cr-Commit-Position: refs/heads/master@{#495230} Committed: https://chromium.googlesource.com/chromium/src/+/4fed3346549a90c0de40c02f6388... ==========
Message was sent while issue was closed.
Committed patchset #176 (id:3500001) as https://chromium.googlesource.com/chromium/src/+/4fed3346549a90c0de40c02f6388... |