blob: 4371e693791e8fd809b9874122972dc6b871edfa [file] [log] [blame]
emmaleer8f4ba762015-08-14 07:44:46 -07001/*
2 * Copyright 2015 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/codec/SkCodec.h"
9#include "include/core/SkMath.h"
10#include "include/private/SkTemplates.h"
11#include "src/codec/SkCodecPriv.h"
12#include "src/codec/SkSampledCodec.h"
13#include "src/codec/SkSampler.h"
14#include "src/core/SkMathPriv.h"
emmaleer8f4ba762015-08-14 07:44:46 -070015
Leon Scroggins IIIda3e9ad2018-01-26 15:48:26 -050016SkSampledCodec::SkSampledCodec(SkCodec* codec, ExifOrientationBehavior behavior)
17 : INHERITED(codec, behavior)
emmaleer8f4ba762015-08-14 07:44:46 -070018{}
19
scroggo501b7342015-11-03 07:55:11 -080020SkISize SkSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const {
Leon Scroggins III712476e2018-10-03 15:47:00 -040021 SkISize preSampledSize = this->codec()->dimensions();
scroggo501b7342015-11-03 07:55:11 -080022 int sampleSize = *sampleSizePtr;
23 SkASSERT(sampleSize > 1);
24
25 if (nativeSampleSize) {
26 *nativeSampleSize = 1;
27 }
28
29 // Only JPEG supports native downsampling.
Hal Canarydb683012016-11-23 08:55:18 -070030 if (this->codec()->getEncodedFormat() == SkEncodedImageFormat::kJPEG) {
scroggo501b7342015-11-03 07:55:11 -080031 // See if libjpeg supports this scale directly
32 switch (sampleSize) {
33 case 2:
34 case 4:
35 case 8:
36 // This class does not need to do any sampling.
37 *sampleSizePtr = 1;
msarett90c4d5f2015-12-10 13:09:24 -080038 return this->codec()->getScaledDimensions(get_scale_from_sample_size(sampleSize));
scroggo501b7342015-11-03 07:55:11 -080039 default:
40 break;
41 }
42
43 // Check if sampleSize is a multiple of something libjpeg can support.
44 int remainder;
45 const int sampleSizes[] = { 8, 4, 2 };
46 for (int supportedSampleSize : sampleSizes) {
47 int actualSampleSize;
48 SkTDivMod(sampleSize, supportedSampleSize, &actualSampleSize, &remainder);
49 if (0 == remainder) {
50 float scale = get_scale_from_sample_size(supportedSampleSize);
51
msarett90c4d5f2015-12-10 13:09:24 -080052 // this->codec() will scale to this size.
53 preSampledSize = this->codec()->getScaledDimensions(scale);
scroggo501b7342015-11-03 07:55:11 -080054
55 // And then this class will sample it.
56 *sampleSizePtr = actualSampleSize;
57 if (nativeSampleSize) {
58 *nativeSampleSize = supportedSampleSize;
59 }
60 break;
61 }
62 }
63 }
64
65 return preSampledSize;
66}
67
msarett3d9d7a72015-10-21 10:27:10 -070068SkISize SkSampledCodec::onGetSampledDimensions(int sampleSize) const {
scroggo501b7342015-11-03 07:55:11 -080069 const SkISize size = this->accountForNativeScaling(&sampleSize);
70 return SkISize::Make(get_scaled_dimension(size.width(), sampleSize),
71 get_scaled_dimension(size.height(), sampleSize));
msaretta83593b2015-08-18 08:03:58 -070072}
emmaleer8f4ba762015-08-14 07:44:46 -070073
msarett3d9d7a72015-10-21 10:27:10 -070074SkCodec::Result SkSampledCodec::onGetAndroidPixels(const SkImageInfo& info, void* pixels,
scroggoe95a0682015-11-04 04:31:12 -080075 size_t rowBytes, const AndroidOptions& options) {
Leon Scroggins1340dbd2020-11-09 14:18:12 -050076 const SkIRect* subset = options.fSubset;
Leon Scroggins III712476e2018-10-03 15:47:00 -040077 if (!subset || subset->size() == this->codec()->dimensions()) {
msarett90c4d5f2015-12-10 13:09:24 -080078 if (this->codec()->dimensionsSupported(info.dimensions())) {
Leon Scroggins1340dbd2020-11-09 14:18:12 -050079 return this->codec()->getPixels(info, pixels, rowBytes, &options);
emmaleer8f4ba762015-08-14 07:44:46 -070080 }
emmaleer8f4ba762015-08-14 07:44:46 -070081
msarett3d9d7a72015-10-21 10:27:10 -070082 // If the native codec does not support the requested scale, scale by sampling.
83 return this->sampledDecode(info, pixels, rowBytes, options);
scroggoe7fc14b2015-10-02 13:14:46 -070084 }
85
msarett3d9d7a72015-10-21 10:27:10 -070086 // We are performing a subset decode.
87 int sampleSize = options.fSampleSize;
scroggo501b7342015-11-03 07:55:11 -080088 SkISize scaledSize = this->getSampledDimensions(sampleSize);
msarett90c4d5f2015-12-10 13:09:24 -080089 if (!this->codec()->dimensionsSupported(scaledSize)) {
msarett3d9d7a72015-10-21 10:27:10 -070090 // If the native codec does not support the requested scale, scale by sampling.
91 return this->sampledDecode(info, pixels, rowBytes, options);
92 }
scroggoe7fc14b2015-10-02 13:14:46 -070093
msarett3d9d7a72015-10-21 10:27:10 -070094 // Calculate the scaled subset bounds.
95 int scaledSubsetX = subset->x() / sampleSize;
96 int scaledSubsetY = subset->y() / sampleSize;
97 int scaledSubsetWidth = info.width();
98 int scaledSubsetHeight = info.height();
emmaleer8f4ba762015-08-14 07:44:46 -070099
Brian Salomon9241a6d2019-10-03 13:26:54 -0400100 const SkImageInfo scaledInfo = info.makeDimensions(scaledSize);
scroggo8e6c7ad2016-09-16 08:20:38 -0700101
Leon Scroggins1340dbd2020-11-09 14:18:12 -0500102 // Copy so we can use a different fSubset.
103 AndroidOptions subsetOptions = options;
scroggo8e6c7ad2016-09-16 08:20:38 -0700104 {
105 // Although startScanlineDecode expects the bottom and top to match the
106 // SkImageInfo, startIncrementalDecode uses them to determine which rows to
107 // decode.
108 SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY,
109 scaledSubsetWidth, scaledSubsetHeight);
Leon Scroggins1340dbd2020-11-09 14:18:12 -0500110 subsetOptions.fSubset = &incrementalSubset;
scroggo8e6c7ad2016-09-16 08:20:38 -0700111 const SkCodec::Result startResult = this->codec()->startIncrementalDecode(
Leon Scroggins1340dbd2020-11-09 14:18:12 -0500112 scaledInfo, pixels, rowBytes, &subsetOptions);
scroggo8e6c7ad2016-09-16 08:20:38 -0700113 if (SkCodec::kSuccess == startResult) {
Leon Scroggins III6e45ce72018-10-16 15:29:11 -0400114 int rowsDecoded = 0;
scroggo8e6c7ad2016-09-16 08:20:38 -0700115 const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
116 if (incResult == SkCodec::kSuccess) {
117 return SkCodec::kSuccess;
118 }
Leon Scroggins III6e45ce72018-10-16 15:29:11 -0400119 SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput);
scroggo8e6c7ad2016-09-16 08:20:38 -0700120
121 // FIXME: Can zero initialized be read from SkCodec::fOptions?
122 this->codec()->fillIncompleteImage(scaledInfo, pixels, rowBytes,
123 options.fZeroInitialized, scaledSubsetHeight, rowsDecoded);
Leon Scroggins III6e45ce72018-10-16 15:29:11 -0400124 return incResult;
scroggo8e6c7ad2016-09-16 08:20:38 -0700125 } else if (startResult != SkCodec::kUnimplemented) {
126 return startResult;
127 }
128 // Otherwise fall down to use the old scanline decoder.
Leon Scroggins1340dbd2020-11-09 14:18:12 -0500129 // subsetOptions.fSubset will be reset below, so it will not continue to
scroggo8e6c7ad2016-09-16 08:20:38 -0700130 // point to the object that is no longer on the stack.
131 }
132
msarett3d9d7a72015-10-21 10:27:10 -0700133 // Start the scanline decode.
134 SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
135 scaledSize.height());
Leon Scroggins1340dbd2020-11-09 14:18:12 -0500136 subsetOptions.fSubset = &scanlineSubset;
scroggo8e6c7ad2016-09-16 08:20:38 -0700137
138 SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo,
Leon Scroggins1340dbd2020-11-09 14:18:12 -0500139 &subsetOptions);
msarett3d9d7a72015-10-21 10:27:10 -0700140 if (SkCodec::kSuccess != result) {
141 return result;
142 }
emmaleer8f4ba762015-08-14 07:44:46 -0700143
msarett3d9d7a72015-10-21 10:27:10 -0700144 // At this point, we are only concerned with subsetting. Either no scale was
msarett90c4d5f2015-12-10 13:09:24 -0800145 // requested, or the this->codec() is handling the scale.
scroggo8e6c7ad2016-09-16 08:20:38 -0700146 // Note that subsetting is only supported for kTopDown, so this code will not be
147 // reached for other orders.
148 SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder);
149 if (!this->codec()->skipScanlines(scaledSubsetY)) {
150 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
151 scaledSubsetHeight, 0);
152 return SkCodec::kIncompleteInput;
scroggo6fb23912016-06-02 14:16:43 -0700153 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700154
155 int decodedLines = this->codec()->getScanlines(pixels, scaledSubsetHeight, rowBytes);
156 if (decodedLines != scaledSubsetHeight) {
157 return SkCodec::kIncompleteInput;
158 }
159 return SkCodec::kSuccess;
emmaleer8f4ba762015-08-14 07:44:46 -0700160}
161
emmaleer8f4ba762015-08-14 07:44:46 -0700162
msarett3d9d7a72015-10-21 10:27:10 -0700163SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
scroggoe95a0682015-11-04 04:31:12 -0800164 size_t rowBytes, const AndroidOptions& options) {
scroggo501b7342015-11-03 07:55:11 -0800165 // We should only call this function when sampling.
166 SkASSERT(options.fSampleSize > 1);
167
scroggo501b7342015-11-03 07:55:11 -0800168 // FIXME: This was already called by onGetAndroidPixels. Can we reduce that?
169 int sampleSize = options.fSampleSize;
170 int nativeSampleSize;
171 SkISize nativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize);
172
msarett3d9d7a72015-10-21 10:27:10 -0700173 // Check if there is a subset.
174 SkIRect subset;
175 int subsetY = 0;
scroggo501b7342015-11-03 07:55:11 -0800176 int subsetWidth = nativeSize.width();
177 int subsetHeight = nativeSize.height();
emmaleer8f4ba762015-08-14 07:44:46 -0700178 if (options.fSubset) {
msarett3d9d7a72015-10-21 10:27:10 -0700179 // We will need to know about subsetting in the y-dimension in order to use the
180 // scanline decoder.
msarett90c4d5f2015-12-10 13:09:24 -0800181 // Update the subset to account for scaling done by this->codec().
scroggo4f2a88c2016-10-17 14:32:41 -0700182 const SkIRect* subsetPtr = options.fSubset;
scroggo501b7342015-11-03 07:55:11 -0800183
184 // Do the divide ourselves, instead of calling get_scaled_dimension. If
185 // X and Y are 0, they should remain 0, rather than being upgraded to 1
186 // due to being smaller than the sampleSize.
187 const int subsetX = subsetPtr->x() / nativeSampleSize;
188 subsetY = subsetPtr->y() / nativeSampleSize;
189
190 subsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize);
191 subsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize);
msarett3d9d7a72015-10-21 10:27:10 -0700192
193 // The scanline decoder only needs to be aware of subsetting in the x-dimension.
scroggo501b7342015-11-03 07:55:11 -0800194 subset.setXYWH(subsetX, 0, subsetWidth, nativeSize.height());
emmaleer8f4ba762015-08-14 07:44:46 -0700195 }
196
scroggo8e6c7ad2016-09-16 08:20:38 -0700197 // Since we guarantee that output dimensions are always at least one (even if the sampleSize
198 // is greater than a given dimension), the input sampleSize is not always the sampleSize that
199 // we use in practice.
200 const int sampleX = subsetWidth / info.width();
201 const int sampleY = subsetHeight / info.height();
202
203 const int samplingOffsetY = get_start_coord(sampleY);
204 const int startY = samplingOffsetY + subsetY;
scroggo4f2a88c2016-10-17 14:32:41 -0700205 const int dstHeight = info.height();
scroggo8e6c7ad2016-09-16 08:20:38 -0700206
Brian Salomon9241a6d2019-10-03 13:26:54 -0400207 const SkImageInfo nativeInfo = info.makeDimensions(nativeSize);
scroggo8e6c7ad2016-09-16 08:20:38 -0700208
209 {
210 // Although startScanlineDecode expects the bottom and top to match the
211 // SkImageInfo, startIncrementalDecode uses them to determine which rows to
212 // decode.
Leon Scroggins1340dbd2020-11-09 14:18:12 -0500213 AndroidOptions incrementalOptions = options;
scroggo8e6c7ad2016-09-16 08:20:38 -0700214 SkIRect incrementalSubset;
Leon Scroggins1340dbd2020-11-09 14:18:12 -0500215 if (options.fSubset) {
216 incrementalSubset.fTop = subsetY;
217 incrementalSubset.fBottom = subsetY + subsetHeight;
218 incrementalSubset.fLeft = subset.fLeft;
219 incrementalSubset.fRight = subset.fRight;
scroggo4f2a88c2016-10-17 14:32:41 -0700220 incrementalOptions.fSubset = &incrementalSubset;
scroggo8e6c7ad2016-09-16 08:20:38 -0700221 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700222 const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo,
Leon Scroggins571b30f2017-07-11 17:35:31 +0000223 pixels, rowBytes, &incrementalOptions);
scroggo8e6c7ad2016-09-16 08:20:38 -0700224 if (SkCodec::kSuccess == startResult) {
225 SkSampler* sampler = this->codec()->getSampler(true);
226 if (!sampler) {
227 return SkCodec::kUnimplemented;
228 }
229
230 if (sampler->setSampleX(sampleX) != info.width()) {
231 return SkCodec::kInvalidScale;
232 }
233 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
234 return SkCodec::kInvalidScale;
235 }
236
237 sampler->setSampleY(sampleY);
238
Leon Scroggins III6e45ce72018-10-16 15:29:11 -0400239 int rowsDecoded = 0;
scroggo8e6c7ad2016-09-16 08:20:38 -0700240 const SkCodec::Result incResult = this->codec()->incrementalDecode(&rowsDecoded);
241 if (incResult == SkCodec::kSuccess) {
242 return SkCodec::kSuccess;
243 }
Leon Scroggins III674a1842017-07-06 12:26:09 -0400244 SkASSERT(incResult == SkCodec::kIncompleteInput || incResult == SkCodec::kErrorInInput);
msarettc6c81e12016-09-16 14:52:07 -0700245
scroggoff9f7bb2016-10-10 11:35:01 -0700246 SkASSERT(rowsDecoded <= info.height());
scroggo8e6c7ad2016-09-16 08:20:38 -0700247 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
scroggoff9f7bb2016-10-10 11:35:01 -0700248 info.height(), rowsDecoded);
Leon Scroggins III674a1842017-07-06 12:26:09 -0400249 return incResult;
Leon Scroggins III32526b52018-10-31 15:00:16 -0400250 } else if (startResult == SkCodec::kIncompleteInput
251 || startResult == SkCodec::kErrorInInput) {
252 return SkCodec::kInvalidInput;
scroggo8e6c7ad2016-09-16 08:20:38 -0700253 } else if (startResult != SkCodec::kUnimplemented) {
254 return startResult;
255 } // kUnimplemented means use the old method.
256 }
257
msarett3d9d7a72015-10-21 10:27:10 -0700258 // Start the scanline decode.
Leon Scroggins1340dbd2020-11-09 14:18:12 -0500259 AndroidOptions sampledOptions = options;
260 if (options.fSubset) {
261 sampledOptions.fSubset = &subset;
262 }
scroggo8e6c7ad2016-09-16 08:20:38 -0700263 SkCodec::Result result = this->codec()->startScanlineDecode(nativeInfo,
Leon Scroggins571b30f2017-07-11 17:35:31 +0000264 &sampledOptions);
Leon Scroggins III32526b52018-10-31 15:00:16 -0400265 if (SkCodec::kIncompleteInput == result || SkCodec::kErrorInInput == result) {
266 return SkCodec::kInvalidInput;
267 } else if (SkCodec::kSuccess != result) {
emmaleer8f4ba762015-08-14 07:44:46 -0700268 return result;
269 }
emmaleer8f4ba762015-08-14 07:44:46 -0700270
msarett90c4d5f2015-12-10 13:09:24 -0800271 SkSampler* sampler = this->codec()->getSampler(true);
scroggoe7fc14b2015-10-02 13:14:46 -0700272 if (!sampler) {
Leon Scroggins III9eb78742020-03-02 13:01:08 -0500273 return SkCodec::kInternalError;
scroggoe7fc14b2015-10-02 13:14:46 -0700274 }
275
msarett3d9d7a72015-10-21 10:27:10 -0700276 if (sampler->setSampleX(sampleX) != info.width()) {
277 return SkCodec::kInvalidScale;
278 }
279 if (get_scaled_dimension(subsetHeight, sampleY) != info.height()) {
280 return SkCodec::kInvalidScale;
scroggoe7fc14b2015-10-02 13:14:46 -0700281 }
282
msarett90c4d5f2015-12-10 13:09:24 -0800283 switch(this->codec()->getScanlineOrder()) {
scroggo46c57472015-09-30 08:57:13 -0700284 case SkCodec::kTopDown_SkScanlineOrder: {
msarett90c4d5f2015-12-10 13:09:24 -0800285 if (!this->codec()->skipScanlines(startY)) {
286 this->codec()->fillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
msarett3d9d7a72015-10-21 10:27:10 -0700287 dstHeight, 0);
288 return SkCodec::kIncompleteInput;
msarett5406d6f2015-08-31 06:55:13 -0700289 }
msarett3d9d7a72015-10-21 10:27:10 -0700290 void* pixelPtr = pixels;
msarett5406d6f2015-08-31 06:55:13 -0700291 for (int y = 0; y < dstHeight; y++) {
msarett90c4d5f2015-12-10 13:09:24 -0800292 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
293 this->codec()->fillIncompleteImage(info, pixels, rowBytes,
294 options.fZeroInitialized, dstHeight, y + 1);
msarett3d9d7a72015-10-21 10:27:10 -0700295 return SkCodec::kIncompleteInput;
msarett5406d6f2015-08-31 06:55:13 -0700296 }
msarett691ad762015-11-05 11:19:29 -0800297 if (y < dstHeight - 1) {
msarett90c4d5f2015-12-10 13:09:24 -0800298 if (!this->codec()->skipScanlines(sampleY - 1)) {
299 this->codec()->fillIncompleteImage(info, pixels, rowBytes,
msarett691ad762015-11-05 11:19:29 -0800300 options.fZeroInitialized, dstHeight, y + 1);
301 return SkCodec::kIncompleteInput;
302 }
msarett5406d6f2015-08-31 06:55:13 -0700303 }
msarett3d9d7a72015-10-21 10:27:10 -0700304 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
msarett5406d6f2015-08-31 06:55:13 -0700305 }
msarett3d9d7a72015-10-21 10:27:10 -0700306 return SkCodec::kSuccess;
emmaleer8f4ba762015-08-14 07:44:46 -0700307 }
msarett887e18e2015-11-17 08:46:02 -0800308 case SkCodec::kBottomUp_SkScanlineOrder: {
msarett5af4e0b2015-11-17 11:18:03 -0800309 // Note that these modes do not support subsetting.
msarett887e18e2015-11-17 08:46:02 -0800310 SkASSERT(0 == subsetY && nativeSize.height() == subsetHeight);
311 int y;
312 for (y = 0; y < nativeSize.height(); y++) {
msarett90c4d5f2015-12-10 13:09:24 -0800313 int srcY = this->codec()->nextScanline();
msarett887e18e2015-11-17 08:46:02 -0800314 if (is_coord_necessary(srcY, sampleY, dstHeight)) {
315 void* pixelPtr = SkTAddOffset<void>(pixels,
316 rowBytes * get_dst_coord(srcY, sampleY));
msarett90c4d5f2015-12-10 13:09:24 -0800317 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
msarett887e18e2015-11-17 08:46:02 -0800318 break;
319 }
320 } else {
msarett90c4d5f2015-12-10 13:09:24 -0800321 if (!this->codec()->skipScanlines(1)) {
msarett887e18e2015-11-17 08:46:02 -0800322 break;
323 }
324 }
325 }
326
327 if (nativeSize.height() == y) {
328 return SkCodec::kSuccess;
329 }
330
msarett90c4d5f2015-12-10 13:09:24 -0800331 // We handle filling uninitialized memory here instead of using this->codec().
332 // this->codec() does not know that we are sampling.
msarett887e18e2015-11-17 08:46:02 -0800333 const SkImageInfo fillInfo = info.makeWH(info.width(), 1);
334 for (; y < nativeSize.height(); y++) {
msarett90c4d5f2015-12-10 13:09:24 -0800335 int srcY = this->codec()->outputScanline(y);
msarett887e18e2015-11-17 08:46:02 -0800336 if (!is_coord_necessary(srcY, sampleY, dstHeight)) {
337 continue;
338 }
339
340 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, sampleY));
Leon Scroggins IIIe643a9e2018-08-03 16:15:04 -0400341 SkSampler::Fill(fillInfo, rowPtr, rowBytes, options.fZeroInitialized);
msarett887e18e2015-11-17 08:46:02 -0800342 }
343 return SkCodec::kIncompleteInput;
344 }
msarett5406d6f2015-08-31 06:55:13 -0700345 default:
346 SkASSERT(false);
msarett3d9d7a72015-10-21 10:27:10 -0700347 return SkCodec::kUnimplemented;
emmaleer8f4ba762015-08-14 07:44:46 -0700348 }
emmaleer8f4ba762015-08-14 07:44:46 -0700349}