| OLD | NEW |
| 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 "SkCodec.h" |
| 8 #include "SkCodecPriv.h" | 9 #include "SkCodecPriv.h" |
| 9 #include "SkScaledCodec.h" | 10 #include "SkSampledCodec.h" |
| 10 #include "SkStream.h" | 11 |
| 11 #include "SkWebpCodec.h" | 12 // FIXME: Rename this file to SkSampledCodec.cpp |
| 12 | 13 |
| 13 | 14 SkSampledCodec::SkSampledCodec(SkCodec* codec) |
| 14 SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { | 15 : INHERITED(codec->getInfo()) |
| 15 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream)); | |
| 16 if (nullptr == codec) { | |
| 17 return nullptr; | |
| 18 } | |
| 19 | |
| 20 switch (codec->getEncodedFormat()) { | |
| 21 case kWEBP_SkEncodedFormat: | |
| 22 // Webp codec supports scaling and subsetting natively | |
| 23 return codec.detach(); | |
| 24 case kPNG_SkEncodedFormat: | |
| 25 case kJPEG_SkEncodedFormat: | |
| 26 // wrap in new SkScaledCodec | |
| 27 return new SkScaledCodec(codec.detach()); | |
| 28 default: | |
| 29 // FIXME: SkScaledCodec is temporarily disabled for other formats | |
| 30 // while focusing on the formats that are supported by | |
| 31 // BitmapRegionDecoder. | |
| 32 return nullptr; | |
| 33 } | |
| 34 } | |
| 35 | |
| 36 SkCodec* SkScaledCodec::NewFromData(SkData* data) { | |
| 37 if (!data) { | |
| 38 return nullptr; | |
| 39 } | |
| 40 return NewFromStream(new SkMemoryStream(data)); | |
| 41 } | |
| 42 | |
| 43 SkScaledCodec::SkScaledCodec(SkCodec* codec) | |
| 44 : INHERITED(codec->getInfo(), nullptr) | |
| 45 , fCodec(codec) | 16 , fCodec(codec) |
| 46 {} | 17 {} |
| 47 | 18 |
| 48 SkScaledCodec::~SkScaledCodec() {} | 19 SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const { |
| 49 | 20 // Fast path for when we are not scaling. |
| 50 bool SkScaledCodec::onRewind() { | 21 if (1 == sampleSize) { |
| 51 return fCodec->onRewind(); | 22 return fCodec->getInfo().dimensions(); |
| 52 } | 23 } |
| 53 | 24 |
| 54 static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& na
tiveDims, | 25 const int width = fCodec->getInfo().width(); |
| 55 const SkISize& scaledCodecDims, float desi
redScale) { | 26 const int height = fCodec->getInfo().height(); |
| 56 if (nativeDims == scaledCodecDims) { | 27 |
| 57 // does not matter which to return if equal. Return here to skip below c
alculations | 28 // Check if the codec can provide the scaling natively. |
| 58 return nativeDims; | 29 float scale = get_scale_from_sample_size(sampleSize); |
| 59 } | 30 SkSize idealSize = SkSize::Make(scale * (float) width, scale * (float) heigh
t); |
| 60 float idealWidth = origDims.width() * desiredScale; | 31 SkISize nativeSize = fCodec->getScaledDimensions(scale); |
| 61 float idealHeight = origDims.height() * desiredScale; | 32 float widthDiff = SkTAbs(((float) nativeSize.width()) - idealSize.width()); |
| 62 | 33 float heightDiff = SkTAbs(((float) nativeSize.height()) - idealSize.height()
); |
| 63 // calculate difference between native dimensions and ideal dimensions | |
| 64 float nativeWDiff = SkTAbs(idealWidth - nativeDims.width()); | |
| 65 float nativeHDiff = SkTAbs(idealHeight - nativeDims.height()); | |
| 66 float nativeDiff = nativeWDiff + nativeHDiff; | |
| 67 | |
| 68 // Native scaling is preferred to sampling. If we can scale natively to | 34 // Native scaling is preferred to sampling. If we can scale natively to |
| 69 // within one of the ideal value, we should choose to scale natively. | 35 // within one of the ideal value, we should choose to scale natively. |
| 70 if (nativeWDiff < 1.0f && nativeHDiff < 1.0f) { | 36 if (widthDiff < 1.0f && heightDiff < 1.0f) { |
| 71 return nativeDims; | 37 return nativeSize; |
| 72 } | 38 } |
| 73 | 39 |
| 74 // calculate difference between scaledCodec dimensions and ideal dimensions | 40 // Provide the scaling by sampling. |
| 75 float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width()); | 41 return SkISize::Make(get_scaled_dimension(width, sampleSize), |
| 76 float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height()); | 42 get_scaled_dimension(height, sampleSize)); |
| 77 float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff; | |
| 78 | |
| 79 // return dimensions closest to ideal dimensions. | |
| 80 // If the differences are equal, return nativeDims, as native scaling is mor
e efficient. | |
| 81 return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims; | |
| 82 } | 43 } |
| 83 | 44 |
| 84 /* | 45 SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void
* pixels, |
| 85 * Return a valid set of output dimensions for this decoder, given an input scal
e | 46 size_t rowBytes, AndroidOptions& options) { |
| 86 */ | 47 // Create an Options struct for the codec. |
| 87 SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const { | 48 SkCodec::Options codecOptions; |
| 88 SkISize nativeDimensions = fCodec->getScaledDimensions(desiredScale); | 49 codecOptions.fZeroInitialized = options.fZeroInitialized; |
| 89 // support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ... | 50 |
| 90 SkISize scaledCodecDimensions; | 51 SkIRect* subset = options.fSubset; |
| 91 if (desiredScale > 0.5f) { | 52 if (!subset || subset->size() == fCodec->getInfo().dimensions()) { |
| 92 // sampleSize = 1 | 53 if (fCodec->dimensionsSupported(info.dimensions())) { |
| 93 scaledCodecDimensions = fCodec->getInfo().dimensions(); | 54 return fCodec->getPixels(info, pixels, rowBytes, &codecOptions, opti
ons.fColorPtr, |
| 94 } | 55 options.fColorCount); |
| 95 // sampleSize determines the step size between samples | 56 } |
| 96 // Ex: sampleSize = 2, sample every second pixel in x and y directions | 57 |
| 97 int sampleSize = int ((1.0f / desiredScale) + 0.5f); | 58 // If the native codec does not support the requested scale, scale by sa
mpling. |
| 98 | 59 return this->sampledDecode(info, pixels, rowBytes, options); |
| 99 int scaledWidth = get_scaled_dimension(this->getInfo().width(), sampleSize); | 60 } |
| 100 int scaledHeight = get_scaled_dimension(this->getInfo().height(), sampleSize
); | 61 |
| 101 | 62 // We are performing a subset decode. |
| 102 // Return the calculated output dimensions for the given scale | 63 int sampleSize = options.fSampleSize; |
| 103 scaledCodecDimensions = SkISize::Make(scaledWidth, scaledHeight); | 64 SkISize scaledSize = this->onGetSampledDimensions(sampleSize); |
| 104 | 65 if (!fCodec->dimensionsSupported(scaledSize)) { |
| 105 return best_scaled_dimensions(this->getInfo().dimensions(), nativeDimensions
, | 66 // If the native codec does not support the requested scale, scale by sa
mpling. |
| 106 scaledCodecDimensions, desiredScale); | 67 return this->sampledDecode(info, pixels, rowBytes, options); |
| 68 } |
| 69 |
| 70 // Calculate the scaled subset bounds. |
| 71 int scaledSubsetX = subset->x() / sampleSize; |
| 72 int scaledSubsetY = subset->y() / sampleSize; |
| 73 int scaledSubsetWidth = info.width(); |
| 74 int scaledSubsetHeight = info.height(); |
| 75 |
| 76 // Start the scanline decode. |
| 77 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWid
th, |
| 78 scaledSize.height()); |
| 79 codecOptions.fSubset = &scanlineSubset; |
| 80 SkCodec::Result result = fCodec->startScanlineDecode(info.makeWH(scaledSize.
width(), |
| 81 scaledSize.height()), &codecOptions, options.fColorPtr, options.fCol
orCount); |
| 82 if (SkCodec::kSuccess != result) { |
| 83 return result; |
| 84 } |
| 85 |
| 86 // At this point, we are only concerned with subsetting. Either no scale wa
s |
| 87 // requested, or the fCodec is handling the scale. |
| 88 switch (fCodec->getScanlineOrder()) { |
| 89 case SkCodec::kTopDown_SkScanlineOrder: |
| 90 case SkCodec::kNone_SkScanlineOrder: { |
| 91 if (!fCodec->skipScanlines(scaledSubsetY)) { |
| 92 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer
oInitialized, |
| 93 scaledSubsetHeight, 0); |
| 94 return SkCodec::kIncompleteInput; |
| 95 } |
| 96 |
| 97 int decodedLines = fCodec->getScanlines(pixels, scaledSubsetHeight,
rowBytes); |
| 98 if (decodedLines != scaledSubsetHeight) { |
| 99 return SkCodec::kIncompleteInput; |
| 100 } |
| 101 return SkCodec::kSuccess; |
| 102 } |
| 103 default: |
| 104 SkASSERT(false); |
| 105 return SkCodec::kUnimplemented; |
| 106 } |
| 107 } | 107 } |
| 108 | 108 |
| 109 // check if scaling to dstInfo size from srcInfo size using sampleSize is possib
le | 109 |
| 110 static bool scaling_supported(const SkISize& dstDim, const SkISize& srcDim, | 110 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix
els, |
| 111 int* sampleX, int* sampleY) { | 111 size_t rowBytes, AndroidOptions& options) { |
| 112 SkScaledCodec::ComputeSampleSize(dstDim, srcDim, sampleX, sampleY); | 112 // Create options struct for the codec. |
| 113 const int dstWidth = dstDim.width(); | 113 SkCodec::Options sampledOptions; |
| 114 const int dstHeight = dstDim.height(); | 114 sampledOptions.fZeroInitialized = options.fZeroInitialized; |
| 115 const int srcWidth = srcDim.width(); | 115 |
| 116 const int srcHeight = srcDim.height(); | 116 // Check if there is a subset. |
| 117 // only support down sampling, not up sampling | 117 SkIRect subset; |
| 118 if (dstWidth > srcWidth || dstHeight > srcHeight) { | 118 int subsetY = 0; |
| 119 return false; | 119 int subsetWidth = fCodec->getInfo().width(); |
| 120 } | 120 int subsetHeight = fCodec->getInfo().height(); |
| 121 // check that srcWidth is scaled down by an integer value | |
| 122 if (get_scaled_dimension(srcWidth, *sampleX) != dstWidth) { | |
| 123 return false; | |
| 124 } | |
| 125 // check that src height is scaled down by an integer value | |
| 126 if (get_scaled_dimension(srcHeight, *sampleY) != dstHeight) { | |
| 127 return false; | |
| 128 } | |
| 129 // sampleX and sampleY should be equal unless the original sampleSize reques
ted was larger | |
| 130 // than srcWidth or srcHeight. If so, the result of this is dstWidth or dstH
eight = 1. | |
| 131 // This functionality allows for tall thin images to still be scaled down by
scaling factors. | |
| 132 if (*sampleX != *sampleY){ | |
| 133 if (1 != dstWidth && 1 != dstHeight) { | |
| 134 return false; | |
| 135 } | |
| 136 } | |
| 137 return true; | |
| 138 } | |
| 139 | |
| 140 bool SkScaledCodec::onDimensionsSupported(const SkISize& dim) { | |
| 141 // Check with fCodec first. No need to call the non-virtual version, which | |
| 142 // just checks if it matches the original, since a match means this method | |
| 143 // will not be called. | |
| 144 if (fCodec->onDimensionsSupported(dim)) { | |
| 145 return true; | |
| 146 } | |
| 147 | |
| 148 // FIXME: These variables are unused, but are needed by scaling_supported. | |
| 149 // This class could also cache these values, and avoid calling this in | |
| 150 // onGetPixels (since getPixels already calls it). | |
| 151 int sampleX; | |
| 152 int sampleY; | |
| 153 return scaling_supported(dim, this->getInfo().dimensions(), &sampleX, &sampl
eY); | |
| 154 } | |
| 155 | |
| 156 // calculates sampleSize in x and y direction | |
| 157 void SkScaledCodec::ComputeSampleSize(const SkISize& dstDim, const SkISize& srcD
im, | |
| 158 int* sampleXPtr, int* sampleYPtr) { | |
| 159 int srcWidth = srcDim.width(); | |
| 160 int dstWidth = dstDim.width(); | |
| 161 int srcHeight = srcDim.height(); | |
| 162 int dstHeight = dstDim.height(); | |
| 163 | |
| 164 int sampleX = srcWidth / dstWidth; | |
| 165 int sampleY = srcHeight / dstHeight; | |
| 166 | |
| 167 // only support down sampling, not up sampling | |
| 168 SkASSERT(dstWidth <= srcWidth); | |
| 169 SkASSERT(dstHeight <= srcHeight); | |
| 170 | |
| 171 // sampleX and sampleY should be equal unless the original sampleSize reques
ted was | |
| 172 // larger than srcWidth or srcHeight. | |
| 173 // If so, the result of this is dstWidth or dstHeight = 1. This functionalit
y | |
| 174 // allows for tall thin images to still be scaled down by scaling factors. | |
| 175 | |
| 176 if (sampleX != sampleY){ | |
| 177 if (1 != dstWidth && 1 != dstHeight) { | |
| 178 | |
| 179 // rounding during onGetScaledDimensions can cause different sampleS
izes | |
| 180 // Ex: srcWidth = 79, srcHeight = 20, sampleSize = 10 | |
| 181 // dstWidth = 7, dstHeight = 2, sampleX = 79/7 = 11, sampleY = 20/2
= 10 | |
| 182 // correct for this rounding by comparing width to sampleY and heigh
t to sampleX | |
| 183 | |
| 184 if (get_scaled_dimension(srcWidth, sampleY) == dstWidth) { | |
| 185 sampleX = sampleY; | |
| 186 } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) { | |
| 187 sampleY = sampleX; | |
| 188 } | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 if (sampleXPtr) { | |
| 193 *sampleXPtr = sampleX; | |
| 194 } | |
| 195 if (sampleYPtr) { | |
| 196 *sampleYPtr = sampleY; | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 // TODO: Implement subsetting in onGetPixels which works when and when not sampl
ing | |
| 201 | |
| 202 SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi
d* dst, | |
| 203 size_t rowBytes, const Options& optio
ns, | |
| 204 SkPMColor ctable[], int* ctableCount, | |
| 205 int* rowsDecoded) { | |
| 206 | |
| 207 if (options.fSubset) { | 121 if (options.fSubset) { |
| 208 // Subsets are not supported. | 122 // We will need to know about subsetting in the y-dimension in order to
use the |
| 209 return kUnimplemented; | 123 // scanline decoder. |
| 210 } | 124 SkIRect* subsetPtr = options.fSubset; |
| 211 | 125 subsetY = subsetPtr->y(); |
| 212 if (fCodec->dimensionsSupported(requestedInfo.dimensions())) { | 126 subsetWidth = subsetPtr->width(); |
| 213 // Make sure that the parent class does not fill on an incomplete decode
, since | 127 subsetHeight = subsetPtr->height(); |
| 214 // fCodec will take care of filling the uninitialized lines. | 128 |
| 215 *rowsDecoded = requestedInfo.height(); | 129 // The scanline decoder only needs to be aware of subsetting in the x-di
mension. |
| 216 return fCodec->getPixels(requestedInfo, dst, rowBytes, &options, ctable,
ctableCount); | 130 subset.setXYWH(subsetPtr->x(), 0, subsetWidth, fCodec->getInfo().height(
)); |
| 217 } | 131 sampledOptions.fSubset = ⊂ |
| 218 | 132 } |
| 219 // scaling requested | 133 |
| 220 int sampleX; | 134 // Start the scanline decode. |
| 221 int sampleY; | 135 SkCodec::Result result = fCodec->startScanlineDecode( |
| 222 if (!scaling_supported(requestedInfo.dimensions(), fCodec->getInfo().dimensi
ons(), | 136 info.makeWH(fCodec->getInfo().width(), fCodec->getInfo().height()),
&sampledOptions, |
| 223 &sampleX, &sampleY)) { | 137 options.fColorPtr, options.fColorCount); |
| 224 // onDimensionsSupported would have returned false, meaning we should ne
ver reach here. | 138 if (SkCodec::kSuccess != result) { |
| 225 SkASSERT(false); | |
| 226 return kInvalidScale; | |
| 227 } | |
| 228 | |
| 229 // set first sample pixel in y direction | |
| 230 const int Y0 = get_start_coord(sampleY); | |
| 231 | |
| 232 const int dstHeight = requestedInfo.height(); | |
| 233 const int srcWidth = fCodec->getInfo().width(); | |
| 234 const int srcHeight = fCodec->getInfo().height(); | |
| 235 | |
| 236 const SkImageInfo info = requestedInfo.makeWH(srcWidth, srcHeight); | |
| 237 | |
| 238 Result result = fCodec->startScanlineDecode(info, &options, ctable, ctableCo
unt); | |
| 239 | |
| 240 if (kSuccess != result) { | |
| 241 return result; | 139 return result; |
| 242 } | 140 } |
| 243 | 141 |
| 244 SkSampler* sampler = fCodec->getSampler(true); | 142 SkSampler* sampler = fCodec->getSampler(true); |
| 245 if (!sampler) { | 143 if (!sampler) { |
| 246 return kUnimplemented; | 144 return SkCodec::kUnimplemented; |
| 247 } | 145 } |
| 248 | 146 |
| 249 if (sampler->setSampleX(sampleX) != requestedInfo.width()) { | 147 // Since we guarantee that output dimensions are always at least one (even i
f the sampleSize |
| 250 return kInvalidScale; | 148 // is greater than a given dimension), the input sampleSize is not always th
e sampleSize that |
| 251 } | 149 // we use in practice. |
| 252 | 150 const int sampleX = subsetWidth / info.width(); |
| 151 const int sampleY = subsetHeight / info.height(); |
| 152 if (sampler->setSampleX(sampleX) != info.width()) { |
| 153 return SkCodec::kInvalidScale; |
| 154 } |
| 155 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { |
| 156 return SkCodec::kInvalidScale; |
| 157 } |
| 158 |
| 159 const int samplingOffsetY = get_start_coord(sampleY); |
| 160 const int startY = samplingOffsetY + subsetY; |
| 161 int dstHeight = info.height(); |
| 253 switch(fCodec->getScanlineOrder()) { | 162 switch(fCodec->getScanlineOrder()) { |
| 254 case SkCodec::kTopDown_SkScanlineOrder: { | 163 case SkCodec::kTopDown_SkScanlineOrder: { |
| 255 if (!fCodec->skipScanlines(Y0)) { | 164 if (!fCodec->skipScanlines(startY)) { |
| 256 *rowsDecoded = 0; | 165 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer
oInitialized, |
| 257 return kIncompleteInput; | 166 dstHeight, 0); |
| 258 } | 167 return SkCodec::kIncompleteInput; |
| 168 } |
| 169 void* pixelPtr = pixels; |
| 259 for (int y = 0; y < dstHeight; y++) { | 170 for (int y = 0; y < dstHeight; y++) { |
| 260 if (1 != fCodec->getScanlines(dst, 1, rowBytes)) { | 171 if (1 != fCodec->getScanlines(pixelPtr, 1, rowBytes)) { |
| 261 // The failed call to getScanlines() will take care of | 172 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.
fZeroInitialized, |
| 262 // filling the failed row, so we indicate that we have | 173 dstHeight, y + 1); |
| 263 // decoded (y + 1) rows. | 174 return SkCodec::kIncompleteInput; |
| 264 *rowsDecoded = y + 1; | |
| 265 return kIncompleteInput; | |
| 266 } | 175 } |
| 267 if (y < dstHeight - 1) { | 176 int linesToSkip = SkTMin(sampleY - 1, dstHeight - y - 1); |
| 268 if (!fCodec->skipScanlines(sampleY - 1)) { | 177 if (!fCodec->skipScanlines(linesToSkip)) { |
| 269 *rowsDecoded = y + 1; | 178 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.
fZeroInitialized, |
| 270 return kIncompleteInput; | 179 dstHeight, y + 1); |
| 271 } | 180 return SkCodec::kIncompleteInput; |
| 272 } | 181 } |
| 273 dst = SkTAddOffset<void>(dst, rowBytes); | 182 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes); |
| 274 } | 183 } |
| 275 return kSuccess; | 184 return SkCodec::kSuccess; |
| 276 } | |
| 277 case SkCodec::kBottomUp_SkScanlineOrder: | |
| 278 case SkCodec::kOutOfOrder_SkScanlineOrder: { | |
| 279 Result result = kSuccess; | |
| 280 int y; | |
| 281 for (y = 0; y < srcHeight; y++) { | |
| 282 int srcY = fCodec->nextScanline(); | |
| 283 if (is_coord_necessary(srcY, sampleY, dstHeight)) { | |
| 284 void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_co
ord(srcY, sampleY)); | |
| 285 if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { | |
| 286 result = kIncompleteInput; | |
| 287 break; | |
| 288 } | |
| 289 } else { | |
| 290 if (!fCodec->skipScanlines(1)) { | |
| 291 result = kIncompleteInput; | |
| 292 break; | |
| 293 } | |
| 294 } | |
| 295 } | |
| 296 | |
| 297 // We handle filling uninitialized memory here instead of in the par
ent class. | |
| 298 // The parent class does not know that we are sampling. | |
| 299 if (kIncompleteInput == result) { | |
| 300 const uint32_t fillValue = fCodec->getFillValue(requestedInfo.co
lorType(), | |
| 301 requestedInfo.alphaType()); | |
| 302 for (; y < srcHeight; y++) { | |
| 303 int srcY = fCodec->outputScanline(y); | |
| 304 if (is_coord_necessary(srcY, sampleY, dstHeight)) { | |
| 305 void* dstRow = SkTAddOffset<void>(dst, | |
| 306 rowBytes * get_dst_coord(srcY, sampleY)); | |
| 307 SkSampler::Fill(requestedInfo.makeWH(requestedInfo.width
(), 1), dstRow, | |
| 308 rowBytes, fillValue, options.fZeroInitialized); | |
| 309 } | |
| 310 } | |
| 311 *rowsDecoded = dstHeight; | |
| 312 } | |
| 313 return result; | |
| 314 } | 185 } |
| 315 case SkCodec::kNone_SkScanlineOrder: { | 186 case SkCodec::kNone_SkScanlineOrder: { |
| 316 SkAutoMalloc storage(srcHeight * rowBytes); | 187 const int linesNeeded = subsetHeight - samplingOffsetY; |
| 188 SkAutoMalloc storage(linesNeeded * rowBytes); |
| 317 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); | 189 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); |
| 318 int scanlines = fCodec->getScanlines(storagePtr, srcHeight, rowBytes
); | 190 |
| 319 storagePtr += Y0 * rowBytes; | 191 if (!fCodec->skipScanlines(startY)) { |
| 320 scanlines -= Y0; | 192 fCodec->fillIncompleteImage(info, pixels, rowBytes, options.fZer
oInitialized, |
| 321 int y = 0; | 193 dstHeight, 0); |
| 322 while (y < dstHeight && scanlines > 0) { | 194 return SkCodec::kIncompleteInput; |
| 323 memcpy(dst, storagePtr, rowBytes); | 195 } |
| 196 int scanlines = fCodec->getScanlines(storagePtr, linesNeeded, rowByt
es); |
| 197 |
| 198 for (int y = 0; y < dstHeight; y++) { |
| 199 memcpy(pixels, storagePtr, info.minRowBytes()); |
| 324 storagePtr += sampleY * rowBytes; | 200 storagePtr += sampleY * rowBytes; |
| 325 dst = SkTAddOffset<void>(dst, rowBytes); | 201 pixels = SkTAddOffset<void>(pixels, rowBytes); |
| 326 scanlines -= sampleY; | 202 } |
| 327 y++; | 203 |
| 328 } | 204 if (scanlines < dstHeight) { |
| 329 if (y < dstHeight) { | |
| 330 // fCodec has already handled filling uninitialized memory. | 205 // fCodec has already handled filling uninitialized memory. |
| 331 *rowsDecoded = dstHeight; | 206 return SkCodec::kIncompleteInput; |
| 332 return kIncompleteInput; | 207 } |
| 333 } | 208 return SkCodec::kSuccess; |
| 334 return kSuccess; | |
| 335 } | 209 } |
| 336 default: | 210 default: |
| 337 SkASSERT(false); | 211 SkASSERT(false); |
| 338 return kUnimplemented; | 212 return SkCodec::kUnimplemented; |
| 339 } | 213 } |
| 340 } | 214 } |
| 341 | |
| 342 uint32_t SkScaledCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaT
ype) const { | |
| 343 return fCodec->onGetFillValue(colorType, alphaType); | |
| 344 } | |
| 345 | |
| 346 SkCodec::SkScanlineOrder SkScaledCodec::onGetScanlineOrder() const { | |
| 347 return fCodec->onGetScanlineOrder(); | |
| 348 } | |
| OLD | NEW |