SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, size_t rowBytes, const Options& options, SkPMColor ctable[], int* ctableCount, int* rowsDecoded) { if (options.fSubset) { // Subsets are not supported. return kUnimplemented; } if (fCodec->dimensionsSupported(requestedInfo.dimensions())) { // Make sure that the parent class does not fill on an incomplete decode, since // fCodec will take care of filling the uninitialized lines. *rowsDecoded = requestedInfo.height(); return fCodec->getPixels(requestedInfo, dst, rowBytes, &options, ctable, ctableCount); } // scaling requested int sampleX; int sampleY; if (!scaling_supported(requestedInfo.dimensions(), fCodec->getInfo().dimensions(), &sampleX, &sampleY)) { // onDimensionsSupported would have returned false, meaning we should never reach here. SkASSERT(false); return kInvalidScale; } // set first sample pixel in y direction const int Y0 = get_start_coord(sampleY); const int dstHeight = requestedInfo.height(); const int srcWidth = fCodec->getInfo().width(); const int srcHeight = fCodec->getInfo().height(); const SkImageInfo info = requestedInfo.makeWH(srcWidth, srcHeight); Result result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount); if (kSuccess != result) { return result; } SkSampler* sampler = fCodec->getSampler(true); if (!sampler) { return kUnimplemented; } if (sampler->setSampleX(sampleX) != requestedInfo.width()) { return kInvalidScale; } switch(fCodec->getScanlineOrder()) { case SkCodec::kTopDown_SkScanlineOrder: { if (!fCodec->skipScanlines(Y0)) { *rowsDecoded = 0; return kIncompleteInput; } for (int y = 0; y < dstHeight; y++) { if (1 != fCodec->getScanlines(dst, 1, rowBytes)) { // The failed call to getScanlines() will take care of // filling the failed row, so we indicate that we have // decoded (y + 1) rows. *rowsDecoded = y + 1; return kIncompleteInput; } if (y < dstHeight - 1) { if (!fCodec->skipScanlines(sampleY - 1)) { *rowsDecoded = y + 1; return kIncompleteInput; } } dst = SkTAddOffset<void>(dst, rowBytes); } return kSuccess; } case SkCodec::kBottomUp_SkScanlineOrder: case SkCodec::kOutOfOrder_SkScanlineOrder: { Result result = kSuccess; int y; for (y = 0; y < srcHeight; y++) { int srcY = fCodec->nextScanline(); if (is_coord_necessary(srcY, sampleY, dstHeight)) { void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY)); if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { result = kIncompleteInput; break; } } else { if (!fCodec->skipScanlines(1)) { result = kIncompleteInput; break; } } } // We handle filling uninitialized memory here instead of in the parent class. // The parent class does not know that we are sampling. if (kIncompleteInput == result) { const uint32_t fillValue = fCodec->getFillValue(requestedInfo.colorType(), requestedInfo.alphaType()); for (; y < srcHeight; y++) { int srcY = fCodec->outputScanline(y); if (is_coord_necessary(srcY, sampleY, dstHeight)) { void* dstRow = SkTAddOffset<void>(dst, rowBytes * get_dst_coord(srcY, sampleY)); SkSampler::Fill(requestedInfo.makeWH(requestedInfo.width(), 1), dstRow, rowBytes, fillValue, options.fZeroInitialized); } } *rowsDecoded = dstHeight; } return result; } case SkCodec::kNone_SkScanlineOrder: { SkAutoMalloc storage(srcHeight * rowBytes); uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); int scanlines = fCodec->getScanlines(storagePtr, srcHeight, rowBytes); storagePtr += Y0 * rowBytes; scanlines -= Y0; int y = 0; while (y < dstHeight && scanlines > 0) { memcpy(dst, storagePtr, rowBytes); storagePtr += sampleY * rowBytes; dst = SkTAddOffset<void>(dst, rowBytes); scanlines -= sampleY; y++; } if (y < dstHeight) { // fCodec has already handled filling uninitialized memory. *rowsDecoded = dstHeight; return kIncompleteInput; } return kSuccess; } default: SkASSERT(false); return kUnimplemented; } }
SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels, size_t rowBytes, const AndroidOptions& options) { // We should only call this function when sampling. SkASSERT(options.fSampleSize > 1); // Create options struct for the codec. SkCodec::Options sampledOptions; sampledOptions.fZeroInitialized = options.fZeroInitialized; sampledOptions.fPremulBehavior = SkTransferFunctionBehavior::kIgnore; // FIXME: This was already called by onGetAndroidPixels. Can we reduce that? int sampleSize = options.fSampleSize; int nativeSampleSize; SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize); // Check if there is a subset. SkIRect subset; int subsetY = 0; int subsetWidth = nativeSize.width(); int subsetHeight = nativeSize.height(); if (options.fSubset) { // We will need to know about subsetting in the y-dimension in order to use the // scanline decoder. // Update the subset to account for scaling done by this->codec(). const SkIRect* subsetPtr = options.fSubset; // Do the divide ourselves, instead of calling get_scaled_dimension. If // X and Y are 0, they should remain 0, rather than being upgraded to 1 // due to being smaller than the sampleSize. const int subsetX = subsetPtr->x() / nativeSampleSize; subsetY = subsetPtr->y() / nativeSampleSize; subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize); subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize); // The scanline decoder only needs to be aware of subsetting in the x-dimension. subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height()); sampledOptions.fSubset = ⊂ } // Since we guarantee that output dimensions are always at least one (even if the sampleSize // is greater than a given dimension), the input sampleSize is not always the sampleSize that // we use in practice. const int sampleX = subsetWidth / info.width(); const int sampleY = subsetHeight / info.height(); const int samplingOffsetY = get_start_coord(sampleY); const int startY = samplingOffsetY + subsetY; const int dstHeight = info.height(); const SkImageInfo nativeInfo = info.makeWH(nativeSize.width(), nativeSize.height()); { // Although startScanlineDecode expects the bottom and top to match the // SkImageInfo, startIncrementalDecode uses them to determine which rows to // decode. SkCodec::Options incrementalOptions = sampledOptions; SkIRect incrementalSubset; if (sampledOptions.fSubset) { incrementalSubset.fTop = subsetY; incrementalSubset.fBottom = subsetY + subsetHeight; incrementalSubset.fLeft = sampledOptions.fSubset->fLeft; incrementalSubset.fRight = sampledOptions.fSubset->fRight; incrementalOptions.fSubset = &incrementalSubset; } const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo, pixels, rowBytes, &incrementalOptions); if (SkCodec::kSuccess == startResult) { SkSampler* sampler = this->codec()->getSampler(true); if (!sampler) { return SkCodec::kUnimplemented; } if (sampler->setSampleX(sampleX) != info.width()) { return SkCodec::kInvalidScale; } if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { return SkCodec::kInvalidScale; } sampler->setSampleY(sampleY); int rowsDecoded; const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded); if (incResult == SkCodec::kSuccess) { return SkCodec::kSuccess; } SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput); SkASSERT(rowsDecoded <= info.height()); this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, info.height(), rowsDecoded); return incResult; } else if (startResult != SkCodec::kUnimplemented) { return startResult; } // kUnimplemented means use the old method. } // Start the scanline decode. SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo, &sampledOptions); if (SkCodec::kSuccess != result) { return result; } SkSampler* sampler = this->codec()->getSampler(true); if (!sampler) { return SkCodec::kUnimplemented; } if (sampler->setSampleX(sampleX) != info.width()) { return SkCodec::kInvalidScale; } if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { return SkCodec::kInvalidScale; } switch(this->codec()->getScanlineOrder()) { case SkCodec::kTopDown_SkScanlineOrder: { if (!this->codec()->skipScanlines(startY)) { this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, 0); return SkCodec::kIncompleteInput; } void* pixelPtr = pixels; for (int y = 0; y < dstHeight; y++) { if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, y + 1); return SkCodec::kIncompleteInput; } if (y < dstHeight - 1) { if (!this->codec()->skipScanlines(sampleY - 1)) { this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, y + 1); return SkCodec::kIncompleteInput; } } pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); } return SkCodec::kSuccess; } case SkCodec::kBottomUp_SkScanlineOrder: { // Note that these modes do not support subsetting. SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight); int y; for (y = 0; y < nativeSize.height(); y++) { int srcY = this->codec()->nextScanline(); if (is_coord_necessary(srcY, sampleY, dstHeight)) { void* pixelPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY)); if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { break; } } else { if (!this->codec()->skipScanlines(1)) { break; } } } if (nativeSize.height() == y) { return SkCodec::kSuccess; } // We handle filling uninitialized memory here instead of using this->codec(). // this->codec() does not know that we are sampling. const uint64_t fillValue = this->codec()->getFillValue(info); const SkImageInfo fillInfo = info.makeWH(info.width(), 1); for (; y < nativeSize.height(); y++) { int srcY = this->codec()->outputScanline(y); if (!is_coord_necessary(srcY, sampleY, dstHeight)) { continue; } void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY)); SkSampler::Fill(fillInfo, rowPtr, rowBytes, fillValue, options.fZeroInitialized); } return SkCodec::kIncompleteInput; } default: SkASSERT(false); return SkCodec::kUnimplemented; } }
SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels, size_t rowBytes, const AndroidOptions& options) { // We should only call this function when sampling. SkASSERT(options.fSampleSize > 1); // Create options struct for the codec. SkCodec::Options sampledOptions; sampledOptions.fZeroInitialized = options.fZeroInitialized; // FIXME: This was already called by onGetAndroidPixels. Can we reduce that? int sampleSize = options.fSampleSize; int nativeSampleSize; SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize); // Check if there is a subset. SkIRect subset; int subsetY = 0; int subsetWidth = nativeSize.width(); int subsetHeight = nativeSize.height(); if (options.fSubset) { // We will need to know about subsetting in the y-dimension in order to use the // scanline decoder. // Update the subset to account for scaling done by this->codec(). SkIRect* subsetPtr = options.fSubset; // Do the divide ourselves, instead of calling get_scaled_dimension. If // X and Y are 0, they should remain 0, rather than being upgraded to 1 // due to being smaller than the sampleSize. const int subsetX = subsetPtr->x() / nativeSampleSize; subsetY = subsetPtr->y() / nativeSampleSize; subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize); subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize); // The scanline decoder only needs to be aware of subsetting in the x-dimension. subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height()); sampledOptions.fSubset = ⊂ } // Start the scanline decode. SkCodec::Result result = this->codec()->startScanlineDecode( info.makeWH(nativeSize.width(), nativeSize.height()), &sampledOptions, options.fColorPtr, options.fColorCount); if (SkCodec::kSuccess != result) { return result; } SkSampler* sampler = this->codec()->getSampler(true); if (!sampler) { return SkCodec::kUnimplemented; } // Since we guarantee that output dimensions are always at least one (even if the sampleSize // is greater than a given dimension), the input sampleSize is not always the sampleSize that // we use in practice. const int sampleX = subsetWidth / info.width(); const int sampleY = subsetHeight / info.height(); if (sampler->setSampleX(sampleX) != info.width()) { return SkCodec::kInvalidScale; } if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { return SkCodec::kInvalidScale; } const int samplingOffsetY = get_start_coord(sampleY); const int startY = samplingOffsetY + subsetY; int dstHeight = info.height(); switch(this->codec()->getScanlineOrder()) { case SkCodec::kTopDown_SkScanlineOrder: { if (!this->codec()->skipScanlines(startY)) { this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, 0); return SkCodec::kIncompleteInput; } void* pixelPtr = pixels; for (int y = 0; y < dstHeight; y++) { if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, y + 1); return SkCodec::kIncompleteInput; } if (y < dstHeight - 1) { if (!this->codec()->skipScanlines(sampleY - 1)) { this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, y + 1); return SkCodec::kIncompleteInput; } } pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); } return SkCodec::kSuccess; } case SkCodec::kOutOfOrder_SkScanlineOrder: case SkCodec::kBottomUp_SkScanlineOrder: { // Note that these modes do not support subsetting. SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight); int y; for (y = 0; y < nativeSize.height(); y++) { int srcY = this->codec()->nextScanline(); if (is_coord_necessary(srcY, sampleY, dstHeight)) { void* pixelPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY)); if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { break; } } else { if (!this->codec()->skipScanlines(1)) { break; } } } if (nativeSize.height() == y) { return SkCodec::kSuccess; } // We handle filling uninitialized memory here instead of using this->codec(). // this->codec() does not know that we are sampling. const uint32_t fillValue = this->codec()->getFillValue(info.colorType()); const SkImageInfo fillInfo = info.makeWH(info.width(), 1); for (; y < nativeSize.height(); y++) { int srcY = this->codec()->outputScanline(y); if (!is_coord_necessary(srcY, sampleY, dstHeight)) { continue; } void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY)); SkSampler::Fill(fillInfo, rowPtr, rowBytes, fillValue, options.fZeroInitialized); } return SkCodec::kIncompleteInput; } case SkCodec::kNone_SkScanlineOrder: { const int linesNeeded = subsetHeight - samplingOffsetY; SkAutoTMalloc<uint8_t> storage(linesNeeded * rowBytes); uint8_t* storagePtr = storage.get(); if (!this->codec()->skipScanlines(startY)) { this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized, dstHeight, 0); return SkCodec::kIncompleteInput; } int scanlines = this->codec()->getScanlines(storagePtr, linesNeeded, rowBytes); for (int y = 0; y < dstHeight; y++) { memcpy(pixels, storagePtr, info.minRowBytes()); storagePtr += sampleY * rowBytes; pixels = SkTAddOffset<void>(pixels, rowBytes); } if (scanlines < dstHeight) { // this->codec() has already handled filling uninitialized memory. return SkCodec::kIncompleteInput; } return SkCodec::kSuccess; } default: SkASSERT(false); return SkCodec::kUnimplemented; } }