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 |