• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "sk_ohoscodec.h"
17 #include "src/codec/SkSampler.h"
18 #include "include/codec/SkCodec.h"
19 #include "include/core/SkPixmap.h"
20 #include "src/codec/SkCodecPriv.h"
21 #include "src/codec/SkSampledCodec.h"
22 #include "src/core/SkMathPriv.h"
23 
is_valid_sample_size(int sampleSize)24 static bool is_valid_sample_size(int sampleSize)
25 {
26     return sampleSize > 0;
27 }
28 
29 static constexpr int RGB_CHANNEL_COUNT = 3;
30 static constexpr int SAMPLE_SIZE_TWO = 2;
31 static constexpr int SAMPLE_SIZE_FOUR = 4;
32 static constexpr int SAMPLE_SIZE_EIGHT = 8;
33 
load_gamut(SkPoint rgb[],int length,const skcms_Matrix3x3 & xyz)34 static void load_gamut(SkPoint rgb[], int length, const skcms_Matrix3x3& xyz)
35 {
36     // rx = rX / (rX + rY + rZ)
37     // ry = rY / (rX + rY + rZ)
38     // gx, gy, bx, and gy are calulcated similarly.
39     for (int rgbIdx = 0; rgbIdx < length; rgbIdx++) {
40         float sum = xyz.vals[rgbIdx][0] + xyz.vals[rgbIdx][1] + xyz.vals[rgbIdx][2];
41         rgb[rgbIdx].fX = xyz.vals[rgbIdx][0] / sum;
42         rgb[rgbIdx].fY = xyz.vals[rgbIdx][1] / sum;
43     }
44 }
45 
calculate_area(SkPoint abc[],int length)46 static float calculate_area(SkPoint abc[], int length)
47 {
48     SkPoint a = abc[length - 3];
49     SkPoint b = abc[length - 2];
50     SkPoint c = abc[length - 1];
51     return 0.5f * SkTAbs(a.fX*b.fY + b.fX*c.fY - a.fX*c.fY - c.fX*b.fY - b.fX*a.fY);
52 }
53 
54 static constexpr float SRGB_D50_GAMUT_AREA = 0.084f;
55 
is_wide_gamut(const skcms_ICCProfile & profile)56 static bool is_wide_gamut(const skcms_ICCProfile& profile)
57 {
58     if (profile.has_toXYZD50) {
59         SkPoint rgb[3];
60         load_gamut(rgb, RGB_CHANNEL_COUNT, profile.toXYZD50);
61         return calculate_area(rgb, RGB_CHANNEL_COUNT) > SRGB_D50_GAMUT_AREA;
62     }
63 
64     return false;
65 }
66 
supports_any_down_scale(const SkCodec * codec)67 static bool supports_any_down_scale(const SkCodec* codec)
68 {
69     return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP;
70 }
71 
smaller_than(const SkISize & a,const SkISize & b)72 static inline bool smaller_than(const SkISize& a, const SkISize& b)
73 {
74     return a.width() < b.width() || a.height() < b.height();
75 }
76 
strictly_bigger_than(const SkISize & a,const SkISize & b)77 static inline bool strictly_bigger_than(const SkISize& a, const SkISize& b)
78 {
79     return a.width() > b.width() && a.height() > b.height();
80 }
81 
SkOHOSCodec(SkCodec * codec)82 SkOHOSCodec::SkOHOSCodec(SkCodec* codec)
83     : fInfo(codec->getInfo()), fCodec(codec)
84 {}
85 
~SkOHOSCodec()86 SkOHOSCodec::~SkOHOSCodec() {}
87 
MakeFromStream(std::unique_ptr<SkStream> stream,SkPngChunkReader * chunkReader)88 std::unique_ptr<SkOHOSCodec> SkOHOSCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
89                                                          SkPngChunkReader* chunkReader)
90 {
91     auto codec = SkCodec::MakeFromStream(std::move(stream), nullptr, chunkReader);
92     return MakeFromCodec(std::move(codec));
93 }
94 
MakeFromCodec(std::unique_ptr<SkCodec> codec)95 std::unique_ptr<SkOHOSCodec> SkOHOSCodec::MakeFromCodec(std::unique_ptr<SkCodec> codec)
96 {
97     if (nullptr == codec) {
98         return nullptr;
99     }
100 
101     switch ((SkEncodedImageFormat)codec->getEncodedFormat()) {
102         case SkEncodedImageFormat::kPNG:
103         case SkEncodedImageFormat::kICO:
104         case SkEncodedImageFormat::kJPEG:
105 #ifndef SK_HAS_WUFFS_LIBRARY
106         case SkEncodedImageFormat::kGIF:
107 #endif
108         case SkEncodedImageFormat::kBMP:
109         case SkEncodedImageFormat::kWBMP:
110         case SkEncodedImageFormat::kHEIF:
111         case SkEncodedImageFormat::kAVIF:
112             return std::make_unique<SkOHOSSampledCodec>(codec.release());
113 #ifdef SK_HAS_WUFFS_LIBRARY
114         case SkEncodedImageFormat::kGIF:
115 #endif
116 #ifdef SK_CODEC_DECODES_WEBP
117         case SkEncodedImageFormat::kWEBP:
118 #endif
119 #ifdef SK_CODEC_DECODES_RAW
120         case SkEncodedImageFormat::kDNG:
121 #endif
122 #if defined(SK_CODEC_DECODES_WEBP) || defined(SK_CODEC_DECODES_RAW) || defined(SK_HAS_WUFFS_LIBRARY)
123             return std::make_unique<SkOHOSCodecAdapter>(codec.release());
124 #endif
125 
126         default:
127             return nullptr;
128     }
129 }
130 
MakeFromData(sk_sp<SkData> data,SkPngChunkReader * chunkReader)131 std::unique_ptr<SkOHOSCodec> SkOHOSCodec::MakeFromData(sk_sp<SkData> data,
132                                                        SkPngChunkReader* chunkReader)
133 {
134     if (!data) {
135         return nullptr;
136     }
137 
138     return MakeFromStream(SkMemoryStream::Make(std::move(data)), chunkReader);
139 }
140 
computeOutputColorType(SkColorType requestedColorType)141 SkColorType SkOHOSCodec::computeOutputColorType(SkColorType requestedColorType)
142 {
143     bool highPrecision = fCodec->callGetEncodedInfo().bitsPerComponent() > 8;
144     switch (requestedColorType) {
145         case kARGB_4444_SkColorType:
146             return kN32_SkColorType;
147         case kN32_SkColorType:
148             break;
149         case kAlpha_8_SkColorType:
150             // Fall through to kGray_8.  Before kGray_8_SkColorType existed,
151             // we allowed clients to request kAlpha_8 when they wanted a
152             // grayscale decode.
153         case kGray_8_SkColorType:
154             if (kGray_8_SkColorType == this->getInfo().colorType()) {
155                 return kGray_8_SkColorType;
156             }
157             break;
158         case kRGB_565_SkColorType:
159             if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
160                 return kRGB_565_SkColorType;
161             }
162             break;
163         case kRGBA_F16_SkColorType:
164             return kRGBA_F16_SkColorType;
165         default:
166             break;
167     }
168 
169     return highPrecision ? kRGBA_F16_SkColorType : kN32_SkColorType;
170 }
171 
computeOutputAlphaType(bool requestedUnpremul)172 SkAlphaType SkOHOSCodec::computeOutputAlphaType(bool requestedUnpremul)
173 {
174     if (kOpaque_SkAlphaType == this->getInfo().alphaType()) {
175         return kOpaque_SkAlphaType;
176     }
177     return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType;
178 }
179 
computeOutputColorSpace(SkColorType outputColorType,sk_sp<SkColorSpace> prefColorSpace)180 sk_sp<SkColorSpace> SkOHOSCodec::computeOutputColorSpace(SkColorType outputColorType,
181                                                          sk_sp<SkColorSpace> prefColorSpace)
182 {
183     switch (outputColorType) {
184         case kRGBA_F16_SkColorType:
185         case kRGB_565_SkColorType:
186         case kRGBA_8888_SkColorType:
187         case kBGRA_8888_SkColorType: {
188             if (prefColorSpace) {
189                 return prefColorSpace;
190             }
191 
192             const skcms_ICCProfile* encodedProfile = fCodec->callGetEncodedInfo().profile();
193             if (encodedProfile) {
194                 if (auto encodedSpace = SkColorSpace::Make(*encodedProfile)) {
195                     return encodedSpace;
196                 }
197 
198                 if (is_wide_gamut(*encodedProfile)) {
199                     return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3);
200                 }
201             }
202 
203             return SkColorSpace::MakeSRGB();
204         }
205         default:
206             return nullptr;
207     }
208 }
209 
computeSampleSize(SkISize * desiredSize) const210 int SkOHOSCodec::computeSampleSize(SkISize* desiredSize) const
211 {
212     SkASSERT(desiredSize);
213 
214     const auto origDims = fCodec->dimensions();
215     if (!desiredSize || *desiredSize == origDims) {
216         return 1;
217     }
218 
219     if (smaller_than(origDims, *desiredSize)) {
220         *desiredSize = origDims;
221         return 1;
222     }
223 
224     if (desiredSize->width() < 1 || desiredSize->height() < 1) {
225         *desiredSize = SkISize::Make(std::max(1, desiredSize->width()),
226                                      std::max(1, desiredSize->height()));
227     }
228 
229     if (supports_any_down_scale(fCodec.get())) {
230         return 1;
231     }
232 
233     int sampleX = origDims.width()  / desiredSize->width();
234     int sampleY = origDims.height() / desiredSize->height();
235     int sampleSize = std::min(sampleX, sampleY);
236     auto computedSize = this->getSampledDimensions(sampleSize);
237     if (computedSize == *desiredSize) {
238         return sampleSize;
239     }
240 
241     if (computedSize == origDims || sampleSize == 1) {
242         *desiredSize = computedSize;
243         return 1;
244     }
245 
246     if (strictly_bigger_than(computedSize, *desiredSize)) {
247         while (true) {
248             auto smaller = this->getSampledDimensions(sampleSize + 1);
249             if (smaller == *desiredSize) {
250                 return sampleSize + 1;
251             }
252             if (smaller == computedSize || smaller_than(smaller, *desiredSize)) {
253                 *desiredSize = computedSize;
254                 return sampleSize;
255             }
256 
257             sampleSize++;
258             computedSize = smaller;
259         }
260 
261         SkASSERT(false);
262     }
263 
264     if (!smaller_than(computedSize, *desiredSize)) {
265         *desiredSize = computedSize;
266         return sampleSize;
267     }
268 
269     while (sampleSize > SAMPLE_SIZE_TWO) {
270         auto bigger = this->getSampledDimensions(sampleSize - 1);
271         if (bigger == *desiredSize || !smaller_than(bigger, *desiredSize)) {
272             *desiredSize = bigger;
273             return sampleSize - 1;
274         }
275         sampleSize--;
276     }
277 
278     *desiredSize = origDims;
279     return 1;
280 }
281 
getSampledDimensions(int sampleSize) const282 SkISize SkOHOSCodec::getSampledDimensions(int sampleSize) const
283 {
284     if (!is_valid_sample_size(sampleSize)) {
285         return {0, 0};
286     }
287 
288     if (1 == sampleSize) {
289         return fCodec->dimensions();
290     }
291 
292     return this->onGetSampledDimensions(sampleSize);
293 }
294 
getSupportedSubset(SkIRect * desiredSubset) const295 bool SkOHOSCodec::getSupportedSubset(SkIRect* desiredSubset) const
296 {
297     if (!desiredSubset || !is_valid_subset(*desiredSubset, fCodec->dimensions())) {
298         return false;
299     }
300 
301     return this->onGetSupportedSubset(desiredSubset);
302 }
303 
getSampledSubsetDimensions(int sampleSize,const SkIRect & subset) const304 SkISize SkOHOSCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const
305 {
306     if (!is_valid_sample_size(sampleSize)) {
307         return {0, 0};
308     }
309 
310     SkIRect copySubset = subset;
311     if (!this->getSupportedSubset(&copySubset) || copySubset != subset) {
312         return {0, 0};
313     }
314 
315     if (fCodec->dimensions() == subset.size()) {
316         return this->getSampledDimensions(sampleSize);
317     }
318 
319     return {get_scaled_dimension(subset.width(), sampleSize),
320             get_scaled_dimension(subset.height(), sampleSize)};
321 }
322 
getOHOSPixels(const SkImageInfo & requestInfo,void * requestPixels,size_t requestRowBytes,const OHOSOptions * options)323 SkCodec::Result SkOHOSCodec::getOHOSPixels(const SkImageInfo& requestInfo,
324     void* requestPixels, size_t requestRowBytes, const OHOSOptions* options)
325 {
326     if (!requestPixels) {
327         return SkCodec::kInvalidParameters;
328     }
329     if (requestRowBytes < requestInfo.minRowBytes()) {
330         return SkCodec::kInvalidParameters;
331     }
332 
333     OHOSOptions defaultOptions;
334     if (!options) {
335         options = &defaultOptions;
336     } else {
337         if (options->fSubset) {
338             if (!is_valid_subset(*options->fSubset, fCodec->dimensions())) {
339                 return SkCodec::kInvalidParameters;
340             }
341 
342             if (SkIRect::MakeSize(fCodec->dimensions()) == *options->fSubset) {
343                 defaultOptions = *options;
344                 defaultOptions.fSubset = nullptr;
345                 options = &defaultOptions;
346             }
347         }
348     }
349     auto getPixelsFn = [&](const SkImageInfo& info, void* pixels, size_t rowBytes,
350                            const SkCodec::Options& opts, int requiredFrame
351                            ) -> SkCodec::Result {
352         SkOHOSCodec::OHOSOptions prevFrameOptions(
353             reinterpret_cast<const SkOHOSCodec::OHOSOptions&>(opts));
354         prevFrameOptions.fFrameIndex = requiredFrame;
355         return this->getOHOSPixels(info, pixels, rowBytes, &prevFrameOptions);
356     };
357     auto result = fCodec->callHandleFrameIndex(requestInfo, requestPixels, requestRowBytes,
358         *options, getPixelsFn);
359     if (result != SkCodec::kSuccess) {
360         return result;
361     }
362 
363     return this->onGetOHOSPixels(requestInfo, requestPixels, requestRowBytes, *options);
364 }
365 
getOHOSPixels(const SkImageInfo & info,void * pixels,size_t rowBytes)366 SkCodec::Result SkOHOSCodec::getOHOSPixels(const SkImageInfo& info, void* pixels,
367     size_t rowBytes)
368 {
369     return this->getOHOSPixels(info, pixels, rowBytes, nullptr);
370 }
371 
SkOHOSCodecAdapter(SkCodec * codec)372 SkOHOSCodecAdapter::SkOHOSCodecAdapter(SkCodec* codec)
373     : INHERITED(codec)
374 {}
375 
onGetSampledDimensions(int sampleSize) const376 SkISize SkOHOSCodecAdapter::onGetSampledDimensions(int sampleSize) const
377 {
378     float scale = get_scale_from_sample_size(sampleSize);
379     return this->codec()->getScaledDimensions(scale);
380 }
381 
onGetSupportedSubset(SkIRect * desiredSubset) const382 bool SkOHOSCodecAdapter::onGetSupportedSubset(SkIRect* desiredSubset) const
383 {
384     return this->codec()->getValidSubset(desiredSubset);
385 }
386 
onGetOHOSPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const OHOSOptions & options)387 SkCodec::Result SkOHOSCodecAdapter::onGetOHOSPixels(const SkImageInfo& info, void* pixels,
388     size_t rowBytes, const OHOSOptions& options)
389 {
390     return this->codec()->getPixels(info, pixels, rowBytes, &options);
391 }
392 
SkOHOSSampledCodec(SkCodec * codec)393 SkOHOSSampledCodec::SkOHOSSampledCodec(SkCodec* codec)
394     : INHERITED(codec)
395 {}
396 
accountForNativeScaling(int * sampleSizePtr,int * nativeSampleSize) const397 SkISize SkOHOSSampledCodec::accountForNativeScaling(int* sampleSizePtr, int* nativeSampleSize) const
398 {
399     SkISize preSampledSize = this->codec()->dimensions();
400     int sampleSize = *sampleSizePtr;
401     SkASSERT(sampleSize > 1);
402 
403     if (nativeSampleSize) {
404         *nativeSampleSize = 1;
405     }
406 
407     if (this->codec()->getEncodedFormat() == SkEncodedImageFormat::kJPEG) {
408         switch (sampleSize) {
409             case SAMPLE_SIZE_TWO:
410             case SAMPLE_SIZE_FOUR:
411             case SAMPLE_SIZE_EIGHT:
412                 *sampleSizePtr = 1;
413                 return this->codec()->getScaledDimensions(get_scale_from_sample_size(sampleSize));
414             default:
415                 break;
416         }
417 
418         int ohosRemainder;
419         const int ohosSampleSizes[] = { 8, 4, 2 };
420         for (int supportedSampleSize : ohosSampleSizes) {
421             int ohosActualSampleSize;
422             SkTDivMod(sampleSize, supportedSampleSize, &ohosActualSampleSize, &ohosRemainder);
423             if (0 == ohosRemainder) {
424                 float scale = get_scale_from_sample_size(supportedSampleSize);
425 
426                 preSampledSize = this->codec()->getScaledDimensions(scale);
427 
428                 *sampleSizePtr = ohosActualSampleSize;
429                 if (nativeSampleSize) {
430                     *nativeSampleSize = supportedSampleSize;
431                 }
432                 break;
433             }
434         }
435     }
436 
437     return preSampledSize;
438 }
439 
onGetSampledDimensions(int sampleSize) const440 SkISize SkOHOSSampledCodec::onGetSampledDimensions(int sampleSize) const
441 {
442     const SkISize size = this->accountForNativeScaling(&sampleSize);
443     return SkISize::Make(get_scaled_dimension(size.width(), sampleSize),
444                          get_scaled_dimension(size.height(), sampleSize));
445 }
446 
onGetOHOSPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,const OHOSOptions & options)447 SkCodec::Result SkOHOSSampledCodec::onGetOHOSPixels(const SkImageInfo& info, void* pixels,
448     size_t rowBytes, const OHOSOptions& options)
449 {
450     const SkIRect* subset = options.fSubset;
451     if (!subset || subset->size() == this->codec()->dimensions()) {
452         if (this->codec()->callDimensionsSupported(info.dimensions())) {
453             return this->codec()->getPixels(info, pixels, rowBytes, &options);
454         }
455 
456         return this->sampledDecode(info, pixels, rowBytes, options);
457     }
458 
459     int sampleSize = options.fSampleSize;
460     SkISize scaledSize = this->getSampledDimensions(sampleSize);
461     if (!this->codec()->callDimensionsSupported(scaledSize)) {
462         return this->sampledDecode(info, pixels, rowBytes, options);
463     }
464 
465     int scaledSubsetX = subset->x() / sampleSize;
466     int scaledSubsetY = subset->y() / sampleSize;
467     int scaledSubsetWidth = info.width();
468     int subsetScaledHeight = info.height();
469 
470     const SkImageInfo scaledInfo = info.makeDimensions(scaledSize);
471 
472     OHOSOptions subsetOptions = options;
473     {
474         SkIRect incrementalSubset = SkIRect::MakeXYWH(scaledSubsetX, scaledSubsetY,
475                                                       scaledSubsetWidth, subsetScaledHeight);
476         subsetOptions.fSubset = &incrementalSubset;
477         const SkCodec::Result startIncrementalResult = this->codec()->startIncrementalDecode(
478             scaledInfo, pixels, rowBytes, &subsetOptions);
479         if (SkCodec::kSuccess == startIncrementalResult) {
480             int decodedRows = 0;
481             const SkCodec::Result incrementalResult = this->codec()->incrementalDecode(&decodedRows);
482             if (incrementalResult == SkCodec::kSuccess) {
483                 return SkCodec::kSuccess;
484             }
485             SkASSERT(incrementalResult == SkCodec::kIncompleteInput || incrementalResult == SkCodec::kErrorInInput);
486 
487             this->codec()->callFillIncompleteImage(scaledInfo, pixels, rowBytes,
488                 options.fZeroInitialized, subsetScaledHeight, decodedRows);
489             return incrementalResult;
490         } else if (startIncrementalResult != SkCodec::kUnimplemented) {
491             return startIncrementalResult;
492         }
493     }
494 
495     SkIRect scanlineSubset = SkIRect::MakeXYWH(scaledSubsetX, 0, scaledSubsetWidth,
496         scaledSize.height());
497     subsetOptions.fSubset = &scanlineSubset;
498 
499     SkCodec::Result result = this->codec()->startScanlineDecode(scaledInfo,
500         &subsetOptions);
501     if (SkCodec::kSuccess != result) {
502         return result;
503     }
504 
505     SkASSERT(this->codec()->getScanlineOrder() == SkCodec::kTopDown_SkScanlineOrder);
506     if (!this->codec()->skipScanlines(scaledSubsetY)) {
507         this->codec()->callFillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
508             subsetScaledHeight, 0);
509         return SkCodec::kIncompleteInput;
510     }
511 
512     int decodedLines = this->codec()->getScanlines(pixels, subsetScaledHeight, rowBytes);
513     if (decodedLines != subsetScaledHeight) {
514         return SkCodec::kIncompleteInput;
515     }
516     return SkCodec::kSuccess;
517 }
518 
sampledDecode(const SkImageInfo & info,void * pixels,size_t rowBytes,const OHOSOptions & options)519 SkCodec::Result SkOHOSSampledCodec::sampledDecode(const SkImageInfo& info, void* pixels,
520     size_t rowBytes, const OHOSOptions& options)
521 {
522     SkASSERT(options.fSampleSize > 1);
523 
524     int sampleSize = options.fSampleSize;
525     int nativeSampleSize;
526     SkISize ohosNativeSize = this->accountForNativeScaling(&sampleSize, &nativeSampleSize);
527 
528     SkIRect subset;
529     int ohosSubsetY = 0;
530     int ohosSubsetWidth = ohosNativeSize.width();
531     int ohosSubsetHeight = ohosNativeSize.height();
532     if (options.fSubset) {
533         const SkIRect* subsetPtr = options.fSubset;
534 
535         const int subsetX = subsetPtr->x() / nativeSampleSize;
536         ohosSubsetY = subsetPtr->y() / nativeSampleSize;
537 
538         ohosSubsetWidth = get_scaled_dimension(subsetPtr->width(), nativeSampleSize);
539         ohosSubsetHeight = get_scaled_dimension(subsetPtr->height(), nativeSampleSize);
540 
541         subset.setXYWH(subsetX, 0, ohosSubsetWidth, ohosNativeSize.height());
542     }
543 
544     const int ohosSampleX = ohosSubsetWidth / info.width();
545     const int ohosSampleY = ohosSubsetHeight / info.height();
546 
547     const int ohosSamplingOffsetY = get_start_coord(ohosSampleY);
548     const int ohosStartY = ohosSamplingOffsetY + ohosSubsetY;
549     const int ohosDstHeight = info.height();
550 
551     const SkImageInfo nativeInfo = info.makeDimensions(ohosNativeSize);
552 
553     {
554         OHOSOptions incrementalOptions = options;
555         SkIRect incrementalSubset;
556         if (options.fSubset) {
557             incrementalSubset.fTop     = ohosSubsetY;
558             incrementalSubset.fBottom  = ohosSubsetY + ohosSubsetHeight;
559             incrementalSubset.fLeft    = subset.fLeft;
560             incrementalSubset.fRight   = subset.fRight;
561             incrementalOptions.fSubset = &incrementalSubset;
562         }
563         const SkCodec::Result startResult = this->codec()->startIncrementalDecode(nativeInfo,
564             pixels, rowBytes, &incrementalOptions);
565         if (SkCodec::kSuccess == startResult) {
566             SkSampler* ohosSampler = this->codec()->callGetSampler(true);
567             if (!ohosSampler) {
568                 return SkCodec::kUnimplemented;
569             }
570 
571             if (ohosSampler->setSampleX(ohosSampleX) != info.width()) {
572                 return SkCodec::kInvalidScale;
573             }
574             if (get_scaled_dimension(ohosSubsetHeight, ohosSampleY) != info.height()) {
575                 return SkCodec::kInvalidScale;
576             }
577 
578             ohosSampler->setSampleY(ohosSampleY);
579 
580             int rowsDecoded = 0;
581             const SkCodec::Result incrementalResult = this->codec()->incrementalDecode(&rowsDecoded);
582             if (incrementalResult == SkCodec::kSuccess) {
583                 return SkCodec::kSuccess;
584             }
585             SkASSERT(incrementalResult == SkCodec::kIncompleteInput || incrementalResult == SkCodec::kErrorInInput);
586 
587             SkASSERT(rowsDecoded <= info.height());
588             this->codec()->callFillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
589                                                    info.height(), rowsDecoded);
590             return incrementalResult;
591         } else if (startResult == SkCodec::kIncompleteInput
592                 || startResult == SkCodec::kErrorInInput) {
593             return SkCodec::kInvalidInput;
594         } else if (startResult != SkCodec::kUnimplemented) {
595             return startResult;
596         }
597     }
598 
599     OHOSOptions ohosSampledOptions = options;
600     if (options.fSubset) {
601         ohosSampledOptions.fSubset = &subset;
602     }
603     SkCodec::Result startScanlineResult = this->codec()->startScanlineDecode(nativeInfo,
604         &ohosSampledOptions);
605     if (SkCodec::kIncompleteInput == startScanlineResult || SkCodec::kErrorInInput == startScanlineResult) {
606         return SkCodec::kInvalidInput;
607     } else if (SkCodec::kSuccess != startScanlineResult) {
608         return startScanlineResult;
609     }
610 
611     SkSampler* ohosSampler = this->codec()->callGetSampler(true);
612     if (!ohosSampler) {
613         return SkCodec::kInternalError;
614     }
615 
616     if (ohosSampler->setSampleX(ohosSampleX) != info.width()) {
617         return SkCodec::kInvalidScale;
618     }
619     if (get_scaled_dimension(ohosSubsetHeight, ohosSampleY) != info.height()) {
620         return SkCodec::kInvalidScale;
621     }
622 
623     switch (this->codec()->getScanlineOrder()) {
624         case SkCodec::kTopDown_SkScanlineOrder: {
625             if (!this->codec()->skipScanlines(ohosStartY)) {
626                 this->codec()->callFillIncompleteImage(info, pixels, rowBytes, options.fZeroInitialized,
627                     ohosDstHeight, 0);
628                 return SkCodec::kIncompleteInput;
629             }
630             void* pixelPtr = pixels;
631             for (int y = 0; y < ohosDstHeight; y++) {
632                 if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
633                     this->codec()->callFillIncompleteImage(info, pixels, rowBytes,
634                         options.fZeroInitialized, ohosDstHeight, y + 1);
635                     return SkCodec::kIncompleteInput;
636                 }
637                 if (y < ohosDstHeight - 1) {
638                     if (!this->codec()->skipScanlines(ohosSampleY - 1)) {
639                         this->codec()->callFillIncompleteImage(info, pixels, rowBytes,
640                             options.fZeroInitialized, ohosDstHeight, y + 1);
641                         return SkCodec::kIncompleteInput;
642                     }
643                 }
644                 pixelPtr = SkTAddOffset<void>(pixelPtr, rowBytes);
645             }
646             return SkCodec::kSuccess;
647         }
648         case SkCodec::kBottomUp_SkScanlineOrder: {
649             SkASSERT(0 == ohosSubsetY && ohosNativeSize.height() == ohosSubsetHeight);
650             int y;
651             for (y = 0; y < ohosNativeSize.height(); y++) {
652                 int srcY = this->codec()->nextScanline();
653                 if (is_coord_necessary(srcY, ohosSampleY, ohosDstHeight)) {
654                     void* pixelPtr = SkTAddOffset<void>(pixels,
655                         rowBytes * get_dst_coord(srcY, ohosSampleY));
656                     if (1 != this->codec()->getScanlines(pixelPtr, 1, rowBytes)) {
657                         break;
658                     }
659                 } else {
660                     if (!this->codec()->skipScanlines(1)) {
661                         break;
662                     }
663                 }
664             }
665 
666             if (ohosNativeSize.height() == y) {
667                 return SkCodec::kSuccess;
668             }
669 
670             const SkImageInfo ohosFillInfo = info.makeWH(info.width(), 1);
671             for (; y < ohosNativeSize.height(); y++) {
672                 int srcY = this->codec()->outputScanline(y);
673                 if (!is_coord_necessary(srcY, ohosSampleY, ohosDstHeight)) {
674                     continue;
675                 }
676 
677                 void* rowPtr = SkTAddOffset<void>(pixels, rowBytes * get_dst_coord(srcY, ohosSampleY));
678                 SkSampler::Fill(ohosFillInfo, rowPtr, rowBytes, options.fZeroInitialized);
679             }
680             return SkCodec::kIncompleteInput;
681         }
682         default:
683             SkASSERT(false);
684             return SkCodec::kUnimplemented;
685     }
686 }