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

Side by Side Diff: src/codec/SkWebpCodec.cpp

Issue 2311793004: Use demux API in SkWebpCodec (Closed)
Patch Set: Fix test Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2015 Google Inc. 2 * Copyright 2015 Google Inc.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license that can be 4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file. 5 * found in the LICENSE file.
6 */ 6 */
7 7
8 #include "SkCodecPriv.h" 8 #include "SkCodecPriv.h"
9 #include "SkWebpCodec.h" 9 #include "SkWebpCodec.h"
10 #include "SkStreamPriv.h"
10 #include "SkTemplates.h" 11 #include "SkTemplates.h"
11 12
12 // A WebP decoder on top of (subset of) libwebp 13 // A WebP decoder on top of (subset of) libwebp
13 // For more information on WebP image format, and libwebp library, see: 14 // For more information on WebP image format, and libwebp library, see:
14 // https://code.google.com/speed/webp/ 15 // https://code.google.com/speed/webp/
15 // http://www.webmproject.org/code/#libwebp-webp-image-library 16 // http://www.webmproject.org/code/#libwebp-webp-image-library
16 // https://chromium.googlesource.com/webm/libwebp 17 // https://chromium.googlesource.com/webm/libwebp
17 18
18 // If moving libwebp out of skia source tree, path for webp headers must be 19 // If moving libwebp out of skia source tree, path for webp headers must be
19 // updated accordingly. Here, we enforce using local copy in webp sub-directory. 20 // updated accordingly. Here, we enforce using local copy in webp sub-directory.
20 #include "webp/decode.h" 21 #include "webp/decode.h"
21 #include "webp/demux.h" 22 #include "webp/demux.h"
22 #include "webp/encode.h" 23 #include "webp/encode.h"
23 24
24 bool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) { 25 bool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) {
25 // WEBP starts with the following: 26 // WEBP starts with the following:
26 // RIFFXXXXWEBPVP 27 // RIFFXXXXWEBPVP
27 // Where XXXX is unspecified. 28 // Where XXXX is unspecified.
28 const char* bytes = static_cast<const char*>(buf); 29 const char* bytes = static_cast<const char*>(buf);
29 return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "W EBPVP", 6); 30 return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "W EBPVP", 6);
30 } 31 }
31 32
32 // Parse headers of RIFF container, and check for valid Webp (VP8) content. 33 // Parse headers of RIFF container, and check for valid Webp (VP8) content.
33 // NOTE: This calls peek instead of read, since onGetPixels will need these 34 // NOTE: This calls peek instead of read, since onGetPixels will need these
34 // bytes again. 35 // bytes again.
35 // Returns an SkWebpCodec on success; 36 // Returns an SkWebpCodec on success;
36 SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) { 37 SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
37 SkAutoTDelete<SkStream> streamDeleter(stream); 38 SkAutoTDelete<SkStream> streamDeleter(stream);
38 39
39 unsigned char buffer[WEBP_VP8_HEADER_SIZE]; 40 // Webp demux needs a contiguous data buffer.
40 SkASSERT(WEBP_VP8_HEADER_SIZE <= SkCodec::MinBufferedBytesNeeded()); 41 sk_sp<SkData> data = nullptr;
42 if (stream->getMemoryBase()) {
43 // It is safe to make without copy because we'll hold onto the stream.
44 data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLengt h());
45 } else {
46 data = SkCopyStreamToData(stream);
41 47
42 const size_t bytesPeeked = stream->peek(buffer, WEBP_VP8_HEADER_SIZE); 48 // If we are forced to copy the stream to a data, we can go ahead and de lete the stream.
43 if (bytesPeeked != WEBP_VP8_HEADER_SIZE) { 49 streamDeleter.reset(nullptr);
44 // Use read + rewind as a backup 50 }
45 if (stream->read(buffer, WEBP_VP8_HEADER_SIZE) != WEBP_VP8_HEADER_SIZE 51
46 || !stream->rewind()) 52 // It's a little strange that the |demux| will outlive |webpData|, though it needs the
53 // pointer in |webpData| to remain valid. This works because the pointer re mains valid
54 // until the SkData is freed.
55 WebPData webpData = { data->bytes(), data->size() };
56 SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&webpD ata, nullptr));
57 if (nullptr == demux) {
47 return nullptr; 58 return nullptr;
48 } 59 }
49 60
50 WebPBitstreamFeatures features; 61 WebPChunkIterator chunkIterator;
51 VP8StatusCode status = WebPGetFeatures(buffer, WEBP_VP8_HEADER_SIZE, &featur es); 62 SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&c hunkIterator);
52 if (VP8_STATUS_OK != status) { 63 sk_sp<SkColorSpace> colorSpace = nullptr;
53 return nullptr; // Invalid WebP file. 64 if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) {
65 colorSpace = SkColorSpace::NewICC(chunkIterator.chunk.bytes, chunkIterat or.chunk.size);
54 } 66 }
55 67
56 // sanity check for image size that's about to be decoded. 68 if (!colorSpace) {
69 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
70 }
71
72 // Since we do not yet support animation, we get the |width|, |height|, |col or|, and |alpha|
73 // from the first frame. It's the only frame we will decode.
74 //
75 // TODO:
76 // When we support animation, we'll want to report the canvas width and canv as height instead.
77 // We can get these from the |demux| directly.
78 // What |color| and |alpha| will we want to report though? WebP allows diff erent frames
79 // to be encoded in different ways, making the encoded format difficult to d escribe.
80 WebPIterator frame;
81 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
82 if (!WebPDemuxGetFrame(demux, 1, &frame)) {
83 return nullptr;
84 }
85
86 // Sanity check for image size that's about to be decoded.
57 { 87 {
58 const int64_t size = sk_64_mul(features.width, features.height); 88 const int64_t size = sk_64_mul(frame.width, frame.height);
59 if (!sk_64_isS32(size)) { 89 if (!sk_64_isS32(size)) {
60 return nullptr; 90 return nullptr;
61 } 91 }
62 // now check that if we are 4-bytes per pixel, we also don't overflow 92 // now check that if we are 4-bytes per pixel, we also don't overflow
63 if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) { 93 if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) {
64 return nullptr; 94 return nullptr;
65 } 95 }
66 } 96 }
67 97
98 // TODO:
99 // The only reason we actually need to call WebPGetFeatures() is to get the |features.format|.
100 // This call actually re-reads the frame header. Should we suggest that lib webp expose
101 // the format on the |frame|?
102 WebPBitstreamFeatures features;
103 VP8StatusCode status = WebPGetFeatures(frame.fragment.bytes, frame.fragment. size, &features);
104 if (VP8_STATUS_OK != status) {
105 return nullptr;
106 }
107
68 SkEncodedInfo::Color color; 108 SkEncodedInfo::Color color;
69 SkEncodedInfo::Alpha alpha; 109 SkEncodedInfo::Alpha alpha;
70 switch (features.format) { 110 switch (features.format) {
71 case 0: 111 case 0:
72 // This indicates a "mixed" format. We would see this for 112 // This indicates a "mixed" format. We would see this for
73 // animated webps or for webps encoded in multiple fragments. 113 // animated webps or for webps encoded in multiple fragments.
74 // I believe that this is a rare case. 114 // I believe that this is a rare case.
75 // We could also guess kYUV here, but I think it makes more 115 // We could also guess kYUV here, but I think it makes more
76 // sense to guess kBGRA which is likely closer to the final 116 // sense to guess kBGRA which is likely closer to the final
77 // output. Otherwise, we might end up converting 117 // output. Otherwise, we might end up converting
(...skipping 13 matching lines...) Expand all
91 break; 131 break;
92 case 2: 132 case 2:
93 // This is the lossless format (BGRA). 133 // This is the lossless format (BGRA).
94 color = SkEncodedInfo::kBGRA_Color; 134 color = SkEncodedInfo::kBGRA_Color;
95 alpha = SkEncodedInfo::kUnpremul_Alpha; 135 alpha = SkEncodedInfo::kUnpremul_Alpha;
96 break; 136 break;
97 default: 137 default:
98 return nullptr; 138 return nullptr;
99 } 139 }
100 140
101 // FIXME (msarett):
102 // Temporary strategy for getting ICC profiles from webps. Once the increme ntal decoding
103 // API lands, we will use the WebPDemuxer to manage the entire decode.
104 sk_sp<SkColorSpace> colorSpace = nullptr;
105 const void* memory = stream->getMemoryBase();
106 if (memory) {
107 WebPData data = { (const uint8_t*) memory, stream->getLength() };
108 WebPDemuxState state;
109 SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&d ata, &state));
110
111 WebPChunkIterator chunkIterator;
112 SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoC I(&chunkIterator);
113 if (demux && WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) {
114 colorSpace = SkColorSpace::NewICC(chunkIterator.chunk.bytes, chunkIt erator.chunk.size);
115 }
116 }
117
118 if (!colorSpace) {
119 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
120 }
121
122 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8); 141 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
123 return new SkWebpCodec(features.width, features.height, info, colorSpace, 142 return new SkWebpCodec(features.width, features.height, info, std::move(colo rSpace),
124 streamDeleter.release()); 143 streamDeleter.release(), demux.release(), std::move(d ata));
125 } 144 }
126 145
127 // This version is slightly different from SkCodecPriv's version of conversion_p ossible. It 146 // This version is slightly different from SkCodecPriv's version of conversion_p ossible. It
128 // supports both byte orders for 8888. 147 // supports both byte orders for 8888.
129 static bool webp_conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) { 148 static bool webp_conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
130 if (!valid_alpha(dst.alphaType(), src.alphaType())) { 149 if (!valid_alpha(dst.alphaType(), src.alphaType())) {
131 return false; 150 return false;
132 } 151 }
133 152
134 switch (dst.colorType()) { 153 switch (dst.colorType()) {
(...skipping 16 matching lines...) Expand all
151 dim.fHeight = SkTMax(1, SkScalarRoundToInt(desiredScale * dim.fHeight)); 170 dim.fHeight = SkTMax(1, SkScalarRoundToInt(desiredScale * dim.fHeight));
152 return dim; 171 return dim;
153 } 172 }
154 173
155 bool SkWebpCodec::onDimensionsSupported(const SkISize& dim) { 174 bool SkWebpCodec::onDimensionsSupported(const SkISize& dim) {
156 const SkImageInfo& info = this->getInfo(); 175 const SkImageInfo& info = this->getInfo();
157 return dim.width() >= 1 && dim.width() <= info.width() 176 return dim.width() >= 1 && dim.width() <= info.width()
158 && dim.height() >= 1 && dim.height() <= info.height(); 177 && dim.height() >= 1 && dim.height() <= info.height();
159 } 178 }
160 179
161
162 static WEBP_CSP_MODE webp_decode_mode(SkColorType ct, bool premultiply) { 180 static WEBP_CSP_MODE webp_decode_mode(SkColorType ct, bool premultiply) {
163 switch (ct) { 181 switch (ct) {
164 case kBGRA_8888_SkColorType: 182 case kBGRA_8888_SkColorType:
165 return premultiply ? MODE_bgrA : MODE_BGRA; 183 return premultiply ? MODE_bgrA : MODE_BGRA;
166 case kRGBA_8888_SkColorType: 184 case kRGBA_8888_SkColorType:
167 return premultiply ? MODE_rgbA : MODE_RGBA; 185 return premultiply ? MODE_rgbA : MODE_RGBA;
168 case kRGB_565_SkColorType: 186 case kRGB_565_SkColorType:
169 return MODE_RGB_565; 187 return MODE_RGB_565;
170 default: 188 default:
171 return MODE_LAST; 189 return MODE_LAST;
172 } 190 }
173 } 191 }
174 192
175 // The WebP decoding API allows us to incrementally pass chunks of bytes as we r eceive them to the
176 // decoder with WebPIAppend. In order to do so, we need to read chunks from the SkStream. This size
177 // is arbitrary.
178 static const size_t BUFFER_SIZE = 4096;
179
180 bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const { 193 bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const {
181 if (!desiredSubset) { 194 if (!desiredSubset) {
182 return false; 195 return false;
183 } 196 }
184 197
185 SkIRect dimensions = SkIRect::MakeSize(this->getInfo().dimensions()); 198 SkIRect dimensions = SkIRect::MakeSize(this->getInfo().dimensions());
186 if (!dimensions.contains(*desiredSubset)) { 199 if (!dimensions.contains(*desiredSubset)) {
187 return false; 200 return false;
188 } 201 }
189 202
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 config.options.scaled_height = dstDimensions.height(); 269 config.options.scaled_height = dstDimensions.height();
257 } 270 }
258 271
259 config.output.colorspace = webp_decode_mode(dstInfo.colorType(), 272 config.output.colorspace = webp_decode_mode(dstInfo.colorType(),
260 dstInfo.alphaType() == kPremul_SkAlphaType); 273 dstInfo.alphaType() == kPremul_SkAlphaType);
261 config.output.u.RGBA.rgba = (uint8_t*) dst; 274 config.output.u.RGBA.rgba = (uint8_t*) dst;
262 config.output.u.RGBA.stride = (int) rowBytes; 275 config.output.u.RGBA.stride = (int) rowBytes;
263 config.output.u.RGBA.size = dstInfo.getSafeSize(rowBytes); 276 config.output.u.RGBA.size = dstInfo.getSafeSize(rowBytes);
264 config.output.is_external_memory = 1; 277 config.output.is_external_memory = 1;
265 278
279 WebPIterator frame;
280 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
281 // If this succeeded in NewFromStream(), it should succeed again here.
282 SkAssertResult(WebPDemuxGetFrame(fDemux, 1, &frame));
283
266 SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &co nfig)); 284 SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &co nfig));
267 if (!idec) { 285 if (!idec) {
268 return kInvalidInput; 286 return kInvalidInput;
269 } 287 }
270 288
271 SkAutoTMalloc<uint8_t> storage(BUFFER_SIZE); 289 switch (WebPIUpdate(idec, frame.fragment.bytes, frame.fragment.size)) {
272 uint8_t* buffer = storage.get(); 290 case VP8_STATUS_OK:
273 while (true) { 291 return kSuccess;
274 const size_t bytesRead = stream()->read(buffer, BUFFER_SIZE); 292 case VP8_STATUS_SUSPENDED:
275 if (0 == bytesRead) { 293 WebPIDecGetRGB(idec, rowsDecoded, nullptr, nullptr, nullptr);
276 WebPIDecGetRGB(idec, rowsDecoded, NULL, NULL, NULL);
277 return kIncompleteInput; 294 return kIncompleteInput;
278 } 295 default:
279 296 return kInvalidInput;
280 switch (WebPIAppend(idec, buffer, bytesRead)) {
281 case VP8_STATUS_OK:
282 return kSuccess;
283 case VP8_STATUS_SUSPENDED:
284 // Break out of the switch statement. Continue the loop.
285 break;
286 default:
287 return kInvalidInput;
288 }
289 } 297 }
290 } 298 }
291 299
292 SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info, 300 SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info,
293 sk_sp<SkColorSpace> colorSpace, SkStream* stream) 301 sk_sp<SkColorSpace> colorSpace, SkStream* stream, WebPD emuxer* demux,
294 : INHERITED(width, height, info, stream, colorSpace) 302 sk_sp<SkData> data)
303 : INHERITED(width, height, info, stream, std::move(colorSpace))
304 , fDemux(demux)
305 , fData(std::move(data))
295 {} 306 {}
OLDNEW
« src/codec/SkCodec.cpp ('K') | « src/codec/SkWebpCodec.h ('k') | tests/CodecTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698