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

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

Issue 2311793004: Use demux API in SkWebpCodec (Closed)
Patch Set: Forward declare 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
« no previous file with comments | « src/codec/SkWebpCodec.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "SkTemplates.h" 10 #include "SkTemplates.h"
(...skipping 18 matching lines...) Expand all
29 return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "W EBPVP", 6); 29 return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "W EBPVP", 6);
30 } 30 }
31 31
32 // Parse headers of RIFF container, and check for valid Webp (VP8) content. 32 // 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 33 // NOTE: This calls peek instead of read, since onGetPixels will need these
34 // bytes again. 34 // bytes again.
35 // Returns an SkWebpCodec on success; 35 // Returns an SkWebpCodec on success;
36 SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) { 36 SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) {
37 SkAutoTDelete<SkStream> streamDeleter(stream); 37 SkAutoTDelete<SkStream> streamDeleter(stream);
38 38
39 unsigned char buffer[WEBP_VP8_HEADER_SIZE]; 39 // Webp demux needs a contiguous data buffer.
40 SkASSERT(WEBP_VP8_HEADER_SIZE <= SkCodec::MinBufferedBytesNeeded()); 40 sk_sp<SkData> data = nullptr;
41 if (stream->getMemoryBase()) {
scroggo 2016/09/07 14:20:51 I think we have some common code to handle this ca
msarett 2016/09/07 16:47:10 Ahh yes. The common code always performs a copy,
42 data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLengt h());
scroggo 2016/09/07 14:20:51 Maybe add a comment here that this is safe because
msarett 2016/09/07 16:47:10 SGTM
43 } else if (stream->hasLength()) {
44 size_t size = stream->getLength();
45 data = SkData::MakeUninitialized(size);
46 stream->read(data->writable_data(), size);
41 47
42 const size_t bytesPeeked = stream->peek(buffer, WEBP_VP8_HEADER_SIZE); 48 // Go ahead and delete the stream, we don't need it anymore.
43 if (bytesPeeked != WEBP_VP8_HEADER_SIZE) { 49 streamDeleter.reset(nullptr);
44 // Use read + rewind as a backup 50 } else {
45 if (stream->read(buffer, WEBP_VP8_HEADER_SIZE) != WEBP_VP8_HEADER_SIZE 51 SkDynamicMemoryWStream writeStream;
46 || !stream->rewind()) 52 static constexpr size_t kChunkSize = 8192; // Arbitrary
53 uint8_t buffer[kChunkSize];
54 size_t bytesRead;
55 do {
56 bytesRead = stream->read(buffer, kChunkSize);
57 writeStream.write(buffer, bytesRead);
58 } while (bytesRead == kChunkSize);
59 data.reset(writeStream.copyToData());
60
61 streamDeleter.reset(nullptr);
62 }
63
64 WebPData webpData = { data->bytes(), data->size() };
65 SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&webpD ata, nullptr));
scroggo 2016/09/07 14:20:51 I think this is OK (in fact I think it's what chro
msarett 2016/09/07 16:47:10 Agree that this is weird... Don't think it's nice
66 if (nullptr == demux) {
67 return nullptr;
68 }
69
70 WebPChunkIterator chunkIterator;
71 SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&c hunkIterator);
72 sk_sp<SkColorSpace> colorSpace = nullptr;
73 if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) {
74 colorSpace = SkColorSpace::NewICC(chunkIterator.chunk.bytes, chunkIterat or.chunk.size);
75 }
76
77 if (!colorSpace) {
78 colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
79 }
80
81 WebPIterator frame;
82 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
83 if (!WebPDemuxGetFrame(demux, 1, &frame)) {
scroggo 2016/09/07 14:20:51 I forget, does this decode the frame? I guess not,
msarett 2016/09/07 16:47:10 Yes, this won't decode the image - just sets us up
47 return nullptr; 84 return nullptr;
48 } 85 }
49 86
50 WebPBitstreamFeatures features; 87 WebPBitstreamFeatures features;
51 VP8StatusCode status = WebPGetFeatures(buffer, WEBP_VP8_HEADER_SIZE, &featur es); 88 VP8StatusCode status = WebPGetFeatures(frame.fragment.bytes, frame.fragment. size, &features);
52 if (VP8_STATUS_OK != status) { 89 if (VP8_STATUS_OK != status) {
53 return nullptr; // Invalid WebP file. 90 return nullptr;
54 } 91 }
55 92
56 // sanity check for image size that's about to be decoded. 93 // Sanity check for image size that's about to be decoded.
57 { 94 {
58 const int64_t size = sk_64_mul(features.width, features.height); 95 const int64_t size = sk_64_mul(features.width, features.height);
59 if (!sk_64_isS32(size)) { 96 if (!sk_64_isS32(size)) {
60 return nullptr; 97 return nullptr;
61 } 98 }
62 // now check that if we are 4-bytes per pixel, we also don't overflow 99 // now check that if we are 4-bytes per pixel, we also don't overflow
63 if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) { 100 if (sk_64_asS32(size) > (0x7FFFFFFF >> 2)) {
64 return nullptr; 101 return nullptr;
65 } 102 }
66 } 103 }
(...skipping 24 matching lines...) Expand all
91 break; 128 break;
92 case 2: 129 case 2:
93 // This is the lossless format (BGRA). 130 // This is the lossless format (BGRA).
94 color = SkEncodedInfo::kBGRA_Color; 131 color = SkEncodedInfo::kBGRA_Color;
95 alpha = SkEncodedInfo::kUnpremul_Alpha; 132 alpha = SkEncodedInfo::kUnpremul_Alpha;
96 break; 133 break;
97 default: 134 default:
98 return nullptr; 135 return nullptr;
99 } 136 }
100 137
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); 138 SkEncodedInfo info = SkEncodedInfo::Make(color, alpha, 8);
123 return new SkWebpCodec(features.width, features.height, info, colorSpace, 139 return new SkWebpCodec(features.width, features.height, info, std::move(colo rSpace),
124 streamDeleter.release()); 140 streamDeleter.release(), demux.release(), std::move(d ata));
scroggo 2016/09/07 14:20:51 Sometimes streamDeleter will hold null. I forgot,
msarett 2016/09/07 16:47:10 I hadn't thought about this, but now that I look c
125 } 141 }
126 142
127 // This version is slightly different from SkCodecPriv's version of conversion_p ossible. It 143 // This version is slightly different from SkCodecPriv's version of conversion_p ossible. It
128 // supports both byte orders for 8888. 144 // supports both byte orders for 8888.
129 static bool webp_conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) { 145 static bool webp_conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) {
130 if (!valid_alpha(dst.alphaType(), src.alphaType())) { 146 if (!valid_alpha(dst.alphaType(), src.alphaType())) {
131 return false; 147 return false;
132 } 148 }
133 149
134 switch (dst.colorType()) { 150 switch (dst.colorType()) {
(...skipping 16 matching lines...) Expand all
151 dim.fHeight = SkTMax(1, SkScalarRoundToInt(desiredScale * dim.fHeight)); 167 dim.fHeight = SkTMax(1, SkScalarRoundToInt(desiredScale * dim.fHeight));
152 return dim; 168 return dim;
153 } 169 }
154 170
155 bool SkWebpCodec::onDimensionsSupported(const SkISize& dim) { 171 bool SkWebpCodec::onDimensionsSupported(const SkISize& dim) {
156 const SkImageInfo& info = this->getInfo(); 172 const SkImageInfo& info = this->getInfo();
157 return dim.width() >= 1 && dim.width() <= info.width() 173 return dim.width() >= 1 && dim.width() <= info.width()
158 && dim.height() >= 1 && dim.height() <= info.height(); 174 && dim.height() >= 1 && dim.height() <= info.height();
159 } 175 }
160 176
161
162 static WEBP_CSP_MODE webp_decode_mode(SkColorType ct, bool premultiply) { 177 static WEBP_CSP_MODE webp_decode_mode(SkColorType ct, bool premultiply) {
163 switch (ct) { 178 switch (ct) {
164 case kBGRA_8888_SkColorType: 179 case kBGRA_8888_SkColorType:
165 return premultiply ? MODE_bgrA : MODE_BGRA; 180 return premultiply ? MODE_bgrA : MODE_BGRA;
166 case kRGBA_8888_SkColorType: 181 case kRGBA_8888_SkColorType:
167 return premultiply ? MODE_rgbA : MODE_RGBA; 182 return premultiply ? MODE_rgbA : MODE_RGBA;
168 case kRGB_565_SkColorType: 183 case kRGB_565_SkColorType:
169 return MODE_RGB_565; 184 return MODE_RGB_565;
170 default: 185 default:
171 return MODE_LAST; 186 return MODE_LAST;
172 } 187 }
173 } 188 }
174 189
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 { 190 bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const {
181 if (!desiredSubset) { 191 if (!desiredSubset) {
182 return false; 192 return false;
183 } 193 }
184 194
185 SkIRect dimensions = SkIRect::MakeSize(this->getInfo().dimensions()); 195 SkIRect dimensions = SkIRect::MakeSize(this->getInfo().dimensions());
186 if (!dimensions.contains(*desiredSubset)) { 196 if (!dimensions.contains(*desiredSubset)) {
187 return false; 197 return false;
188 } 198 }
189 199
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 config.options.scaled_height = dstDimensions.height(); 266 config.options.scaled_height = dstDimensions.height();
257 } 267 }
258 268
259 config.output.colorspace = webp_decode_mode(dstInfo.colorType(), 269 config.output.colorspace = webp_decode_mode(dstInfo.colorType(),
260 dstInfo.alphaType() == kPremul_SkAlphaType); 270 dstInfo.alphaType() == kPremul_SkAlphaType);
261 config.output.u.RGBA.rgba = (uint8_t*) dst; 271 config.output.u.RGBA.rgba = (uint8_t*) dst;
262 config.output.u.RGBA.stride = (int) rowBytes; 272 config.output.u.RGBA.stride = (int) rowBytes;
263 config.output.u.RGBA.size = dstInfo.getSafeSize(rowBytes); 273 config.output.u.RGBA.size = dstInfo.getSafeSize(rowBytes);
264 config.output.is_external_memory = 1; 274 config.output.is_external_memory = 1;
265 275
276 WebPIterator frame;
277 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame);
278 // If this succeeded in NewFromStream(), it should succeed again here.
279 SkAssertResult(WebPDemuxGetFrame(fDemux, 1, &frame));
280
266 SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &co nfig)); 281 SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &co nfig));
267 if (!idec) { 282 if (!idec) {
268 return kInvalidInput; 283 return kInvalidInput;
269 } 284 }
270 285
271 SkAutoTMalloc<uint8_t> storage(BUFFER_SIZE); 286 switch (WebPIUpdate(idec, frame.fragment.bytes, frame.fragment.size)) {
msarett 2016/09/06 23:47:11 It's kind of ugly to create the WebPIDecoder with
scroggo 2016/09/07 14:20:51 It's the same as we did before though, right?
msarett 2016/09/07 16:47:10 Yes, true. Before it seemed to make a little more
272 uint8_t* buffer = storage.get(); 287 case VP8_STATUS_OK:
273 while (true) { 288 return kSuccess;
274 const size_t bytesRead = stream()->read(buffer, BUFFER_SIZE); 289 case VP8_STATUS_SUSPENDED:
275 if (0 == bytesRead) { 290 WebPIDecGetRGB(idec, rowsDecoded, nullptr, nullptr, nullptr);
276 WebPIDecGetRGB(idec, rowsDecoded, NULL, NULL, NULL);
277 return kIncompleteInput; 291 return kIncompleteInput;
278 } 292 default:
279 293 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 } 294 }
290 } 295 }
291 296
292 SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info, 297 SkWebpCodec::SkWebpCodec(int width, int height, const SkEncodedInfo& info,
293 sk_sp<SkColorSpace> colorSpace, SkStream* stream) 298 sk_sp<SkColorSpace> colorSpace, SkStream* stream, WebPD emuxer* demux,
294 : INHERITED(width, height, info, stream, colorSpace) 299 sk_sp<SkData> data)
300 : INHERITED(width, height, info, stream, std::move(colorSpace))
301 , fDemux(demux)
302 , fData(std::move(data))
295 {} 303 {}
OLDNEW
« no previous file with comments | « src/codec/SkWebpCodec.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698