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

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

Issue 1997703003: Make SkPngCodec decode progressively. (Closed) Base URL: https://skia.googlesource.com/skia.git@foil
Patch Set: Rebase 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 | « include/codec/SkCodec.h ('k') | src/codec/SkIcoCodec.h » ('j') | 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 "SkBmpCodec.h" 8 #include "SkBmpCodec.h"
9 #include "SkCodec.h" 9 #include "SkCodec.h"
10 #include "SkCodecPriv.h" 10 #include "SkCodecPriv.h"
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 // Store the value of fNeedsRewind so we can update it. Next read will 151 // Store the value of fNeedsRewind so we can update it. Next read will
152 // require a rewind. 152 // require a rewind.
153 const bool needsRewind = fNeedsRewind; 153 const bool needsRewind = fNeedsRewind;
154 fNeedsRewind = true; 154 fNeedsRewind = true;
155 if (!needsRewind) { 155 if (!needsRewind) {
156 return true; 156 return true;
157 } 157 }
158 158
159 // startScanlineDecode will need to be called before decoding scanlines. 159 // startScanlineDecode will need to be called before decoding scanlines.
160 fCurrScanline = -1; 160 fCurrScanline = -1;
161 // startIncrementalDecode will need to be called before incrementalDecode.
162 fStartedIncrementalDecode = false;
161 163
162 if (!fStream->rewind()) { 164 if (!fStream->rewind()) {
163 return false; 165 return false;
164 } 166 }
165 167
166 return this->onRewind(); 168 return this->onRewind();
167 } 169 }
168 170
171 #define CHECK_COLOR_TABLE \
172 if (kIndex_8_SkColorType == info.colorType()) { \
173 if (nullptr == ctable || nullptr == ctableCount) { \
174 return SkCodec::kInvalidParameters; \
175 } \
176 } else { \
177 if (ctableCount) { \
178 *ctableCount = 0; \
179 } \
180 ctableCount = nullptr; \
181 ctable = nullptr; \
182 }
183
184
169 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, 185 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
170 const Options* options, SkPMColor ctable[], i nt* ctableCount) { 186 const Options* options, SkPMColor ctable[], i nt* ctableCount) {
171 if (kUnknown_SkColorType == info.colorType()) { 187 if (kUnknown_SkColorType == info.colorType()) {
172 return kInvalidConversion; 188 return kInvalidConversion;
173 } 189 }
174 if (nullptr == pixels) { 190 if (nullptr == pixels) {
175 return kInvalidParameters; 191 return kInvalidParameters;
176 } 192 }
177 if (rowBytes < info.minRowBytes()) { 193 if (rowBytes < info.minRowBytes()) {
178 return kInvalidParameters; 194 return kInvalidParameters;
179 } 195 }
180 196
181 if (kIndex_8_SkColorType == info.colorType()) { 197 CHECK_COLOR_TABLE;
182 if (nullptr == ctable || nullptr == ctableCount) {
183 return kInvalidParameters;
184 }
185 } else {
186 if (ctableCount) {
187 *ctableCount = 0;
188 }
189 ctableCount = nullptr;
190 ctable = nullptr;
191 }
192 198
193 if (!this->rewindIfNeeded()) { 199 if (!this->rewindIfNeeded()) {
194 return kCouldNotRewind; 200 return kCouldNotRewind;
195 } 201 }
196 202
197 // Default options. 203 // Default options.
198 Options optsStorage; 204 Options optsStorage;
199 if (nullptr == options) { 205 if (nullptr == options) {
200 options = &optsStorage; 206 options = &optsStorage;
201 } else if (options->fSubset) { 207 } else if (options->fSubset) {
202 SkIRect subset(*options->fSubset); 208 SkIRect subset(*options->fSubset);
203 if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) { 209 if (!this->onGetValidSubset(&subset) || subset != *options->fSubset) {
204 // FIXME: How to differentiate between not supporting subset at all 210 // FIXME: How to differentiate between not supporting subset at all
205 // and not supporting this particular subset? 211 // and not supporting this particular subset?
206 return kUnimplemented; 212 return kUnimplemented;
207 } 213 }
208 } 214 }
209 215
210 // FIXME: Support subsets somehow? Note that this works for SkWebpCodec 216 // FIXME: Support subsets somehow? Note that this works for SkWebpCodec
211 // because it supports arbitrary scaling/subset combinations. 217 // because it supports arbitrary scaling/subset combinations.
212 if (!this->dimensionsSupported(info.dimensions())) { 218 if (!this->dimensionsSupported(info.dimensions())) {
213 return kInvalidScale; 219 return kInvalidScale;
214 } 220 }
215 221
222 fDstInfo = info;
223 // FIXME: fOptions should be updated to options here, since fillIncompleteIm age (called below
224 // in this method) accesses it. Without updating, it uses the old value.
225 //fOptions = *options;
226
216 // On an incomplete decode, the subclass will specify the number of scanline s that it decoded 227 // On an incomplete decode, the subclass will specify the number of scanline s that it decoded
217 // successfully. 228 // successfully.
218 int rowsDecoded = 0; 229 int rowsDecoded = 0;
219 const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ct able, ctableCount, 230 const Result result = this->onGetPixels(info, pixels, rowBytes, *options, ct able, ctableCount,
220 &rowsDecoded); 231 &rowsDecoded);
221 232
222 if ((kIncompleteInput == result || kSuccess == result) && ctableCount) { 233 if ((kIncompleteInput == result || kSuccess == result) && ctableCount) {
223 SkASSERT(*ctableCount >= 0 && *ctableCount <= 256); 234 SkASSERT(*ctableCount >= 0 && *ctableCount <= 256);
224 } 235 }
225 236
226 // A return value of kIncompleteInput indicates a truncated image stream. 237 // A return value of kIncompleteInput indicates a truncated image stream.
227 // In this case, we will fill any uninitialized memory with a default value. 238 // In this case, we will fill any uninitialized memory with a default value.
228 // Some subclasses will take care of filling any uninitialized memory on 239 // Some subclasses will take care of filling any uninitialized memory on
229 // their own. They indicate that all of the memory has been filled by 240 // their own. They indicate that all of the memory has been filled by
230 // setting rowsDecoded equal to the height. 241 // setting rowsDecoded equal to the height.
231 if (kIncompleteInput == result && rowsDecoded != info.height()) { 242 if (kIncompleteInput == result && rowsDecoded != info.height()) {
232 this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitiali zed, info.height(), 243 this->fillIncompleteImage(info, pixels, rowBytes, options->fZeroInitiali zed, info.height(),
233 rowsDecoded); 244 rowsDecoded);
234 } 245 }
235 246
236 return result; 247 return result;
237 } 248 }
238 249
239 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) { 250 SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) {
240 return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr); 251 return this->getPixels(info, pixels, rowBytes, nullptr, nullptr, nullptr);
241 } 252 }
242 253
243 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo, 254 SkCodec::Result SkCodec::startIncrementalDecode(const SkImageInfo& info, void* p ixels,
255 size_t rowBytes, const SkCodec::Options* options, SkPMColor* ctable, int * ctableCount) {
256 fStartedIncrementalDecode = false;
257
258 if (kUnknown_SkColorType == info.colorType()) {
259 return kInvalidConversion;
260 }
261 if (nullptr == pixels) {
262 return kInvalidParameters;
263 }
264
265 // Ensure that valid color ptrs are passed in for kIndex8 color type
266 CHECK_COLOR_TABLE;
267
268 // FIXME: If the rows come after the rows of a previous incremental decode,
269 // we might be able to skip the rewind, but only the implementation knows
270 // that. (e.g. PNG will always need to rewind, since we called longjmp, but
271 // a bottom-up BMP could skip rewinding if the new rows are above the old
272 // rows.)
273 if (!this->rewindIfNeeded()) {
274 return kCouldNotRewind;
275 }
276
277 // Set options.
278 Options optsStorage;
279 if (nullptr == options) {
280 options = &optsStorage;
281 } else if (options->fSubset) {
282 SkIRect size = SkIRect::MakeSize(info.dimensions());
283 if (!size.contains(*options->fSubset)) {
284 return kInvalidParameters;
285 }
286
287 const int top = options->fSubset->top();
288 const int bottom = options->fSubset->bottom();
289 if (top < 0 || top >= info.height() || top >= bottom || bottom > info.he ight()) {
290 return kInvalidParameters;
291 }
292 }
293
294 if (!this->dimensionsSupported(info.dimensions())) {
295 return kInvalidScale;
296 }
297
298 fDstInfo = info;
299 fOptions = *options;
300
301 const Result result = this->onStartIncrementalDecode(info, pixels, rowBytes,
302 fOptions, ctable, ctableCount);
303 if (kSuccess == result) {
304 fStartedIncrementalDecode = true;
305 } else if (kUnimplemented == result) {
306 // FIXME: This is temporarily necessary, until we transition SkCodec
307 // implementations from scanline decoding to incremental decoding.
308 // SkAndroidCodec will first attempt to use incremental decoding, but
309 // will fall back to scanline decoding if incremental returns
310 // kUnimplemented. rewindIfNeeded(), above, set fNeedsRewind to true
311 // (after potentially rewinding), but we do not want the next call to
312 // startScanlineDecode() to do a rewind.
313 fNeedsRewind = false;
314 }
315 return result;
316 }
317
318
319 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info,
244 const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) { 320 const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
245 // Reset fCurrScanline in case of failure. 321 // Reset fCurrScanline in case of failure.
246 fCurrScanline = -1; 322 fCurrScanline = -1;
247 // Ensure that valid color ptrs are passed in for kIndex8 color type 323 // Ensure that valid color ptrs are passed in for kIndex8 color type
248 if (kIndex_8_SkColorType == dstInfo.colorType()) { 324 CHECK_COLOR_TABLE;
249 if (nullptr == ctable || nullptr == ctableCount) {
250 return SkCodec::kInvalidParameters;
251 }
252 } else {
253 if (ctableCount) {
254 *ctableCount = 0;
255 }
256 ctableCount = nullptr;
257 ctable = nullptr;
258 }
259 325
260 if (!this->rewindIfNeeded()) { 326 if (!this->rewindIfNeeded()) {
261 return kCouldNotRewind; 327 return kCouldNotRewind;
262 } 328 }
263 329
264 // Set options. 330 // Set options.
265 Options optsStorage; 331 Options optsStorage;
266 if (nullptr == options) { 332 if (nullptr == options) {
267 options = &optsStorage; 333 options = &optsStorage;
268 } else if (options->fSubset) { 334 } else if (options->fSubset) {
269 SkIRect size = SkIRect::MakeSize(dstInfo.dimensions()); 335 SkIRect size = SkIRect::MakeSize(info.dimensions());
270 if (!size.contains(*options->fSubset)) { 336 if (!size.contains(*options->fSubset)) {
271 return kInvalidInput; 337 return kInvalidInput;
272 } 338 }
273 339
274 // We only support subsetting in the x-dimension for scanline decoder. 340 // We only support subsetting in the x-dimension for scanline decoder.
275 // Subsetting in the y-dimension can be accomplished using skipScanlines (). 341 // Subsetting in the y-dimension can be accomplished using skipScanlines ().
276 if (options->fSubset->top() != 0 || options->fSubset->height() != dstInf o.height()) { 342 if (options->fSubset->top() != 0 || options->fSubset->height() != info.h eight()) {
277 return kInvalidInput; 343 return kInvalidInput;
278 } 344 }
279 } 345 }
280 346
281 // FIXME: Support subsets somehow? 347 // FIXME: Support subsets somehow?
282 if (!this->dimensionsSupported(dstInfo.dimensions())) { 348 if (!this->dimensionsSupported(info.dimensions())) {
283 return kInvalidScale; 349 return kInvalidScale;
284 } 350 }
285 351
286 const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount); 352 const Result result = this->onStartScanlineDecode(info, *options, ctable, ct ableCount);
287 if (result != SkCodec::kSuccess) { 353 if (result != SkCodec::kSuccess) {
288 return result; 354 return result;
289 } 355 }
290 356
291 fCurrScanline = 0; 357 fCurrScanline = 0;
292 fDstInfo = dstInfo; 358 fDstInfo = info;
293 fOptions = *options; 359 fOptions = *options;
294 return kSuccess; 360 return kSuccess;
295 } 361 }
296 362
297 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo) { 363 #undef CHECK_COLOR_TABLE
298 return this->startScanlineDecode(dstInfo, nullptr, nullptr, nullptr); 364
365 SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& info) {
366 return this->startScanlineDecode(info, nullptr, nullptr, nullptr);
299 } 367 }
300 368
301 int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) { 369 int SkCodec::getScanlines(void* dst, int countLines, size_t rowBytes) {
302 if (fCurrScanline < 0) { 370 if (fCurrScanline < 0) {
303 return 0; 371 return 0;
304 } 372 }
305 373
306 SkASSERT(!fDstInfo.isEmpty()); 374 SkASSERT(!fDstInfo.isEmpty());
307 if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) { 375 if (countLines <= 0 || fCurrScanline + countLines > fDstInfo.height()) {
308 return 0; 376 return 0;
(...skipping 27 matching lines...) Expand all
336 } 404 }
337 405
338 int SkCodec::outputScanline(int inputScanline) const { 406 int SkCodec::outputScanline(int inputScanline) const {
339 SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height()); 407 SkASSERT(0 <= inputScanline && inputScanline < this->getInfo().height());
340 return this->onOutputScanline(inputScanline); 408 return this->onOutputScanline(inputScanline);
341 } 409 }
342 410
343 int SkCodec::onOutputScanline(int inputScanline) const { 411 int SkCodec::onOutputScanline(int inputScanline) const {
344 switch (this->getScanlineOrder()) { 412 switch (this->getScanlineOrder()) {
345 case kTopDown_SkScanlineOrder: 413 case kTopDown_SkScanlineOrder:
346 case kNone_SkScanlineOrder:
347 return inputScanline; 414 return inputScanline;
348 case kBottomUp_SkScanlineOrder: 415 case kBottomUp_SkScanlineOrder:
349 return this->getInfo().height() - inputScanline - 1; 416 return this->getInfo().height() - inputScanline - 1;
350 default: 417 default:
351 // This case indicates an interlaced gif and is implemented by SkGif Codec. 418 // This case indicates an interlaced gif and is implemented by SkGif Codec.
352 SkASSERT(false); 419 SkASSERT(false);
353 return 0; 420 return 0;
354 } 421 }
355 } 422 }
356 423
(...skipping 29 matching lines...) Expand all
386 const uint64_t fillValue = this->getFillValue(info); 453 const uint64_t fillValue = this->getFillValue(info);
387 const int linesRemaining = linesRequested - linesDecoded; 454 const int linesRemaining = linesRequested - linesDecoded;
388 SkSampler* sampler = this->getSampler(false); 455 SkSampler* sampler = this->getSampler(false);
389 456
390 int fillWidth = info.width(); 457 int fillWidth = info.width();
391 if (fOptions.fSubset) { 458 if (fOptions.fSubset) {
392 fillWidth = fOptions.fSubset->width(); 459 fillWidth = fOptions.fSubset->width();
393 } 460 }
394 461
395 switch (this->getScanlineOrder()) { 462 switch (this->getScanlineOrder()) {
396 case kTopDown_SkScanlineOrder: 463 case kTopDown_SkScanlineOrder: {
397 case kNone_SkScanlineOrder: {
398 const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining); 464 const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining);
399 fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes); 465 fillDst = SkTAddOffset<void>(dst, linesDecoded * rowBytes);
400 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler) ; 466 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler) ;
401 break; 467 break;
402 } 468 }
403 case kBottomUp_SkScanlineOrder: { 469 case kBottomUp_SkScanlineOrder: {
404 fillDst = dst; 470 fillDst = dst;
405 const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining); 471 const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining);
406 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler) ; 472 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler) ;
407 break; 473 break;
408 } 474 }
409 case kOutOfOrder_SkScanlineOrder: { 475 case kOutOfOrder_SkScanlineOrder: {
410 SkASSERT(1 == linesRequested || this->getInfo().height() == linesReq uested); 476 SkASSERT(1 == linesRequested || this->getInfo().height() == linesReq uested);
411 const SkImageInfo fillInfo = info.makeWH(fillWidth, 1); 477 const SkImageInfo fillInfo = info.makeWH(fillWidth, 1);
412 for (int srcY = linesDecoded; srcY < linesRequested; srcY++) { 478 for (int srcY = linesDecoded; srcY < linesRequested; srcY++) {
413 fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * r owBytes); 479 fillDst = SkTAddOffset<void>(dst, this->outputScanline(srcY) * r owBytes);
414 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, samp ler); 480 fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, samp ler);
415 } 481 }
416 break; 482 break;
417 } 483 }
418 } 484 }
419 } 485 }
OLDNEW
« no previous file with comments | « include/codec/SkCodec.h ('k') | src/codec/SkIcoCodec.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698