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 "SkCodec.h" |
9 #include "SkCodecPriv.h" | 9 #include "SkCodecPriv.h" |
10 #include "SkMath.h" | 10 #include "SkMath.h" |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 // If the native codec does not support the requested scale, scale by sa
mpling. | 94 // If the native codec does not support the requested scale, scale by sa
mpling. |
95 return this->sampledDecode(info, pixels, rowBytes, options); | 95 return this->sampledDecode(info, pixels, rowBytes, options); |
96 } | 96 } |
97 | 97 |
98 // Calculate the scaled subset bounds. | 98 // Calculate the scaled subset bounds. |
99 int scaledSubsetX = subset->x() / sampleSize; | 99 int scaledSubsetX = subset->x() / sampleSize; |
100 int scaledSubsetY = subset->y() / sampleSize; | 100 int scaledSubsetY = subset->y() / sampleSize; |
101 int scaledSubsetWidth = info.width(); | 101 int scaledSubsetWidth = info.width(); |
102 int scaledSubsetHeight = info.height(); | 102 int scaledSubsetHeight = info.height(); |
103 | 103 |
104 const SkImageInfo scaledInfo = info.makeWH(scaledSize.width(), scaledSize.he
ight()); | |
105 | |
106 { | |
107 // Although startScanlineDecode expects the bottom and top to match the | |
108 // SkImageInfo, startIncrementalDecode uses them to determine when to st
art | |
109 // and end calling the callback. | |
110 SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubse
tY, | |
111 scaledSubsetWidth, scaledS
ubsetHeight); | |
112 codecOptions.fSubset = &incrementalSubset; | |
113 const SkCodec::Result startResult = this->codec()->startIncrementalDecod
e( | |
114 scaledInfo, pixels, rowBytes, &codecOptions, | |
115 options.fColorPtr, options.fColorCount); | |
116 if (SkCodec::kSuccess == startResult) { | |
117 int rowsDecoded; | |
118 const SkCodec::Result incResult = this->codec()->incrementalDecode(&
rowsDecoded); | |
119 if (incResult == SkCodec::kSuccess) { | |
120 return SkCodec::kSuccess; | |
121 } | |
122 SkASSERT(SkCodec::kIncompleteInput == incResult); | |
123 | |
124 // FIXME: Can zero initialized be read from SkCodec::fOptions? | |
125 this->codec()->fillIncompleteImage(scaledInfo, pixels, rowBytes, | |
126 options.fZeroInitialized, scaledSubsetHeight, rowsDecoded); | |
127 return SkCodec::kIncompleteInput; | |
128 } else if (startResult != SkCodec::kUnimplemented) { | |
129 return startResult; | |
130 } | |
131 // Otherwise fall down to use the old scanline decoder. | |
132 // codecOptions.fSubset will be reset below, so it will not continue to | |
133 // point to the object that is no longer on the stack. | |
134 } | |
135 | |
136 // Start the scanline decode. | 104 // Start the scanline decode. |
137 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWid
th, | 105 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWid
th, |
138 scaledSize.height()); | 106 scaledSize.height()); |
139 codecOptions.fSubset = &scanlineSubset; | 107 codecOptions.fSubset = &scanlineSubset; |
140 | 108 SkCodec::Result result = this->codec()->startScanlineDecode(info.makeWH(scal
edSize.width(), |
141 SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo, | 109 scaledSize.height()), &codecOptions, options.fColorPtr, options.fCol
orCount); |
142 &codecOptions, options.fColorPtr, options.fColorCount); | |
143 if (SkCodec::kSuccess != result) { | 110 if (SkCodec::kSuccess != result) { |
144 return result; | 111 return result; |
145 } | 112 } |
146 | 113 |
147 // At this point, we are only concerned with subsetting. Either no scale wa
s | 114 // At this point, we are only concerned with subsetting. Either no scale wa
s |
148 // requested, or the this->codec() is handling the scale. | 115 // requested, or the this->codec() is handling the scale. |
149 // Note that subsetting is only supported for kTopDown, so this code will no
t be | 116 switch (this->codec()->getScanlineOrder()) { |
150 // reached for other orders. | 117 case SkCodec::kTopDown_SkScanlineOrder: |
151 SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOr
der); | 118 case SkCodec::kNone_SkScanlineOrder: { |
152 if (!this->codec()->skipScanlines(scaledSubsetY)) { | 119 if (!this->codec()->skipScanlines(scaledSubsetY)) { |
153 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZero
Initialized, | 120 this->codec()->fillIncompleteImage(info, pixels, rowBytes, optio
ns.fZeroInitialized, |
154 scaledSubsetHeight, 0); | 121 scaledSubsetHeight, 0); |
155 return SkCodec::kIncompleteInput; | 122 return SkCodec::kIncompleteInput; |
| 123 } |
| 124 |
| 125 int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetH
eight, rowBytes); |
| 126 if (decodedLines != scaledSubsetHeight) { |
| 127 return SkCodec::kIncompleteInput; |
| 128 } |
| 129 return SkCodec::kSuccess; |
| 130 } |
| 131 default: |
| 132 SkASSERT(false); |
| 133 return SkCodec::kUnimplemented; |
156 } | 134 } |
157 | |
158 int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, r
owBytes); | |
159 if (decodedLines != scaledSubsetHeight) { | |
160 return SkCodec::kIncompleteInput; | |
161 } | |
162 return SkCodec::kSuccess; | |
163 } | 135 } |
164 | 136 |
165 | 137 |
166 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix
els, | 138 SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix
els, |
167 size_t rowBytes, const AndroidOptions& options) { | 139 size_t rowBytes, const AndroidOptions& options) { |
168 // We should only call this function when sampling. | 140 // We should only call this function when sampling. |
169 SkASSERT(options.fSampleSize > 1); | 141 SkASSERT(options.fSampleSize > 1); |
170 | 142 |
171 // Create options struct for the codec. | 143 // Create options struct for the codec. |
172 SkCodec::Options sampledOptions; | 144 SkCodec::Options sampledOptions; |
(...skipping 22 matching lines...) Expand all Loading... |
195 subsetY = subsetPtr->y() / nativeSampleSize; | 167 subsetY = subsetPtr->y() / nativeSampleSize; |
196 | 168 |
197 subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize)
; | 169 subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize)
; |
198 subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSiz
e); | 170 subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSiz
e); |
199 | 171 |
200 // The scanline decoder only needs to be aware of subsetting in the x-di
mension. | 172 // The scanline decoder only needs to be aware of subsetting in the x-di
mension. |
201 subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height()); | 173 subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height()); |
202 sampledOptions.fSubset = ⊂ | 174 sampledOptions.fSubset = ⊂ |
203 } | 175 } |
204 | 176 |
205 // Since we guarantee that output dimensions are always at least one (even i
f the sampleSize | |
206 // is greater than a given dimension), the input sampleSize is not always th
e sampleSize that | |
207 // we use in practice. | |
208 const int sampleX = subsetWidth / info.width(); | |
209 const int sampleY = subsetHeight / info.height(); | |
210 | |
211 const int samplingOffsetY = get_start_coord(sampleY); | |
212 const int startY = samplingOffsetY + subsetY; | |
213 int dstHeight = info.height(); | |
214 | |
215 const SkImageInfo nativeInfo = info.makeWH(nativeSize.width(), nativeSize.he
ight()); | |
216 | |
217 { | |
218 // Although startScanlineDecode expects the bottom and top to match the | |
219 // SkImageInfo, startIncrementalDecode uses them to determine when to st
art | |
220 // and end calling the callback. | |
221 SkIRect incrementalSubset; | |
222 incrementalSubset.fTop = startY; | |
223 incrementalSubset.fBottom = startY + (dstHeight - 1) * sampleY + 1; | |
224 if (sampledOptions.fSubset) { | |
225 incrementalSubset.fLeft = sampledOptions.fSubset->fLeft; | |
226 incrementalSubset.fRight = sampledOptions.fSubset->fRight; | |
227 } else { | |
228 incrementalSubset.fLeft = 0; | |
229 incrementalSubset.fRight = nativeSize.width(); | |
230 } | |
231 SkCodec::Options incrementalOptions = sampledOptions; | |
232 incrementalOptions.fSubset = &incrementalSubset; | |
233 const SkCodec::Result startResult = this->codec()->startIncrementalDecod
e(nativeInfo, | |
234 pixels, rowBytes, &incrementalOptions, options.fColorPtr, option
s.fColorCount); | |
235 if (SkCodec::kSuccess == startResult) { | |
236 SkSampler* sampler = this->codec()->getSampler(true); | |
237 if (!sampler) { | |
238 return SkCodec::kUnimplemented; | |
239 } | |
240 | |
241 if (sampler->setSampleX(sampleX) != info.width()) { | |
242 return SkCodec::kInvalidScale; | |
243 } | |
244 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { | |
245 return SkCodec::kInvalidScale; | |
246 } | |
247 | |
248 sampler->setSampleY(sampleY); | |
249 | |
250 int rowsDecoded; | |
251 const SkCodec::Result incResult = this->codec()->incrementalDecode(&
rowsDecoded); | |
252 if (incResult == SkCodec::kSuccess) { | |
253 return SkCodec::kSuccess; | |
254 } | |
255 SkASSERT(incResult == SkCodec::kIncompleteInput); | |
256 const int lastRowInOutput = (rowsDecoded - startY) / sampleY; | |
257 // FIXME: Should this be info or nativeInfo? Does it make a differen
ce? | |
258 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.f
ZeroInitialized, | |
259 info.height(), lastRowInOutput); | |
260 return SkCodec::kIncompleteInput; | |
261 } else if (startResult != SkCodec::kUnimplemented) { | |
262 return startResult; | |
263 } // kUnimplemented means use the old method. | |
264 } | |
265 | |
266 // Start the scanline decode. | 177 // Start the scanline decode. |
267 SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo, | 178 SkCodec::Result result = this->codec()->startScanlineDecode( |
268 &sampledOptions, options.fColorPtr, options.fColorCount); | 179 info.makeWH(nativeSize.width(), nativeSize.height()), &sampledOption
s, |
| 180 options.fColorPtr, options.fColorCount); |
269 if (SkCodec::kSuccess != result) { | 181 if (SkCodec::kSuccess != result) { |
270 return result; | 182 return result; |
271 } | 183 } |
272 | 184 |
273 SkSampler* sampler = this->codec()->getSampler(true); | 185 SkSampler* sampler = this->codec()->getSampler(true); |
274 if (!sampler) { | 186 if (!sampler) { |
275 return SkCodec::kUnimplemented; | 187 return SkCodec::kUnimplemented; |
276 } | 188 } |
277 | 189 |
| 190 // Since we guarantee that output dimensions are always at least one (even i
f the sampleSize |
| 191 // is greater than a given dimension), the input sampleSize is not always th
e sampleSize that |
| 192 // we use in practice. |
| 193 const int sampleX = subsetWidth / info.width(); |
| 194 const int sampleY = subsetHeight / info.height(); |
278 if (sampler->setSampleX(sampleX) != info.width()) { | 195 if (sampler->setSampleX(sampleX) != info.width()) { |
279 return SkCodec::kInvalidScale; | 196 return SkCodec::kInvalidScale; |
280 } | 197 } |
281 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { | 198 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) { |
282 return SkCodec::kInvalidScale; | 199 return SkCodec::kInvalidScale; |
283 } | 200 } |
284 | 201 |
| 202 const int samplingOffsetY = get_start_coord(sampleY); |
| 203 const int startY = samplingOffsetY + subsetY; |
| 204 int dstHeight = info.height(); |
285 switch(this->codec()->getScanlineOrder()) { | 205 switch(this->codec()->getScanlineOrder()) { |
286 case SkCodec::kTopDown_SkScanlineOrder: { | 206 case SkCodec::kTopDown_SkScanlineOrder: { |
287 if (!this->codec()->skipScanlines(startY)) { | 207 if (!this->codec()->skipScanlines(startY)) { |
288 this->codec()->fillIncompleteImage(info, pixels, rowBytes, optio
ns.fZeroInitialized, | 208 this->codec()->fillIncompleteImage(info, pixels, rowBytes, optio
ns.fZeroInitialized, |
289 dstHeight, 0); | 209 dstHeight, 0); |
290 return SkCodec::kIncompleteInput; | 210 return SkCodec::kIncompleteInput; |
291 } | 211 } |
292 void* pixelPtr = pixels; | 212 void* pixelPtr = pixels; |
293 for (int y = 0; y < dstHeight; y++) { | 213 for (int y = 0; y < dstHeight; y++) { |
294 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { | 214 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
339 int srcY = this->codec()->outputScanline(y); | 259 int srcY = this->codec()->outputScanline(y); |
340 if (!is_coord_necessary(srcY, sampleY, dstHeight)) { | 260 if (!is_coord_necessary(srcY, sampleY, dstHeight)) { |
341 continue; | 261 continue; |
342 } | 262 } |
343 | 263 |
344 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coo
rd(srcY, sampleY)); | 264 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coo
rd(srcY, sampleY)); |
345 SkSampler::Fill(fillInfo, rowPtr, rowBytes, fillValue, options.f
ZeroInitialized); | 265 SkSampler::Fill(fillInfo, rowPtr, rowBytes, fillValue, options.f
ZeroInitialized); |
346 } | 266 } |
347 return SkCodec::kIncompleteInput; | 267 return SkCodec::kIncompleteInput; |
348 } | 268 } |
| 269 case SkCodec::kNone_SkScanlineOrder: { |
| 270 const int linesNeeded = subsetHeight - samplingOffsetY; |
| 271 SkAutoTMalloc<uint8_t> storage(linesNeeded * rowBytes); |
| 272 uint8_t* storagePtr = storage.get(); |
| 273 |
| 274 if (!this->codec()->skipScanlines(startY)) { |
| 275 this->codec()->fillIncompleteImage(info, pixels, rowBytes, optio
ns.fZeroInitialized, |
| 276 dstHeight, 0); |
| 277 return SkCodec::kIncompleteInput; |
| 278 } |
| 279 int scanlines = this->codec()->getScanlines(storagePtr, linesNeeded,
rowBytes); |
| 280 |
| 281 for (int y = 0; y < dstHeight; y++) { |
| 282 memcpy(pixels, storagePtr, info.minRowBytes()); |
| 283 storagePtr += sampleY * rowBytes; |
| 284 pixels = SkTAddOffset<void>(pixels, rowBytes); |
| 285 } |
| 286 |
| 287 if (scanlines < dstHeight) { |
| 288 // this->codec() has already handled filling uninitialized memor
y. |
| 289 return SkCodec::kIncompleteInput; |
| 290 } |
| 291 return SkCodec::kSuccess; |
| 292 } |
349 default: | 293 default: |
350 SkASSERT(false); | 294 SkASSERT(false); |
351 return SkCodec::kUnimplemented; | 295 return SkCodec::kUnimplemented; |
352 } | 296 } |
353 } | 297 } |
OLD | NEW |