Chromium Code Reviews| 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 "SkCodecPriv.h" | 8 #include "SkCodecPriv.h" |
| 9 #include "SkScaledCodec.h" | 9 #include "SkScaledCodec.h" |
| 10 #include "SkStream.h" | 10 #include "SkStream.h" |
| 11 #include "SkWebpCodec.h" | 11 #include "SkWebpCodec.h" |
| 12 | 12 |
| 13 | 13 |
| 14 SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { | 14 SkCodec* SkScaledCodec::NewFromStream(SkStream* stream) { |
| 15 bool isWebp = SkWebpCodec::IsWebp(stream); | 15 bool isWebp = SkWebpCodec::IsWebp(stream); |
| 16 if (!stream->rewind()) { | 16 if (!stream->rewind()) { |
| 17 return nullptr; | 17 return nullptr; |
| 18 } | 18 } |
| 19 if (isWebp) { | 19 if (isWebp) { |
| 20 // Webp codec supports scaling and subsetting natively | 20 // Webp codec supports scaling and subsetting natively |
| 21 return SkWebpCodec::NewFromStream(stream); | 21 return SkWebpCodec::NewFromStream(stream); |
| 22 } | 22 } |
| 23 | 23 |
| 24 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream)); | 24 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream)); |
| 25 if (nullptr == codec) { | 25 if (nullptr == codec) { |
| 26 return nullptr; | 26 return nullptr; |
| 27 } | 27 } |
| 28 | 28 |
| 29 // wrap in new SkScaledCodec | 29 // wrap in new SkScaledCodec |
| 30 return new SkScaledCodec(codec.detach()); | 30 return new SkScaledCodec(codec.detach()); |
| 31 } | 31 } |
| 32 | 32 |
| 33 SkCodec* SkScaledCodec::NewFromData(SkData* data) { | 33 SkCodec* SkScaledCodec::NewFromData(SkData* data) { |
| 34 if (!data) { | 34 if (!data) { |
| 35 return nullptr; | 35 return nullptr; |
| 36 } | 36 } |
| 37 return NewFromStream(new SkMemoryStream(data)); | 37 return NewFromStream(new SkMemoryStream(data)); |
| 38 } | 38 } |
| 39 | 39 |
| 40 SkScaledCodec::SkScaledCodec(SkCodec* codec) | 40 SkScaledCodec::SkScaledCodec(SkCodec* codec) |
| 41 : INHERITED(codec->getInfo(), nullptr) | 41 : INHERITED(codec->getInfo(), nullptr) |
| 42 , fCodec(codec) | 42 , fCodec(codec) |
| 43 {} | 43 {} |
| 44 | 44 |
| 45 SkScaledCodec::~SkScaledCodec() {} | 45 SkScaledCodec::~SkScaledCodec() {} |
| 46 | 46 |
| 47 static SkISize best_scaled_dimensions(const SkISize& origDims, const SkISize& na tiveDims, | 47 static bool is_in_subset(int coord, int offset, int length) { |
|
scroggo
2015/10/02 18:27:03
I thought we had something like this function, but
| |
| 48 const SkISize& scaledCodecDims, float desi redScale) { | 48 if (coord < offset || coord >= offset + length) { |
|
scroggo
2015/10/02 18:27:03
Why not:
return coord >= offset && coord < offset
| |
| 49 if (nativeDims == scaledCodecDims) { | 49 return false; |
| 50 // does not matter which to return if equal. Return here to skip below c alculations | |
| 51 return nativeDims; | |
| 52 } | 50 } |
| 51 return true; | |
| 52 } | |
| 53 | |
| 54 static int get_sample_size(float scale) { | |
|
scroggo
2015/10/02 18:27:03
Don't you define this somewhere else?
| |
| 55 return SkScalarRoundToInt(1.0f / scale); | |
| 56 } | |
| 57 | |
| 58 static bool use_native_scaling(const SkISize& origDims, const SkISize& nativeDim s, | |
| 59 const SkISize& sampledDims, float desiredScale) { | |
| 60 if (nativeDims == sampledDims) { | |
| 61 // If both options are the same, choose native scaling. | |
| 62 return true; | |
| 63 } | |
| 64 | |
| 53 float idealWidth = origDims.width() * desiredScale; | 65 float idealWidth = origDims.width() * desiredScale; |
| 54 float idealHeight = origDims.height() * desiredScale; | 66 float idealHeight = origDims.height() * desiredScale; |
| 55 | 67 |
| 56 // calculate difference between native dimensions and ideal dimensions | 68 // Calculate difference between native scaling and ideal scaling. |
| 57 float nativeWDiff = SkTAbs(idealWidth - nativeDims.width()); | 69 float nativeWDiff = SkTAbs(idealWidth - nativeDims.width()); |
| 58 float nativeHDiff = SkTAbs(idealHeight - nativeDims.height()); | 70 float nativeHDiff = SkTAbs(idealHeight - nativeDims.height()); |
| 59 float nativeDiff = nativeWDiff + nativeHDiff; | 71 float nativeDiff = nativeWDiff + nativeHDiff; |
| 60 | 72 |
| 61 // Native scaling is preferred to sampling. If we can scale natively to | 73 // Native scaling is preferred to sampling. If we can scale natively to |
| 62 // within one of the ideal value, we should choose to scale natively. | 74 // within one of the ideal value, we should choose to scale natively. |
| 63 if (nativeWDiff < 1.0f && nativeHDiff < 1.0f) { | 75 if (nativeWDiff < 1.0f && nativeHDiff < 1.0f) { |
| 64 return nativeDims; | 76 return true; |
| 65 } | 77 } |
| 66 | 78 |
| 67 // calculate difference between scaledCodec dimensions and ideal dimensions | 79 // Calculate difference between native scaling and sampled scaling. |
| 68 float scaledCodecWDiff = SkTAbs(idealWidth - scaledCodecDims.width()); | 80 float sampledWDiff = SkTAbs(idealWidth - sampledDims.width()); |
| 69 float scaledCodecHDiff = SkTAbs(idealHeight - scaledCodecDims.height()); | 81 float sampledHDiff = SkTAbs(idealHeight - sampledDims.height()); |
| 70 float scaledCodecDiff = scaledCodecWDiff + scaledCodecHDiff; | 82 float sampledDiff = sampledWDiff + sampledHDiff; |
| 71 | 83 |
| 72 // return dimensions closest to ideal dimensions. | 84 // Use native scaling if it is closer to the ideal scale. |
| 73 // If the differences are equal, return nativeDims, as native scaling is mor e efficient. | 85 return nativeDiff <= sampledDiff; |
| 74 return nativeDiff > scaledCodecDiff ? scaledCodecDims : nativeDims; | |
| 75 } | 86 } |
| 76 | 87 |
| 77 /* | 88 /* |
| 78 * Return a valid set of output dimensions for this decoder, given an input scal e | 89 * Return a valid set of output dimensions for this decoder, given an input scal e |
| 79 */ | 90 */ |
| 80 SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const { | 91 SkISize SkScaledCodec::onGetScaledDimensions(float desiredScale) const { |
| 81 SkISize nativeDimensions = fCodec->getScaledDimensions(desiredScale); | 92 SkISize nativeDims = fCodec->getScaledDimensions(desiredScale); |
| 82 // support scaling down by integer numbers. Ex: 1/2, 1/3, 1/4 ... | 93 int sampleSize = get_sample_size(desiredScale); |
| 83 SkISize scaledCodecDimensions; | 94 SkISize sampledDims = SkISize::Make( |
| 84 if (desiredScale > 0.5f) { | 95 get_scaled_dimension(this->getInfo().width(), sampleSize), |
| 85 // sampleSize = 1 | 96 get_scaled_dimension(this->getInfo().height(), sampleSize)); |
| 86 scaledCodecDimensions = fCodec->getInfo().dimensions(); | 97 |
| 98 if (use_native_scaling(this->getInfo().dimensions(), nativeDims, sampledDims , desiredScale)) { | |
| 99 return nativeDims; | |
| 87 } | 100 } |
| 88 // sampleSize determines the step size between samples | |
| 89 // Ex: sampleSize = 2, sample every second pixel in x and y directions | |
| 90 int sampleSize = int ((1.0f / desiredScale) + 0.5f); | |
| 91 | 101 |
| 92 int scaledWidth = get_scaled_dimension(this->getInfo().width(), sampleSize); | 102 return sampledDims; |
| 93 int scaledHeight = get_scaled_dimension(this->getInfo().height(), sampleSize ); | 103 } |
| 94 | 104 |
| 95 // Return the calculated output dimensions for the given scale | 105 bool SkScaledCodec::onGetScaledSubsetDimensions(float desiredScale, Options* opt ions) const { |
| 96 scaledCodecDimensions = SkISize::Make(scaledWidth, scaledHeight); | |
| 97 | 106 |
| 98 return best_scaled_dimensions(this->getInfo().dimensions(), nativeDimensions , | 107 SkISize nativeDims = fCodec->getScaledDimensions(desiredScale); |
| 99 scaledCodecDimensions, desiredScale); | 108 int sampleSize = get_sample_size(desiredScale); |
| 109 SkISize sampledDims = SkISize::Make( | |
| 110 get_scaled_dimension(this->getInfo().width(), sampleSize), | |
| 111 get_scaled_dimension(this->getInfo().height(), sampleSize)); | |
| 112 | |
| 113 if (use_native_scaling(this->getInfo().dimensions(), nativeDims, sampledDims , desiredScale)) { | |
| 114 // Set the scaled dimensions and calculate subset size using native scal ing. | |
| 115 options->fScaledDimensions = nativeDims; | |
| 116 float widthScale = ((float) nativeDims.width()) / ((float) this->getInfo ().width()); | |
| 117 float heightScale = ((float) nativeDims.height()) / ((float) this->getIn fo().height()); | |
| 118 // Notice that we may round the size of the subset up to 1. This means that we must | |
| 119 // floor the left and top offsets to ensure that we do not suggest a sub set that is | |
| 120 // off the edge of the image. | |
| 121 options->fScaledSubset = SkIRect::MakeXYWH( | |
| 122 int (((float) options->fSubset->left()) * widthScale), | |
| 123 int (((float) options->fSubset->top()) * heightScale), | |
| 124 SkTMax(1, SkScalarRoundToInt(((float) options->fSubset->width()) * widthScale)), | |
| 125 SkTMax(1, SkScalarRoundToInt(((float) options->fSubset->height() ) * heightScale))); | |
| 126 return true; | |
| 127 } | |
| 128 | |
| 129 // Set the scaled dimensions and calculate subset size using sampling. | |
| 130 options->fScaledDimensions = sampledDims; | |
| 131 options->fScaledSubset = SkIRect::MakeXYWH( | |
| 132 options->fSubset->left() / sampleSize, | |
| 133 options->fSubset->top() / sampleSize, | |
| 134 get_scaled_dimension(options->fSubset->width(), sampleSize), | |
| 135 get_scaled_dimension(options->fSubset->height(), sampleSize)); | |
| 136 return true; | |
| 100 } | 137 } |
| 101 | 138 |
| 102 // check if scaling to dstInfo size from srcInfo size using sampleSize is possib le | 139 // check if scaling to dstInfo size from srcInfo size using sampleSize is possib le |
| 103 static bool scaling_supported(const SkImageInfo& dstInfo, const SkImageInfo& src Info, | 140 static bool scaling_supported(const SkISize& dstSize, const SkISize& srcSize, |
| 104 int* sampleX, int* sampleY) { | 141 int* sampleX, int* sampleY) { |
| 105 SkScaledCodec::ComputeSampleSize(dstInfo, srcInfo, sampleX, sampleY); | 142 SkScaledCodec::ComputeSampleSize(dstSize, srcSize, sampleX, sampleY); |
| 106 const int dstWidth = dstInfo.width(); | 143 const int dstWidth = dstSize.width(); |
| 107 const int dstHeight = dstInfo.height(); | 144 const int dstHeight = dstSize.height(); |
| 108 const int srcWidth = srcInfo.width(); | 145 const int srcWidth = srcSize.width(); |
| 109 const int srcHeight = srcInfo.height(); | 146 const int srcHeight = srcSize.height(); |
| 110 // only support down sampling, not up sampling | 147 // only support down sampling, not up sampling |
| 111 if (dstWidth > srcWidth || dstHeight > srcHeight) { | 148 if (dstWidth > srcWidth || dstHeight > srcHeight) { |
| 112 return false; | 149 return false; |
| 113 } | 150 } |
| 114 // check that srcWidth is scaled down by an integer value | 151 // check that srcWidth is scaled down by an integer value |
| 115 if (get_scaled_dimension(srcWidth, *sampleX) != dstWidth) { | 152 if (get_scaled_dimension(srcWidth, *sampleX) != dstWidth) { |
| 116 return false; | 153 return false; |
| 117 } | 154 } |
| 118 // check that src height is scaled down by an integer value | 155 // check that src height is scaled down by an integer value |
| 119 if (get_scaled_dimension(srcHeight, *sampleY) != dstHeight) { | 156 if (get_scaled_dimension(srcHeight, *sampleY) != dstHeight) { |
| 120 return false; | 157 return false; |
| 121 } | 158 } |
| 122 // sampleX and sampleY should be equal unless the original sampleSize reques ted was larger | 159 // sampleX and sampleY should be equal unless the original sampleSize reques ted was larger |
| 123 // than srcWidth or srcHeight. If so, the result of this is dstWidth or dstH eight = 1. | 160 // than srcWidth or srcHeight. If so, the result of this is dstWidth or dstH eight = 1. |
| 124 // This functionality allows for tall thin images to still be scaled down by scaling factors. | 161 // This functionality allows for tall thin images to still be scaled down by scaling factors. |
| 125 if (*sampleX != *sampleY){ | 162 if (*sampleX != *sampleY){ |
| 126 if (1 != dstWidth && 1 != dstHeight) { | 163 if (1 != dstWidth && 1 != dstHeight) { |
| 127 return false; | 164 return false; |
| 128 } | 165 } |
| 129 } | 166 } |
| 130 return true; | 167 return true; |
| 131 } | 168 } |
| 132 | 169 |
| 133 // calculates sampleSize in x and y direction | 170 // calculates sampleSize in x and y direction |
| 134 void SkScaledCodec::ComputeSampleSize(const SkImageInfo& dstInfo, const SkImageI nfo& srcInfo, | 171 void SkScaledCodec::ComputeSampleSize(const SkISize& dstSize, const SkISize& src Size, |
| 135 int* sampleXPtr, int* sampleYPtr) { | 172 int* sampleXPtr, int* sampleYPtr) { |
| 136 int srcWidth = srcInfo.width(); | 173 int srcWidth = srcSize.width(); |
| 137 int dstWidth = dstInfo.width(); | 174 int dstWidth = dstSize.width(); |
| 138 int srcHeight = srcInfo.height(); | 175 int srcHeight = srcSize.height(); |
| 139 int dstHeight = dstInfo.height(); | 176 int dstHeight = dstSize.height(); |
| 140 | 177 |
| 141 int sampleX = srcWidth / dstWidth; | 178 int sampleX = srcWidth / dstWidth; |
| 142 int sampleY = srcHeight / dstHeight; | 179 int sampleY = srcHeight / dstHeight; |
| 143 | 180 |
| 144 // only support down sampling, not up sampling | 181 // only support down sampling, not up sampling |
| 145 SkASSERT(dstWidth <= srcWidth); | 182 SkASSERT(dstWidth <= srcWidth); |
| 146 SkASSERT(dstHeight <= srcHeight); | 183 SkASSERT(dstHeight <= srcHeight); |
| 147 | 184 |
| 148 // sampleX and sampleY should be equal unless the original sampleSize reques ted was | 185 // sampleX and sampleY should be equal unless the original sampleSize reques ted was |
| 149 // larger than srcWidth or srcHeight. | 186 // larger than srcWidth or srcHeight. |
| 150 // If so, the result of this is dstWidth or dstHeight = 1. This functionalit y | 187 // If so, the result of this is dstWidth or dstHeight = 1. This functionalit y |
| 151 // allows for tall thin images to still be scaled down by scaling factors. | 188 // allows for tall thin images to still be scaled down by scaling factors. |
| 152 | 189 |
| 153 if (sampleX != sampleY){ | 190 if (sampleX != sampleY){ |
| 154 if (1 != dstWidth && 1 != dstHeight) { | 191 if (1 != dstWidth && 1 != dstHeight) { |
| 155 | 192 |
| 156 // rounding during onGetScaledDimensions can cause different sampleS izes | 193 // rounding during onGetScaledDimensions can cause different sampleS izes |
| 157 // Ex: srcWidth = 79, srcHeight = 20, sampleSize = 10 | 194 // Ex: srcWidth = 79, srcHeight = 20, sampleSize = 10 |
| 158 // dstWidth = 7, dstHeight = 2, sampleX = 79/7 = 11, sampleY = 20/2 = 10 | 195 // dstWidth = 7, dstHeight = 2, sampleX = 79/7 = 11, sampleY = 20/2 = 10 |
| 159 // correct for this rounding by comparing width to sampleY and heigh t to sampleX | 196 // correct for this rounding by comparing width to sampleY and heigh t to sampleX |
| 160 | 197 |
| 161 if (get_scaled_dimension(srcWidth, sampleY) == dstWidth) { | 198 if (get_scaled_dimension(srcWidth, sampleY) == dstWidth) { |
| 162 sampleX = sampleY; | 199 sampleX = sampleY; |
| 163 } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) { | 200 } else if (get_scaled_dimension(srcHeight, sampleX) == dstHeight) { |
| 164 sampleY = sampleX; | 201 sampleY = sampleX; |
| 165 } | 202 } |
| 203 // FIXME (msarett): Should this never be reached? | |
| 166 } | 204 } |
| 167 } | 205 } |
| 168 | 206 |
| 169 if (sampleXPtr) { | 207 if (sampleXPtr) { |
| 170 *sampleXPtr = sampleX; | 208 *sampleXPtr = sampleX; |
| 171 } | 209 } |
| 172 if (sampleYPtr) { | 210 if (sampleYPtr) { |
| 173 *sampleYPtr = sampleY; | 211 *sampleYPtr = sampleY; |
| 174 } | 212 } |
| 175 } | 213 } |
| 176 | 214 |
| 177 // TODO: Implement subsetting in onGetPixels which works when and when not sampl ing | 215 SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& scaledSubsetInfo, void* dst, |
| 216 size_t rowBytes, const Options& options, SkPMColor ctable[], int* ctable Count, | |
| 217 int* rowsDecoded) { | |
| 178 | 218 |
| 179 SkCodec::Result SkScaledCodec::onGetPixels(const SkImageInfo& requestedInfo, voi d* dst, | 219 // There are various values of Rect, Size, and Info used in this function. I think it is |
| 180 size_t rowBytes, const Options& optio ns, | 220 // useful to go ahead and define what they mean. |
| 181 SkPMColor ctable[], int* ctableCount, | 221 // orig_: Refers to the original image size. |
| 182 int* rowsDecoded) { | 222 // scaledSubset_: Refers to the size of the final output. This can match th e original |
| 223 // dimensions, be a subset of the original dimensions, be a s caled version | |
| 224 // of the original dimensions, or be a scaled subset of the o riginal dimensions. | |
| 225 // subset_: Refers to the size of the unscaled subset in terms of the original image | |
| 226 // dimensions. If this is not a subset decode, this will mat ch the original | |
| 227 // image dimensions. | |
| 228 // scaled_: Refers to the scaled size of the original image, ignoring any subsetting. | |
| 229 // If we are not scaling, this will match the original dimens ions. | |
| 230 SkISize origSize = this->getInfo().dimensions(); | |
| 231 SkIRect subsetRect; | |
| 232 SkISize scaledSize; | |
| 233 SkIRect scaledSubsetRect; | |
| 234 if (nullptr == options.fSubset) { | |
| 235 // This is not a subset decode. | |
| 236 SkASSERT(options.fScaledDimensions.isZero()); | |
| 237 SkASSERT(options.fScaledSubset.isEmpty()); | |
| 183 | 238 |
| 184 if (options.fSubset) { | 239 // Set the "subset" to the full image dimensions. |
| 185 // Subsets are not supported. | 240 subsetRect = SkIRect::MakeSize(origSize); |
| 186 return kUnimplemented; | 241 |
| 242 // This may be scaled or unscaled, depending on if scaledSize matches or igSize. | |
| 243 scaledSize = scaledSubsetInfo.dimensions(); | |
| 244 scaledSubsetRect = SkIRect::MakeSize(scaledSize); | |
| 245 } else { | |
| 246 // This is a subset decode. | |
| 247 if (!is_valid_subset(options.fSubset, origSize)) { | |
| 248 return kInvalidParameters; | |
| 249 } | |
| 250 | |
| 251 subsetRect = *(options.fSubset); | |
| 252 if (options.fScaledDimensions.isZero()) { | |
| 253 // This is an unscaled subset decode. | |
| 254 SkASSERT(options.fScaledSubset.isEmpty()); | |
| 255 SkASSERT(scaledSubsetInfo.dimensions() == subsetRect.size()); | |
| 256 | |
| 257 scaledSize = origSize; | |
| 258 scaledSubsetRect = subsetRect; | |
| 259 } else { | |
| 260 // This is a scaled subset decode. | |
| 261 SkASSERT(!options.fScaledSubset.isEmpty()); | |
| 262 SkASSERT(scaledSubsetInfo.dimensions() == options.fScaledSubset.size ()); | |
| 263 if (!is_valid_subset(&options.fScaledSubset, options.fScaledDimensio ns)) { | |
| 264 return kInvalidParameters; | |
| 265 } | |
| 266 | |
| 267 scaledSize = options.fScaledDimensions; | |
| 268 scaledSubsetRect = options.fScaledSubset; | |
| 269 } | |
| 187 } | 270 } |
| 188 | 271 |
| 189 // FIXME: If no scaling/subsets are requested, we can call fCodec->getPixels . | 272 // Reset the options for use by fCodec. We will handle scaling and subsetti ng |
| 190 Result result = fCodec->startScanlineDecode(requestedInfo, &options, ctable, ctableCount); | 273 // from this level, the native codec does not need to know about it. |
| 191 if (kSuccess == result) { | 274 Options newOptions = options; |
| 192 // native decode supported | 275 newOptions.fSubset = nullptr; |
| 193 switch (fCodec->getScanlineOrder()) { | 276 newOptions.fScaledDimensions = SkISize::Make(0, 0); |
| 194 case SkCodec::kTopDown_SkScanlineOrder: | 277 newOptions.fScaledSubset = SkIRect::MakeEmpty(); |
| 195 case SkCodec::kBottomUp_SkScanlineOrder: | 278 |
| 196 case SkCodec::kNone_SkScanlineOrder: | 279 // The native decoder needs the scaled size of the entire image to check if it can decode to |
| 197 if (fCodec->getScanlines(dst, requestedInfo.height(), rowBytes) != | 280 // the requested scale. |
| 198 requestedInfo.height()) { | 281 SkImageInfo scaledInfo = scaledSubsetInfo.makeWH(scaledSize.width(), scaledS ize.height()); |
| 199 // fCodec has already handled filling uninitialized memory. | 282 Result result = fCodec->startScanlineDecode(scaledInfo, &newOptions, ctable, ctableCount, |
| 200 *rowsDecoded = requestedInfo.height(); | 283 scaledSubsetRect.left(), scaledSubsetRect.width()); |
| 201 return kIncompleteInput; | 284 switch (result) { |
| 202 } | 285 case kSuccess: |
| 203 return kSuccess; | 286 return this->nativeDecode(scaledInfo, dst, rowBytes, scaledSubsetRec t, |
| 204 case SkCodec::kOutOfOrder_SkScanlineOrder: { | 287 options.fZeroInitialized, rowsDecoded); |
| 205 for (int y = 0; y < requestedInfo.height(); y++) { | 288 case kInvalidScale: |
| 206 int dstY = fCodec->nextScanline(); | 289 // We will attempt to scale by sampling below. |
| 207 void* dstPtr = SkTAddOffset<void>(dst, rowBytes * dstY); | 290 break; |
| 291 default: | |
| 292 return result; | |
| 293 } | |
| 294 | |
| 295 // Try to provide the scale using sampling. | |
| 296 int sampleX; | |
| 297 int sampleY; | |
| 298 if (!scaling_supported(scaledSubsetInfo.dimensions(), subsetRect.size(), &sa mpleX, &sampleY)) { | |
| 299 return kInvalidScale; | |
| 300 } | |
| 301 | |
| 302 // Create the image info that will be passed to fCodec. We support scaling in the | |
| 303 // x-dimension, but we will perform scaling in the y-dimension here, so we p ass the scaled | |
| 304 // width and the original height. | |
| 305 // FIXME: This is a bit confusing. I think there is a plan to move scaling completely out | |
| 306 // of the native codec. | |
| 307 SkImageInfo decodeInfo = scaledSubsetInfo.makeWH(scaledSize.width(), this->g etInfo().height()); | |
| 308 | |
| 309 // When starting the fCodec, we pass the left offset based on the original i mage | |
| 310 // dimensions, but need the scaled version of the subset width. | |
| 311 // FIXME: This is a bit confusing. I think there is a plan to move scaling completely out | |
| 312 // of the native codec. | |
| 313 result = fCodec->startScanlineDecode(decodeInfo, &newOptions, ctable, ctable Count, | |
| 314 subsetRect.left(), scaledSubsetInfo.width()); | |
| 315 if (kSuccess != result) { | |
| 316 SkASSERT(kInvalidScale != result); | |
| 317 return result; | |
| 318 } | |
| 319 | |
| 320 return this->sampledDecode(scaledSubsetInfo, dst, rowBytes, subsetRect, scal edSubsetRect, | |
| 321 sampleX, sampleY, options.fZeroInitialized, rowsDecoded); | |
| 322 } | |
| 323 | |
| 324 SkCodec::Result SkScaledCodec::nativeDecode(const SkImageInfo& scaledInfo, void* dst, | |
| 325 size_t rowBytes, const SkIRect& scaledSubsetRect, ZeroInitialized zeroIn it, | |
| 326 int* rowsDecoded) { | |
| 327 | |
| 328 int scaledSubsetTop = scaledSubsetRect.top(); | |
| 329 int scaledSubsetHeight = scaledSubsetRect.height(); | |
| 330 switch (fCodec->getScanlineOrder()) { | |
| 331 case SkCodec::kTopDown_SkScanlineOrder: | |
| 332 case SkCodec::kBottomUp_SkScanlineOrder: | |
| 333 case SkCodec::kNone_SkScanlineOrder: { | |
| 334 if (!fCodec->skipScanlines(scaledSubsetTop)) { | |
| 335 *rowsDecoded = 0; | |
| 336 return kIncompleteInput; | |
| 337 } | |
| 338 | |
| 339 uint32_t decodedLines = fCodec->getScanlines(dst, scaledSubsetHeight , | |
| 340 rowBytes); | |
| 341 if (decodedLines != scaledSubsetHeight) { | |
| 342 *rowsDecoded = decodedLines; | |
| 343 return kIncompleteInput; | |
| 344 } | |
| 345 return kSuccess; | |
| 346 } | |
| 347 case SkCodec::kOutOfOrder_SkScanlineOrder: { | |
| 348 for (int y = 0; y < scaledInfo.height(); y++) { | |
| 349 int dstY = fCodec->nextScanline(); | |
| 350 if (is_in_subset(dstY, scaledSubsetTop, scaledSubsetHeight)) { | |
| 351 void* dstPtr = SkTAddOffset<void>(dst, rowBytes * (dstY - sc aledSubsetTop)); | |
| 208 if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { | 352 if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { |
| 209 *rowsDecoded = y + 1; | 353 *rowsDecoded = y + 1; |
| 210 return kIncompleteInput; | 354 return kIncompleteInput; |
| 211 } | 355 } |
| 356 } else { | |
| 357 if (!fCodec->skipScanlines(1)) { | |
| 358 *rowsDecoded = y + 1; | |
| 359 return kIncompleteInput; | |
| 360 } | |
| 212 } | 361 } |
| 213 return kSuccess; | |
| 214 } | 362 } |
| 363 return kSuccess; | |
| 215 } | 364 } |
| 216 } | 365 } |
| 366 } | |
| 217 | 367 |
| 218 if (kInvalidScale != result) { | 368 SkCodec::Result SkScaledCodec::sampledDecode(const SkImageInfo& scaledSubsetInfo , void* dst, |
| 219 // no scaling requested | 369 size_t rowBytes, const SkIRect& subsetRect, const SkIRect& scaledSubsetR ect, int sampleX, |
| 220 return result; | 370 int sampleY, ZeroInitialized zeroInit, int* rowsDecoded) { |
| 221 } | |
| 222 | 371 |
| 223 // scaling requested | 372 // Set first sample pixel in y direction. |
| 224 int sampleX; | 373 int y0 = get_start_coord(sampleY); |
| 225 int sampleY; | 374 int scaledSubsetHeight = scaledSubsetRect.height(); |
| 226 if (!scaling_supported(requestedInfo, fCodec->getInfo(), &sampleX, &sampleY) ) { | |
| 227 return kInvalidScale; | |
| 228 } | |
| 229 // set first sample pixel in y direction | |
| 230 int Y0 = get_start_coord(sampleY); | |
| 231 | |
| 232 int dstHeight = requestedInfo.height(); | |
| 233 int srcHeight = fCodec->getInfo().height(); | |
| 234 | |
| 235 SkImageInfo info = requestedInfo; | |
| 236 // use original height as codec does not support y sampling natively | |
| 237 info = info.makeWH(requestedInfo.width(), srcHeight); | |
| 238 | |
| 239 // update codec with new info | |
| 240 // FIXME: The previous call to start returned kInvalidScale. This call may | |
| 241 // require a rewind. (skbug.com/4284) | |
| 242 result = fCodec->startScanlineDecode(info, &options, ctable, ctableCount); | |
| 243 if (kSuccess != result) { | |
| 244 return result; | |
| 245 } | |
| 246 | |
| 247 switch(fCodec->getScanlineOrder()) { | 375 switch(fCodec->getScanlineOrder()) { |
| 248 case SkCodec::kTopDown_SkScanlineOrder: { | 376 case SkCodec::kTopDown_SkScanlineOrder: |
| 249 if (!fCodec->skipScanlines(Y0)) { | 377 if (!fCodec->skipScanlines(y0 + subsetRect.top())) { |
| 250 *rowsDecoded = 0; | 378 *rowsDecoded = 0; |
| 251 return kIncompleteInput; | 379 return kIncompleteInput; |
| 252 } | 380 } |
| 253 for (int y = 0; y < dstHeight; y++) { | 381 for (int y = 0; y < scaledSubsetHeight; y++) { |
| 254 if (1 != fCodec->getScanlines(dst, 1, rowBytes)) { | 382 if (1 != fCodec->getScanlines(dst, 1, rowBytes)) { |
| 255 // The failed call to getScanlines() will take care of | 383 // The failed call to getScanlines() will take care of |
| 256 // filling the failed row, so we indicate that we have | 384 // filling the failed row, so we indicate that we have |
| 257 // decoded (y + 1) rows. | 385 // decoded (y + 1) rows. |
| 258 *rowsDecoded = y + 1; | 386 *rowsDecoded = y + 1; |
| 259 return kIncompleteInput; | 387 return kIncompleteInput; |
| 260 } | 388 } |
| 261 if (y < dstHeight - 1) { | 389 if (y < scaledSubsetHeight - 1) { |
| 262 if (!fCodec->skipScanlines(sampleY - 1)) { | 390 if (!fCodec->skipScanlines(sampleY - 1)) { |
| 263 *rowsDecoded = y + 1; | 391 *rowsDecoded = y + 1; |
| 264 return kIncompleteInput; | 392 return kIncompleteInput; |
| 265 } | 393 } |
| 266 } | 394 } |
| 267 dst = SkTAddOffset<void>(dst, rowBytes); | 395 dst = SkTAddOffset<void>(dst, rowBytes); |
| 268 } | 396 } |
| 269 return kSuccess; | 397 return kSuccess; |
| 270 } | |
| 271 case SkCodec::kBottomUp_SkScanlineOrder: | 398 case SkCodec::kBottomUp_SkScanlineOrder: |
| 272 case SkCodec::kOutOfOrder_SkScanlineOrder: { | 399 case SkCodec::kOutOfOrder_SkScanlineOrder: { |
| 273 Result result = kSuccess; | 400 Result result = kSuccess; |
| 274 int y; | 401 int y; |
| 275 for (y = 0; y < srcHeight; y++) { | 402 for (y = 0; y < this->getInfo().height(); y++) { |
| 276 int srcY = fCodec->nextScanline(); | 403 int srcY = fCodec->nextScanline(); |
| 277 if (is_coord_necessary(srcY, sampleY, dstHeight)) { | 404 if (is_coord_necessary(srcY, sampleY, scaledSubsetHeight, subset Rect.top())) { |
| 278 void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_co ord(srcY, sampleY)); | 405 void* dstPtr = SkTAddOffset<void>(dst, rowBytes * get_dst_co ord(srcY, sampleY)); |
| 279 if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { | 406 if (1 != fCodec->getScanlines(dstPtr, 1, rowBytes)) { |
| 280 result = kIncompleteInput; | 407 result = kIncompleteInput; |
| 281 break; | 408 break; |
| 282 } | 409 } |
| 283 } else { | 410 } else { |
| 284 if (!fCodec->skipScanlines(1)) { | 411 if (!fCodec->skipScanlines(1)) { |
| 285 result = kIncompleteInput; | 412 result = kIncompleteInput; |
| 286 break; | 413 break; |
| 287 } | 414 } |
| 288 } | 415 } |
| 289 } | 416 } |
| 290 | 417 |
| 291 // We handle filling uninitialized memory here instead of in the par ent class. | 418 // We handle filling uninitialized memory here instead of in the par ent class. |
| 292 // The parent class does not know that we are sampling. | 419 // The parent class does not know that we are sampling. |
| 293 if (kIncompleteInput == result) { | 420 if (kIncompleteInput == result) { |
| 294 const SkImageInfo fillInfo = requestedInfo.makeWH(requestedInfo. width(), 1); | 421 const SkImageInfo fillInfo = scaledSubsetInfo.makeWH(scaledSubse tRect.width(), 1); |
| 295 const uint32_t fillValue = fCodec->getFillValue(fillInfo.colorTy pe(), | 422 const uint32_t fillValue = fCodec->getFillValue(fillInfo.colorTy pe(), |
| 296 fillInfo.alphaType()); | 423 fillInfo.alphaType()); |
| 297 for (; y < srcHeight; y++) { | 424 for (; y < this->getInfo().height(); y++) { |
| 298 int srcY = fCodec->outputScanline(y); | 425 int srcY = fCodec->outputScanline(y); |
| 299 if (is_coord_necessary(srcY, sampleY, dstHeight)) { | 426 if (is_coord_necessary(srcY, sampleY, scaledSubsetHeight, su bsetRect.top())) { |
| 300 void* dstRow = SkTAddOffset<void>(dst, | 427 void* dstRow = SkTAddOffset<void>(dst, |
| 301 rowBytes * get_dst_coord(srcY, sampleY)); | 428 rowBytes * get_dst_coord(srcY, sampleY)); |
| 302 SkSwizzler::Fill(dstRow, fillInfo, rowBytes, fillValue, | 429 SkSwizzler::Fill(dstRow, fillInfo, rowBytes, fillValue, zeroInit); |
| 303 options.fZeroInitialized); | |
| 304 } | 430 } |
| 305 } | 431 } |
| 306 *rowsDecoded = dstHeight; | 432 *rowsDecoded = scaledSubsetInfo.height(); |
| 307 } | 433 } |
| 308 return result; | 434 return result; |
| 309 } | 435 } |
| 310 case SkCodec::kNone_SkScanlineOrder: { | 436 case SkCodec::kNone_SkScanlineOrder: { |
| 311 SkAutoMalloc storage(srcHeight * rowBytes); | 437 SkAutoMalloc storage(subsetRect.height() * rowBytes); |
| 312 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); | 438 uint8_t* storagePtr = static_cast<uint8_t*>(storage.get()); |
| 313 int scanlines = fCodec->getScanlines(storagePtr, srcHeight, rowBytes ); | 439 if (!fCodec->skipScanlines(subsetRect.top())) { |
| 314 storagePtr += Y0 * rowBytes; | 440 *rowsDecoded = 0; |
| 315 scanlines -= Y0; | 441 return kIncompleteInput; |
| 442 } | |
| 443 int scanlines = fCodec->getScanlines(storagePtr, subsetRect.height() , rowBytes); | |
| 444 scanlines -= y0; | |
| 445 storagePtr += y0 * rowBytes; | |
| 316 int y = 0; | 446 int y = 0; |
| 317 while (y < dstHeight && scanlines > 0) { | 447 while (y < scaledSubsetHeight && scanlines > 0) { |
| 318 memcpy(dst, storagePtr, rowBytes); | 448 memcpy(dst, storagePtr, scaledSubsetInfo.minRowBytes()); |
| 319 storagePtr += sampleY * rowBytes; | 449 storagePtr += sampleY * rowBytes; |
| 320 dst = SkTAddOffset<void>(dst, rowBytes); | 450 dst = SkTAddOffset<void>(dst, rowBytes); |
| 321 scanlines -= sampleY; | 451 scanlines -= sampleY; |
| 322 y++; | 452 y++; |
| 323 } | 453 } |
| 324 if (y < dstHeight) { | 454 if (y < scaledSubsetHeight) { |
| 325 // fCodec has already handled filling uninitialized memory. | 455 // fCodec has already handled filling uninitialized memory. |
| 326 *rowsDecoded = dstHeight; | 456 *rowsDecoded = scaledSubsetInfo.height(); |
| 327 return kIncompleteInput; | 457 return kIncompleteInput; |
| 328 } | 458 } |
| 329 return kSuccess; | 459 return kSuccess; |
| 330 } | 460 } |
| 331 default: | 461 default: |
| 332 SkASSERT(false); | 462 SkASSERT(false); |
| 333 return kUnimplemented; | 463 return kUnimplemented; |
| 334 } | 464 } |
| 335 } | 465 } |
| 336 | 466 |
| 337 uint32_t SkScaledCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaT ype) const { | 467 uint32_t SkScaledCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaT ype) const { |
| 338 return fCodec->onGetFillValue(colorType, alphaType); | 468 return fCodec->onGetFillValue(colorType, alphaType); |
| 339 } | 469 } |
| 340 | 470 |
| 341 SkCodec::SkScanlineOrder SkScaledCodec::onGetScanlineOrder() const { | 471 SkCodec::SkScanlineOrder SkScaledCodec::onGetScanlineOrder() const { |
| 342 return fCodec->onGetScanlineOrder(); | 472 return fCodec->onGetScanlineOrder(); |
| 343 } | 473 } |
| OLD | NEW |