| 1 | /* | 
|---|
| 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 |  | 
|---|
| 8 | #include "include/codec/SkAndroidCodec.h" | 
|---|
| 9 | #include "include/codec/SkCodec.h" | 
|---|
| 10 | #include "include/core/SkPixmap.h" | 
|---|
| 11 | #include "src/codec/SkAndroidCodecAdapter.h" | 
|---|
| 12 | #include "src/codec/SkCodecPriv.h" | 
|---|
| 13 | #include "src/codec/SkSampledCodec.h" | 
|---|
| 14 | #include "src/core/SkPixmapPriv.h" | 
|---|
| 15 |  | 
|---|
| 16 | static bool is_valid_sample_size(int sampleSize) { | 
|---|
| 17 | // FIXME: As Leon has mentioned elsewhere, surely there is also a maximum sampleSize? | 
|---|
| 18 | return sampleSize > 0; | 
|---|
| 19 | } | 
|---|
| 20 |  | 
|---|
| 21 | /** | 
|---|
| 22 | *  Loads the gamut as a set of three points (triangle). | 
|---|
| 23 | */ | 
|---|
| 24 | static void load_gamut(SkPoint rgb[], const skcms_Matrix3x3& xyz) { | 
|---|
| 25 | // rx = rX / (rX + rY + rZ) | 
|---|
| 26 | // ry = rY / (rX + rY + rZ) | 
|---|
| 27 | // gx, gy, bx, and gy are calulcated similarly. | 
|---|
| 28 | for (int rgbIdx = 0; rgbIdx < 3; rgbIdx++) { | 
|---|
| 29 | float sum = xyz.vals[rgbIdx][0] + xyz.vals[rgbIdx][1] + xyz.vals[rgbIdx][2]; | 
|---|
| 30 | rgb[rgbIdx].fX = xyz.vals[rgbIdx][0] / sum; | 
|---|
| 31 | rgb[rgbIdx].fY = xyz.vals[rgbIdx][1] / sum; | 
|---|
| 32 | } | 
|---|
| 33 | } | 
|---|
| 34 |  | 
|---|
| 35 | /** | 
|---|
| 36 | *  Calculates the area of the triangular gamut. | 
|---|
| 37 | */ | 
|---|
| 38 | static float calculate_area(SkPoint abc[]) { | 
|---|
| 39 | SkPoint a = abc[0]; | 
|---|
| 40 | SkPoint b = abc[1]; | 
|---|
| 41 | SkPoint c = abc[2]; | 
|---|
| 42 | return 0.5f * SkTAbs(a.fX*b.fY + b.fX*c.fY - a.fX*c.fY - c.fX*b.fY - b.fX*a.fY); | 
|---|
| 43 | } | 
|---|
| 44 |  | 
|---|
| 45 | static constexpr float kSRGB_D50_GamutArea = 0.084f; | 
|---|
| 46 |  | 
|---|
| 47 | static bool is_wide_gamut(const skcms_ICCProfile& profile) { | 
|---|
| 48 | // Determine if the source image has a gamut that is wider than sRGB.  If so, we | 
|---|
| 49 | // will use P3 as the output color space to avoid clipping the gamut. | 
|---|
| 50 | if (profile.has_toXYZD50) { | 
|---|
| 51 | SkPoint rgb[3]; | 
|---|
| 52 | load_gamut(rgb, profile.toXYZD50); | 
|---|
| 53 | return calculate_area(rgb) > kSRGB_D50_GamutArea; | 
|---|
| 54 | } | 
|---|
| 55 |  | 
|---|
| 56 | return false; | 
|---|
| 57 | } | 
|---|
| 58 |  | 
|---|
| 59 | static inline SkImageInfo adjust_info(SkCodec* codec, | 
|---|
| 60 | SkAndroidCodec::ExifOrientationBehavior orientationBehavior) { | 
|---|
| 61 | auto info = codec->getInfo(); | 
|---|
| 62 | if (orientationBehavior == SkAndroidCodec::ExifOrientationBehavior::kIgnore | 
|---|
| 63 | || !SkPixmapPriv::ShouldSwapWidthHeight(codec->getOrigin())) { | 
|---|
| 64 | return info; | 
|---|
| 65 | } | 
|---|
| 66 | return SkPixmapPriv::SwapWidthHeight(info); | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | SkAndroidCodec::SkAndroidCodec(SkCodec* codec, ExifOrientationBehavior orientationBehavior) | 
|---|
| 70 | : fInfo(adjust_info(codec, orientationBehavior)) | 
|---|
| 71 | , fOrientationBehavior(orientationBehavior) | 
|---|
| 72 | , fCodec(codec) | 
|---|
| 73 | {} | 
|---|
| 74 |  | 
|---|
| 75 | SkAndroidCodec::~SkAndroidCodec() {} | 
|---|
| 76 |  | 
|---|
| 77 | std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromStream(std::unique_ptr<SkStream> stream, | 
|---|
| 78 | SkPngChunkReader* chunkReader) { | 
|---|
| 79 | auto codec = SkCodec::MakeFromStream(std::move(stream), nullptr, chunkReader); | 
|---|
| 80 | return MakeFromCodec(std::move(codec)); | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 | std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromCodec(std::unique_ptr<SkCodec> codec, | 
|---|
| 84 | ExifOrientationBehavior orientationBehavior) { | 
|---|
| 85 | if (nullptr == codec) { | 
|---|
| 86 | return nullptr; | 
|---|
| 87 | } | 
|---|
| 88 |  | 
|---|
| 89 | switch ((SkEncodedImageFormat)codec->getEncodedFormat()) { | 
|---|
| 90 | case SkEncodedImageFormat::kPNG: | 
|---|
| 91 | case SkEncodedImageFormat::kICO: | 
|---|
| 92 | case SkEncodedImageFormat::kJPEG: | 
|---|
| 93 | #ifndef SK_HAS_WUFFS_LIBRARY | 
|---|
| 94 | case SkEncodedImageFormat::kGIF: | 
|---|
| 95 | #endif | 
|---|
| 96 | case SkEncodedImageFormat::kBMP: | 
|---|
| 97 | case SkEncodedImageFormat::kWBMP: | 
|---|
| 98 | case SkEncodedImageFormat::kHEIF: | 
|---|
| 99 | return std::make_unique<SkSampledCodec>(codec.release(), orientationBehavior); | 
|---|
| 100 | #ifdef SK_HAS_WUFFS_LIBRARY | 
|---|
| 101 | case SkEncodedImageFormat::kGIF: | 
|---|
| 102 | #endif | 
|---|
| 103 | #ifdef SK_CODEC_DECODES_WEBP | 
|---|
| 104 | case SkEncodedImageFormat::kWEBP: | 
|---|
| 105 | #endif | 
|---|
| 106 | #ifdef SK_CODEC_DECODES_RAW | 
|---|
| 107 | case SkEncodedImageFormat::kDNG: | 
|---|
| 108 | #endif | 
|---|
| 109 | #if defined(SK_CODEC_DECODES_WEBP) || defined(SK_CODEC_DECODES_RAW) || defined(SK_HAS_WUFFS_LIBRARY) | 
|---|
| 110 | return std::make_unique<SkAndroidCodecAdapter>(codec.release(), orientationBehavior); | 
|---|
| 111 | #endif | 
|---|
| 112 |  | 
|---|
| 113 | default: | 
|---|
| 114 | return nullptr; | 
|---|
| 115 | } | 
|---|
| 116 | } | 
|---|
| 117 |  | 
|---|
| 118 | std::unique_ptr<SkAndroidCodec> SkAndroidCodec::MakeFromData(sk_sp<SkData> data, | 
|---|
| 119 | SkPngChunkReader* chunkReader) { | 
|---|
| 120 | if (!data) { | 
|---|
| 121 | return nullptr; | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | return MakeFromStream(SkMemoryStream::Make(std::move(data)), chunkReader); | 
|---|
| 125 | } | 
|---|
| 126 |  | 
|---|
| 127 | SkColorType SkAndroidCodec::computeOutputColorType(SkColorType requestedColorType) { | 
|---|
| 128 | bool highPrecision = fCodec->getEncodedInfo().bitsPerComponent() > 8; | 
|---|
| 129 | switch (requestedColorType) { | 
|---|
| 130 | case kARGB_4444_SkColorType: | 
|---|
| 131 | return kN32_SkColorType; | 
|---|
| 132 | case kN32_SkColorType: | 
|---|
| 133 | break; | 
|---|
| 134 | case kAlpha_8_SkColorType: | 
|---|
| 135 | // Fall through to kGray_8.  Before kGray_8_SkColorType existed, | 
|---|
| 136 | // we allowed clients to request kAlpha_8 when they wanted a | 
|---|
| 137 | // grayscale decode. | 
|---|
| 138 | case kGray_8_SkColorType: | 
|---|
| 139 | if (kGray_8_SkColorType == this->getInfo().colorType()) { | 
|---|
| 140 | return kGray_8_SkColorType; | 
|---|
| 141 | } | 
|---|
| 142 | break; | 
|---|
| 143 | case kRGB_565_SkColorType: | 
|---|
| 144 | if (kOpaque_SkAlphaType == this->getInfo().alphaType()) { | 
|---|
| 145 | return kRGB_565_SkColorType; | 
|---|
| 146 | } | 
|---|
| 147 | break; | 
|---|
| 148 | case kRGBA_F16_SkColorType: | 
|---|
| 149 | return kRGBA_F16_SkColorType; | 
|---|
| 150 | default: | 
|---|
| 151 | break; | 
|---|
| 152 | } | 
|---|
| 153 |  | 
|---|
| 154 | // F16 is the Android default for high precision images. | 
|---|
| 155 | return highPrecision ? kRGBA_F16_SkColorType : kN32_SkColorType; | 
|---|
| 156 | } | 
|---|
| 157 |  | 
|---|
| 158 | SkAlphaType SkAndroidCodec::computeOutputAlphaType(bool requestedUnpremul) { | 
|---|
| 159 | if (kOpaque_SkAlphaType == this->getInfo().alphaType()) { | 
|---|
| 160 | return kOpaque_SkAlphaType; | 
|---|
| 161 | } | 
|---|
| 162 | return requestedUnpremul ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | sk_sp<SkColorSpace> SkAndroidCodec::computeOutputColorSpace(SkColorType outputColorType, | 
|---|
| 166 | sk_sp<SkColorSpace> prefColorSpace) { | 
|---|
| 167 | switch (outputColorType) { | 
|---|
| 168 | case kRGBA_F16_SkColorType: | 
|---|
| 169 | case kRGB_565_SkColorType: | 
|---|
| 170 | case kRGBA_8888_SkColorType: | 
|---|
| 171 | case kBGRA_8888_SkColorType: { | 
|---|
| 172 | // If |prefColorSpace| is supplied, choose it. | 
|---|
| 173 | if (prefColorSpace) { | 
|---|
| 174 | return prefColorSpace; | 
|---|
| 175 | } | 
|---|
| 176 |  | 
|---|
| 177 | const skcms_ICCProfile* encodedProfile = fCodec->getEncodedInfo().profile(); | 
|---|
| 178 | if (encodedProfile) { | 
|---|
| 179 | if (auto encodedSpace = SkColorSpace::Make(*encodedProfile)) { | 
|---|
| 180 | // Leave the pixels in the encoded color space.  Color space conversion | 
|---|
| 181 | // will be handled after decode time. | 
|---|
| 182 | return encodedSpace; | 
|---|
| 183 | } | 
|---|
| 184 |  | 
|---|
| 185 | if (is_wide_gamut(*encodedProfile)) { | 
|---|
| 186 | return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDisplayP3); | 
|---|
| 187 | } | 
|---|
| 188 | } | 
|---|
| 189 |  | 
|---|
| 190 | return SkColorSpace::MakeSRGB(); | 
|---|
| 191 | } | 
|---|
| 192 | default: | 
|---|
| 193 | // Color correction not supported for kGray. | 
|---|
| 194 | return nullptr; | 
|---|
| 195 | } | 
|---|
| 196 | } | 
|---|
| 197 |  | 
|---|
| 198 | static bool supports_any_down_scale(const SkCodec* codec) { | 
|---|
| 199 | return codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP; | 
|---|
| 200 | } | 
|---|
| 201 |  | 
|---|
| 202 | // There are a variety of ways two SkISizes could be compared. This method | 
|---|
| 203 | // returns true if either dimensions of a is < that of b. | 
|---|
| 204 | // computeSampleSize also uses the opposite, which means that both | 
|---|
| 205 | // dimensions of a >= b. | 
|---|
| 206 | static inline bool smaller_than(const SkISize& a, const SkISize& b) { | 
|---|
| 207 | return a.width() < b.width() || a.height() < b.height(); | 
|---|
| 208 | } | 
|---|
| 209 |  | 
|---|
| 210 | // Both dimensions of a > that of b. | 
|---|
| 211 | static inline bool strictly_bigger_than(const SkISize& a, const SkISize& b) { | 
|---|
| 212 | return a.width() > b.width() && a.height() > b.height(); | 
|---|
| 213 | } | 
|---|
| 214 |  | 
|---|
| 215 | int SkAndroidCodec::computeSampleSize(SkISize* desiredSize) const { | 
|---|
| 216 | SkASSERT(desiredSize); | 
|---|
| 217 |  | 
|---|
| 218 | if (!desiredSize || *desiredSize == fInfo.dimensions()) { | 
|---|
| 219 | return 1; | 
|---|
| 220 | } | 
|---|
| 221 |  | 
|---|
| 222 | if (smaller_than(fInfo.dimensions(), *desiredSize)) { | 
|---|
| 223 | *desiredSize = fInfo.dimensions(); | 
|---|
| 224 | return 1; | 
|---|
| 225 | } | 
|---|
| 226 |  | 
|---|
| 227 | // Handle bad input: | 
|---|
| 228 | if (desiredSize->width() < 1 || desiredSize->height() < 1) { | 
|---|
| 229 | *desiredSize = SkISize::Make(std::max(1, desiredSize->width()), | 
|---|
| 230 | std::max(1, desiredSize->height())); | 
|---|
| 231 | } | 
|---|
| 232 |  | 
|---|
| 233 | if (supports_any_down_scale(fCodec.get())) { | 
|---|
| 234 | return 1; | 
|---|
| 235 | } | 
|---|
| 236 |  | 
|---|
| 237 | int sampleX = fInfo.width()  / desiredSize->width(); | 
|---|
| 238 | int sampleY = fInfo.height() / desiredSize->height(); | 
|---|
| 239 | int sampleSize = std::min(sampleX, sampleY); | 
|---|
| 240 | auto computedSize = this->getSampledDimensions(sampleSize); | 
|---|
| 241 | if (computedSize == *desiredSize) { | 
|---|
| 242 | return sampleSize; | 
|---|
| 243 | } | 
|---|
| 244 |  | 
|---|
| 245 | if (computedSize == fInfo.dimensions() || sampleSize == 1) { | 
|---|
| 246 | // Cannot downscale | 
|---|
| 247 | *desiredSize = computedSize; | 
|---|
| 248 | return 1; | 
|---|
| 249 | } | 
|---|
| 250 |  | 
|---|
| 251 | if (strictly_bigger_than(computedSize, *desiredSize)) { | 
|---|
| 252 | // See if there is a tighter fit. | 
|---|
| 253 | while (true) { | 
|---|
| 254 | auto smaller = this->getSampledDimensions(sampleSize + 1); | 
|---|
| 255 | if (smaller == *desiredSize) { | 
|---|
| 256 | return sampleSize + 1; | 
|---|
| 257 | } | 
|---|
| 258 | if (smaller == computedSize || smaller_than(smaller, *desiredSize)) { | 
|---|
| 259 | // Cannot get any smaller without being smaller than desired. | 
|---|
| 260 | *desiredSize = computedSize; | 
|---|
| 261 | return sampleSize; | 
|---|
| 262 | } | 
|---|
| 263 |  | 
|---|
| 264 | sampleSize++; | 
|---|
| 265 | computedSize = smaller; | 
|---|
| 266 | } | 
|---|
| 267 |  | 
|---|
| 268 | SkASSERT(false); | 
|---|
| 269 | } | 
|---|
| 270 |  | 
|---|
| 271 | if (!smaller_than(computedSize, *desiredSize)) { | 
|---|
| 272 | // This means one of the computed dimensions is equal to desired, and | 
|---|
| 273 | // the other is bigger. This is as close as we can get. | 
|---|
| 274 | *desiredSize = computedSize; | 
|---|
| 275 | return sampleSize; | 
|---|
| 276 | } | 
|---|
| 277 |  | 
|---|
| 278 | // computedSize is too small. Make it larger. | 
|---|
| 279 | while (sampleSize > 2) { | 
|---|
| 280 | auto bigger = this->getSampledDimensions(sampleSize - 1); | 
|---|
| 281 | if (bigger == *desiredSize || !smaller_than(bigger, *desiredSize)) { | 
|---|
| 282 | *desiredSize = bigger; | 
|---|
| 283 | return sampleSize - 1; | 
|---|
| 284 | } | 
|---|
| 285 | sampleSize--; | 
|---|
| 286 | } | 
|---|
| 287 |  | 
|---|
| 288 | *desiredSize = fInfo.dimensions(); | 
|---|
| 289 | return 1; | 
|---|
| 290 | } | 
|---|
| 291 |  | 
|---|
| 292 | SkISize SkAndroidCodec::getSampledDimensions(int sampleSize) const { | 
|---|
| 293 | if (!is_valid_sample_size(sampleSize)) { | 
|---|
| 294 | return {0, 0}; | 
|---|
| 295 | } | 
|---|
| 296 |  | 
|---|
| 297 | // Fast path for when we are not scaling. | 
|---|
| 298 | if (1 == sampleSize) { | 
|---|
| 299 | return fInfo.dimensions(); | 
|---|
| 300 | } | 
|---|
| 301 |  | 
|---|
| 302 | auto dims = this->onGetSampledDimensions(sampleSize); | 
|---|
| 303 | if (fOrientationBehavior == SkAndroidCodec::ExifOrientationBehavior::kIgnore | 
|---|
| 304 | || !SkPixmapPriv::ShouldSwapWidthHeight(fCodec->getOrigin())) { | 
|---|
| 305 | return dims; | 
|---|
| 306 | } | 
|---|
| 307 |  | 
|---|
| 308 | return { dims.height(), dims.width() }; | 
|---|
| 309 | } | 
|---|
| 310 |  | 
|---|
| 311 | bool SkAndroidCodec::getSupportedSubset(SkIRect* desiredSubset) const { | 
|---|
| 312 | if (!desiredSubset || !is_valid_subset(*desiredSubset, fInfo.dimensions())) { | 
|---|
| 313 | return false; | 
|---|
| 314 | } | 
|---|
| 315 |  | 
|---|
| 316 | return this->onGetSupportedSubset(desiredSubset); | 
|---|
| 317 | } | 
|---|
| 318 |  | 
|---|
| 319 | SkISize SkAndroidCodec::getSampledSubsetDimensions(int sampleSize, const SkIRect& subset) const { | 
|---|
| 320 | if (!is_valid_sample_size(sampleSize)) { | 
|---|
| 321 | return {0, 0}; | 
|---|
| 322 | } | 
|---|
| 323 |  | 
|---|
| 324 | // We require that the input subset is a subset that is supported by SkAndroidCodec. | 
|---|
| 325 | // We test this by calling getSupportedSubset() and verifying that no modifications | 
|---|
| 326 | // are made to the subset. | 
|---|
| 327 | SkIRect copySubset = subset; | 
|---|
| 328 | if (!this->getSupportedSubset(©Subset) || copySubset != subset) { | 
|---|
| 329 | return {0, 0}; | 
|---|
| 330 | } | 
|---|
| 331 |  | 
|---|
| 332 | // If the subset is the entire image, for consistency, use getSampledDimensions(). | 
|---|
| 333 | if (fInfo.dimensions() == subset.size()) { | 
|---|
| 334 | return this->getSampledDimensions(sampleSize); | 
|---|
| 335 | } | 
|---|
| 336 |  | 
|---|
| 337 | // This should perhaps call a virtual function, but currently both of our subclasses | 
|---|
| 338 | // want the same implementation. | 
|---|
| 339 | return {get_scaled_dimension(subset.width(), sampleSize), | 
|---|
| 340 | get_scaled_dimension(subset.height(), sampleSize)}; | 
|---|
| 341 | } | 
|---|
| 342 |  | 
|---|
| 343 | static bool acceptable_result(SkCodec::Result result) { | 
|---|
| 344 | switch (result) { | 
|---|
| 345 | // These results mean a partial or complete image. They should be considered | 
|---|
| 346 | // a success by SkPixmapPriv. | 
|---|
| 347 | case SkCodec::kSuccess: | 
|---|
| 348 | case SkCodec::kIncompleteInput: | 
|---|
| 349 | case SkCodec::kErrorInInput: | 
|---|
| 350 | return true; | 
|---|
| 351 | default: | 
|---|
| 352 | return false; | 
|---|
| 353 | } | 
|---|
| 354 | } | 
|---|
| 355 |  | 
|---|
| 356 | SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& requestInfo, | 
|---|
| 357 | void* requestPixels, size_t requestRowBytes, const AndroidOptions* options) { | 
|---|
| 358 | if (!requestPixels) { | 
|---|
| 359 | return SkCodec::kInvalidParameters; | 
|---|
| 360 | } | 
|---|
| 361 | if (requestRowBytes < requestInfo.minRowBytes()) { | 
|---|
| 362 | return SkCodec::kInvalidParameters; | 
|---|
| 363 | } | 
|---|
| 364 |  | 
|---|
| 365 | SkImageInfo adjustedInfo = fInfo; | 
|---|
| 366 | if (ExifOrientationBehavior::kRespect == fOrientationBehavior | 
|---|
| 367 | && SkPixmapPriv::ShouldSwapWidthHeight(fCodec->getOrigin())) { | 
|---|
| 368 | adjustedInfo = SkPixmapPriv::SwapWidthHeight(adjustedInfo); | 
|---|
| 369 | } | 
|---|
| 370 |  | 
|---|
| 371 | AndroidOptions defaultOptions; | 
|---|
| 372 | if (!options) { | 
|---|
| 373 | options = &defaultOptions; | 
|---|
| 374 | } else if (options->fSubset) { | 
|---|
| 375 | if (!is_valid_subset(*options->fSubset, adjustedInfo.dimensions())) { | 
|---|
| 376 | return SkCodec::kInvalidParameters; | 
|---|
| 377 | } | 
|---|
| 378 |  | 
|---|
| 379 | if (SkIRect::MakeSize(adjustedInfo.dimensions()) == *options->fSubset) { | 
|---|
| 380 | // The caller wants the whole thing, rather than a subset. Modify | 
|---|
| 381 | // the AndroidOptions passed to onGetAndroidPixels to not specify | 
|---|
| 382 | // a subset. | 
|---|
| 383 | defaultOptions = *options; | 
|---|
| 384 | defaultOptions.fSubset = nullptr; | 
|---|
| 385 | options = &defaultOptions; | 
|---|
| 386 | } | 
|---|
| 387 | } | 
|---|
| 388 |  | 
|---|
| 389 | if (ExifOrientationBehavior::kIgnore == fOrientationBehavior) { | 
|---|
| 390 | return this->onGetAndroidPixels(requestInfo, requestPixels, requestRowBytes, *options); | 
|---|
| 391 | } | 
|---|
| 392 |  | 
|---|
| 393 | SkCodec::Result result; | 
|---|
| 394 | auto decode = [this, options, &result](const SkPixmap& pm) { | 
|---|
| 395 | result = this->onGetAndroidPixels(pm.info(), pm.writable_addr(), pm.rowBytes(), *options); | 
|---|
| 396 | return acceptable_result(result); | 
|---|
| 397 | }; | 
|---|
| 398 |  | 
|---|
| 399 | SkPixmap dst(requestInfo, requestPixels, requestRowBytes); | 
|---|
| 400 | if (SkPixmapPriv::Orient(dst, fCodec->getOrigin(), decode)) { | 
|---|
| 401 | return result; | 
|---|
| 402 | } | 
|---|
| 403 |  | 
|---|
| 404 | // Orient returned false. If onGetAndroidPixels succeeded, then Orient failed internally. | 
|---|
| 405 | if (acceptable_result(result)) { | 
|---|
| 406 | return SkCodec::kInternalError; | 
|---|
| 407 | } | 
|---|
| 408 |  | 
|---|
| 409 | return result; | 
|---|
| 410 | } | 
|---|
| 411 |  | 
|---|
| 412 | SkCodec::Result SkAndroidCodec::getAndroidPixels(const SkImageInfo& info, void* pixels, | 
|---|
| 413 | size_t rowBytes) { | 
|---|
| 414 | return this->getAndroidPixels(info, pixels, rowBytes, nullptr); | 
|---|
| 415 | } | 
|---|
| 416 |  | 
|---|