xref: /aosp_15_r20/external/skia/src/codec/SkPngCodecBase.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2024 Google LLC.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkPngCodecBase.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
11*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
12*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
13*c8dee2aaSAndroid Build Coastguard Worker 
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkCodec.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/codec/SkEncodedImageFormat.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkEncodedInfo.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkSpan_impl.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skcms/skcms.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkCodecPriv.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkColorPalette.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/codec/SkSwizzler.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMemset.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkSwizzlePriv.h"
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker namespace {
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
35*c8dee2aaSAndroid Build Coastguard Worker 
needs_premul(SkAlphaType dstAT,SkEncodedInfo::Alpha encodedAlpha)36*c8dee2aaSAndroid Build Coastguard Worker inline bool needs_premul(SkAlphaType dstAT, SkEncodedInfo::Alpha encodedAlpha) {
37*c8dee2aaSAndroid Build Coastguard Worker     return kPremul_SkAlphaType == dstAT && SkEncodedInfo::kUnpremul_Alpha == encodedAlpha;
38*c8dee2aaSAndroid Build Coastguard Worker }
39*c8dee2aaSAndroid Build Coastguard Worker 
ToPixelFormat(const SkEncodedInfo & info)40*c8dee2aaSAndroid Build Coastguard Worker skcms_PixelFormat ToPixelFormat(const SkEncodedInfo& info) {
41*c8dee2aaSAndroid Build Coastguard Worker     // We use kRGB and kRGBA formats because color PNGs are always RGB or RGBA.
42*c8dee2aaSAndroid Build Coastguard Worker     if (16 == info.bitsPerComponent()) {
43*c8dee2aaSAndroid Build Coastguard Worker         if (SkEncodedInfo::kRGBA_Color == info.color()) {
44*c8dee2aaSAndroid Build Coastguard Worker             return skcms_PixelFormat_RGBA_16161616BE;
45*c8dee2aaSAndroid Build Coastguard Worker         } else if (SkEncodedInfo::kRGB_Color == info.color()) {
46*c8dee2aaSAndroid Build Coastguard Worker             return skcms_PixelFormat_RGB_161616BE;
47*c8dee2aaSAndroid Build Coastguard Worker         }
48*c8dee2aaSAndroid Build Coastguard Worker     } else if (SkEncodedInfo::kGray_Color == info.color()) {
49*c8dee2aaSAndroid Build Coastguard Worker         return skcms_PixelFormat_G_8;
50*c8dee2aaSAndroid Build Coastguard Worker     }
51*c8dee2aaSAndroid Build Coastguard Worker 
52*c8dee2aaSAndroid Build Coastguard Worker     return skcms_PixelFormat_RGBA_8888;
53*c8dee2aaSAndroid Build Coastguard Worker }
54*c8dee2aaSAndroid Build Coastguard Worker 
55*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
56*c8dee2aaSAndroid Build Coastguard Worker 
57*c8dee2aaSAndroid Build Coastguard Worker SkPngCodecBase::~SkPngCodecBase() = default;
58*c8dee2aaSAndroid Build Coastguard Worker 
59*c8dee2aaSAndroid Build Coastguard Worker // static
isCompatibleColorProfileAndType(const SkEncodedInfo::ICCProfile * profile,SkEncodedInfo::Color color)60*c8dee2aaSAndroid Build Coastguard Worker bool SkPngCodecBase::isCompatibleColorProfileAndType(const SkEncodedInfo::ICCProfile* profile,
61*c8dee2aaSAndroid Build Coastguard Worker                                                      SkEncodedInfo::Color color) {
62*c8dee2aaSAndroid Build Coastguard Worker     if (profile) {
63*c8dee2aaSAndroid Build Coastguard Worker         switch (profile->profile()->data_color_space) {
64*c8dee2aaSAndroid Build Coastguard Worker             case skcms_Signature_CMYK:
65*c8dee2aaSAndroid Build Coastguard Worker                 return false;
66*c8dee2aaSAndroid Build Coastguard Worker             case skcms_Signature_Gray:
67*c8dee2aaSAndroid Build Coastguard Worker                 if (SkEncodedInfo::kGray_Color != color &&
68*c8dee2aaSAndroid Build Coastguard Worker                     SkEncodedInfo::kGrayAlpha_Color != color) {
69*c8dee2aaSAndroid Build Coastguard Worker                     return false;
70*c8dee2aaSAndroid Build Coastguard Worker                 }
71*c8dee2aaSAndroid Build Coastguard Worker                 break;
72*c8dee2aaSAndroid Build Coastguard Worker             default:
73*c8dee2aaSAndroid Build Coastguard Worker                 break;
74*c8dee2aaSAndroid Build Coastguard Worker         }
75*c8dee2aaSAndroid Build Coastguard Worker     }
76*c8dee2aaSAndroid Build Coastguard Worker 
77*c8dee2aaSAndroid Build Coastguard Worker     return true;
78*c8dee2aaSAndroid Build Coastguard Worker }
79*c8dee2aaSAndroid Build Coastguard Worker 
SkPngCodecBase(SkEncodedInfo && encodedInfo,std::unique_ptr<SkStream> stream)80*c8dee2aaSAndroid Build Coastguard Worker SkPngCodecBase::SkPngCodecBase(SkEncodedInfo&& encodedInfo, std::unique_ptr<SkStream> stream)
81*c8dee2aaSAndroid Build Coastguard Worker         : SkCodec(std::move(encodedInfo), ToPixelFormat(encodedInfo), std::move(stream)) {}
82*c8dee2aaSAndroid Build Coastguard Worker 
onGetEncodedFormat() const83*c8dee2aaSAndroid Build Coastguard Worker SkEncodedImageFormat SkPngCodecBase::onGetEncodedFormat() const {
84*c8dee2aaSAndroid Build Coastguard Worker     return SkEncodedImageFormat::kPNG;
85*c8dee2aaSAndroid Build Coastguard Worker }
86*c8dee2aaSAndroid Build Coastguard Worker 
initializeXforms(const SkImageInfo & dstInfo,const Options & options,int frameWidth)87*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Result SkPngCodecBase::initializeXforms(const SkImageInfo& dstInfo,
88*c8dee2aaSAndroid Build Coastguard Worker                                                  const Options& options,
89*c8dee2aaSAndroid Build Coastguard Worker                                                  int frameWidth) {
90*c8dee2aaSAndroid Build Coastguard Worker     if (frameWidth != dstInfo.width() && options.fSubset) {
91*c8dee2aaSAndroid Build Coastguard Worker         return kInvalidParameters;
92*c8dee2aaSAndroid Build Coastguard Worker     }
93*c8dee2aaSAndroid Build Coastguard Worker     fXformWidth = frameWidth;
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker     {
96*c8dee2aaSAndroid Build Coastguard Worker         size_t encodedBitsPerPixel = static_cast<size_t>(getEncodedInfo().bitsPerPixel());
97*c8dee2aaSAndroid Build Coastguard Worker 
98*c8dee2aaSAndroid Build Coastguard Worker         // We assume that `frameWidth` and `bitsPerPixel` have been already sanitized
99*c8dee2aaSAndroid Build Coastguard Worker         // earlier (and that the multiplication and addition below won't overflow).
100*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(0 < frameWidth);
101*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(frameWidth < 0xFFFFFF);
102*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(encodedBitsPerPixel < 128);
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker         size_t encodedBitsPerRow = static_cast<size_t>(frameWidth) * encodedBitsPerPixel;
105*c8dee2aaSAndroid Build Coastguard Worker         fEncodedRowBytes = (encodedBitsPerRow + 7) / 8;  // Round up to the next byte.
106*c8dee2aaSAndroid Build Coastguard Worker 
107*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_DEBUG)
108*c8dee2aaSAndroid Build Coastguard Worker         size_t dstBytesPerPixel = dstInfo.bytesPerPixel();
109*c8dee2aaSAndroid Build Coastguard Worker         fDstRowBytes = static_cast<size_t>(frameWidth) * dstBytesPerPixel;
110*c8dee2aaSAndroid Build Coastguard Worker #endif
111*c8dee2aaSAndroid Build Coastguard Worker     }
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker     // Reset fSwizzler and this->colorXform().  We can't do this in onRewind() because the
114*c8dee2aaSAndroid Build Coastguard Worker     // interlaced scanline decoder may need to rewind.
115*c8dee2aaSAndroid Build Coastguard Worker     fSwizzler.reset(nullptr);
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker     // If skcms directly supports the encoded PNG format, we should skip format
118*c8dee2aaSAndroid Build Coastguard Worker     // conversion in the swizzler (or skip swizzling altogether).
119*c8dee2aaSAndroid Build Coastguard Worker     bool skipFormatConversion = false;
120*c8dee2aaSAndroid Build Coastguard Worker     switch (this->getEncodedInfo().color()) {
121*c8dee2aaSAndroid Build Coastguard Worker         case SkEncodedInfo::kRGB_Color:
122*c8dee2aaSAndroid Build Coastguard Worker             if (this->getEncodedInfo().bitsPerComponent() != 16) {
123*c8dee2aaSAndroid Build Coastguard Worker                 break;
124*c8dee2aaSAndroid Build Coastguard Worker             }
125*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
126*c8dee2aaSAndroid Build Coastguard Worker         case SkEncodedInfo::kRGBA_Color:
127*c8dee2aaSAndroid Build Coastguard Worker         case SkEncodedInfo::kGray_Color:
128*c8dee2aaSAndroid Build Coastguard Worker             skipFormatConversion = this->colorXform();
129*c8dee2aaSAndroid Build Coastguard Worker             break;
130*c8dee2aaSAndroid Build Coastguard Worker         default:
131*c8dee2aaSAndroid Build Coastguard Worker             break;
132*c8dee2aaSAndroid Build Coastguard Worker     }
133*c8dee2aaSAndroid Build Coastguard Worker 
134*c8dee2aaSAndroid Build Coastguard Worker     if (skipFormatConversion && !options.fSubset) {
135*c8dee2aaSAndroid Build Coastguard Worker         fXformMode = kColorOnly_XformMode;
136*c8dee2aaSAndroid Build Coastguard Worker     } else {
137*c8dee2aaSAndroid Build Coastguard Worker         if (SkEncodedInfo::kPalette_Color == this->getEncodedInfo().color()) {
138*c8dee2aaSAndroid Build Coastguard Worker             if (!this->createColorTable(dstInfo)) {
139*c8dee2aaSAndroid Build Coastguard Worker                 return kInvalidInput;
140*c8dee2aaSAndroid Build Coastguard Worker             }
141*c8dee2aaSAndroid Build Coastguard Worker         }
142*c8dee2aaSAndroid Build Coastguard Worker 
143*c8dee2aaSAndroid Build Coastguard Worker         Result result =
144*c8dee2aaSAndroid Build Coastguard Worker                 this->initializeSwizzler(dstInfo, options, skipFormatConversion, frameWidth);
145*c8dee2aaSAndroid Build Coastguard Worker         if (result != kSuccess) {
146*c8dee2aaSAndroid Build Coastguard Worker             return result;
147*c8dee2aaSAndroid Build Coastguard Worker         }
148*c8dee2aaSAndroid Build Coastguard Worker     }
149*c8dee2aaSAndroid Build Coastguard Worker 
150*c8dee2aaSAndroid Build Coastguard Worker     this->allocateStorage(dstInfo);
151*c8dee2aaSAndroid Build Coastguard Worker 
152*c8dee2aaSAndroid Build Coastguard Worker     // We can't call `initializeXformParams` here, because `swizzleWidth` may
153*c8dee2aaSAndroid Build Coastguard Worker     // change *after* `onStartIncrementalDecode`
154*c8dee2aaSAndroid Build Coastguard Worker     // (`SkSampledCodec::sampledDecode` first [transitively] calls
155*c8dee2aaSAndroid Build Coastguard Worker     // `onStartIncrementalDecode` and *then* `SkSwizzler::onSetSampleX`).
156*c8dee2aaSAndroid Build Coastguard Worker 
157*c8dee2aaSAndroid Build Coastguard Worker     return kSuccess;
158*c8dee2aaSAndroid Build Coastguard Worker }
159*c8dee2aaSAndroid Build Coastguard Worker 
initializeXformParams()160*c8dee2aaSAndroid Build Coastguard Worker void SkPngCodecBase::initializeXformParams() {
161*c8dee2aaSAndroid Build Coastguard Worker     if (fXformMode == kSwizzleColor_XformMode) {
162*c8dee2aaSAndroid Build Coastguard Worker         fXformWidth = this->swizzler()->swizzleWidth();
163*c8dee2aaSAndroid Build Coastguard Worker     }
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker 
allocateStorage(const SkImageInfo & dstInfo)166*c8dee2aaSAndroid Build Coastguard Worker void SkPngCodecBase::allocateStorage(const SkImageInfo& dstInfo) {
167*c8dee2aaSAndroid Build Coastguard Worker     switch (fXformMode) {
168*c8dee2aaSAndroid Build Coastguard Worker         case kSwizzleOnly_XformMode:
169*c8dee2aaSAndroid Build Coastguard Worker             break;
170*c8dee2aaSAndroid Build Coastguard Worker         case kColorOnly_XformMode:
171*c8dee2aaSAndroid Build Coastguard Worker             // Intentional fall through.  A swizzler hasn't been created yet, but one will
172*c8dee2aaSAndroid Build Coastguard Worker             // be created later if we are sampling.  We'll go ahead and allocate
173*c8dee2aaSAndroid Build Coastguard Worker             // enough memory to swizzle if necessary.
174*c8dee2aaSAndroid Build Coastguard Worker         case kSwizzleColor_XformMode: {
175*c8dee2aaSAndroid Build Coastguard Worker             const int bitsPerPixel = this->getEncodedInfo().bitsPerPixel();
176*c8dee2aaSAndroid Build Coastguard Worker 
177*c8dee2aaSAndroid Build Coastguard Worker             // If we have more than 8-bits (per component) of precision, we will keep that
178*c8dee2aaSAndroid Build Coastguard Worker             // extra precision.  Otherwise, we will swizzle to RGBA_8888 before transforming.
179*c8dee2aaSAndroid Build Coastguard Worker             const size_t bytesPerPixel = (bitsPerPixel > 32) ? bitsPerPixel / 8 : 4;
180*c8dee2aaSAndroid Build Coastguard Worker             const size_t colorXformBytes = dstInfo.width() * bytesPerPixel;
181*c8dee2aaSAndroid Build Coastguard Worker             fStorage.reset(colorXformBytes);
182*c8dee2aaSAndroid Build Coastguard Worker             break;
183*c8dee2aaSAndroid Build Coastguard Worker         }
184*c8dee2aaSAndroid Build Coastguard Worker     }
185*c8dee2aaSAndroid Build Coastguard Worker }
186*c8dee2aaSAndroid Build Coastguard Worker 
initializeSwizzler(const SkImageInfo & dstInfo,const Options & options,bool skipFormatConversion,int frameWidth)187*c8dee2aaSAndroid Build Coastguard Worker SkCodec::Result SkPngCodecBase::initializeSwizzler(const SkImageInfo& dstInfo,
188*c8dee2aaSAndroid Build Coastguard Worker                                                    const Options& options,
189*c8dee2aaSAndroid Build Coastguard Worker                                                    bool skipFormatConversion,
190*c8dee2aaSAndroid Build Coastguard Worker                                                    int frameWidth) {
191*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo swizzlerInfo = dstInfo;
192*c8dee2aaSAndroid Build Coastguard Worker     Options swizzlerOptions = options;
193*c8dee2aaSAndroid Build Coastguard Worker     fXformMode = kSwizzleOnly_XformMode;
194*c8dee2aaSAndroid Build Coastguard Worker     if (this->colorXform() && this->xformOnDecode()) {
195*c8dee2aaSAndroid Build Coastguard Worker         if (SkEncodedInfo::kGray_Color == this->getEncodedInfo().color()) {
196*c8dee2aaSAndroid Build Coastguard Worker             swizzlerInfo = swizzlerInfo.makeColorType(kGray_8_SkColorType);
197*c8dee2aaSAndroid Build Coastguard Worker         } else {
198*c8dee2aaSAndroid Build Coastguard Worker             swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
199*c8dee2aaSAndroid Build Coastguard Worker         }
200*c8dee2aaSAndroid Build Coastguard Worker         if (kPremul_SkAlphaType == dstInfo.alphaType()) {
201*c8dee2aaSAndroid Build Coastguard Worker             swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
202*c8dee2aaSAndroid Build Coastguard Worker         }
203*c8dee2aaSAndroid Build Coastguard Worker 
204*c8dee2aaSAndroid Build Coastguard Worker         fXformMode = kSwizzleColor_XformMode;
205*c8dee2aaSAndroid Build Coastguard Worker 
206*c8dee2aaSAndroid Build Coastguard Worker         // Here, we swizzle into temporary memory, which is not zero initialized.
207*c8dee2aaSAndroid Build Coastguard Worker         // FIXME (msarett):
208*c8dee2aaSAndroid Build Coastguard Worker         // Is this a problem?
209*c8dee2aaSAndroid Build Coastguard Worker         swizzlerOptions.fZeroInitialized = kNo_ZeroInitialized;
210*c8dee2aaSAndroid Build Coastguard Worker     }
211*c8dee2aaSAndroid Build Coastguard Worker 
212*c8dee2aaSAndroid Build Coastguard Worker     SkIRect frameRect = SkIRect::MakeWH(frameWidth, 1);
213*c8dee2aaSAndroid Build Coastguard Worker     const SkIRect* frameRectPtr = nullptr;
214*c8dee2aaSAndroid Build Coastguard Worker     if (options.fSubset) {
215*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(frameWidth == dstInfo.width());
216*c8dee2aaSAndroid Build Coastguard Worker     } else {
217*c8dee2aaSAndroid Build Coastguard Worker         frameRectPtr = &frameRect;
218*c8dee2aaSAndroid Build Coastguard Worker     }
219*c8dee2aaSAndroid Build Coastguard Worker 
220*c8dee2aaSAndroid Build Coastguard Worker     if (skipFormatConversion) {
221*c8dee2aaSAndroid Build Coastguard Worker         // We cannot skip format conversion when there is a color table.
222*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fColorTable);
223*c8dee2aaSAndroid Build Coastguard Worker         int srcBPP = 0;
224*c8dee2aaSAndroid Build Coastguard Worker         switch (this->getEncodedInfo().color()) {
225*c8dee2aaSAndroid Build Coastguard Worker             case SkEncodedInfo::kRGB_Color:
226*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(this->getEncodedInfo().bitsPerComponent() == 16);
227*c8dee2aaSAndroid Build Coastguard Worker                 srcBPP = 6;
228*c8dee2aaSAndroid Build Coastguard Worker                 break;
229*c8dee2aaSAndroid Build Coastguard Worker             case SkEncodedInfo::kRGBA_Color:
230*c8dee2aaSAndroid Build Coastguard Worker                 srcBPP = this->getEncodedInfo().bitsPerComponent() / 2;
231*c8dee2aaSAndroid Build Coastguard Worker                 break;
232*c8dee2aaSAndroid Build Coastguard Worker             case SkEncodedInfo::kGray_Color:
233*c8dee2aaSAndroid Build Coastguard Worker                 srcBPP = 1;
234*c8dee2aaSAndroid Build Coastguard Worker                 break;
235*c8dee2aaSAndroid Build Coastguard Worker             default:
236*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(false);
237*c8dee2aaSAndroid Build Coastguard Worker                 break;
238*c8dee2aaSAndroid Build Coastguard Worker         }
239*c8dee2aaSAndroid Build Coastguard Worker         fSwizzler = SkSwizzler::MakeSimple(srcBPP, swizzlerInfo, swizzlerOptions, frameRectPtr);
240*c8dee2aaSAndroid Build Coastguard Worker     } else {
241*c8dee2aaSAndroid Build Coastguard Worker         const SkPMColor* colors = get_color_ptr(fColorTable.get());
242*c8dee2aaSAndroid Build Coastguard Worker         fSwizzler = SkSwizzler::Make(
243*c8dee2aaSAndroid Build Coastguard Worker                 this->getEncodedInfo(), colors, swizzlerInfo, swizzlerOptions, frameRectPtr);
244*c8dee2aaSAndroid Build Coastguard Worker     }
245*c8dee2aaSAndroid Build Coastguard Worker 
246*c8dee2aaSAndroid Build Coastguard Worker     return !!fSwizzler ? kSuccess : kUnimplemented;
247*c8dee2aaSAndroid Build Coastguard Worker }
248*c8dee2aaSAndroid Build Coastguard Worker 
getSampler(bool createIfNecessary)249*c8dee2aaSAndroid Build Coastguard Worker SkSampler* SkPngCodecBase::getSampler(bool createIfNecessary) {
250*c8dee2aaSAndroid Build Coastguard Worker     if (fSwizzler || !createIfNecessary) {
251*c8dee2aaSAndroid Build Coastguard Worker         return fSwizzler.get();
252*c8dee2aaSAndroid Build Coastguard Worker     }
253*c8dee2aaSAndroid Build Coastguard Worker 
254*c8dee2aaSAndroid Build Coastguard Worker     // Ok to ignore `initializeSwizzler`'s result, because if it fails, then
255*c8dee2aaSAndroid Build Coastguard Worker     // `fSwizzler` will be `nullptr` and we want to return `nullptr` upon
256*c8dee2aaSAndroid Build Coastguard Worker     // failure.
257*c8dee2aaSAndroid Build Coastguard Worker     std::ignore = this->initializeSwizzler(
258*c8dee2aaSAndroid Build Coastguard Worker             this->dstInfo(), this->options(), true, this->dstInfo().width());
259*c8dee2aaSAndroid Build Coastguard Worker 
260*c8dee2aaSAndroid Build Coastguard Worker     return fSwizzler.get();
261*c8dee2aaSAndroid Build Coastguard Worker }
262*c8dee2aaSAndroid Build Coastguard Worker 
applyXformRow(SkSpan<uint8_t> dstRow,SkSpan<const uint8_t> srcRow)263*c8dee2aaSAndroid Build Coastguard Worker void SkPngCodecBase::applyXformRow(SkSpan<uint8_t> dstRow, SkSpan<const uint8_t> srcRow) {
264*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(dstRow.size() >= fDstRowBytes);
265*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(srcRow.size() >= fEncodedRowBytes);
266*c8dee2aaSAndroid Build Coastguard Worker     applyXformRow(dstRow.data(), srcRow.data());
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker 
applyXformRow(void * dstRow,const uint8_t * srcRow)269*c8dee2aaSAndroid Build Coastguard Worker void SkPngCodecBase::applyXformRow(void* dstRow, const uint8_t* srcRow) {
270*c8dee2aaSAndroid Build Coastguard Worker     switch (fXformMode) {
271*c8dee2aaSAndroid Build Coastguard Worker         case kSwizzleOnly_XformMode:
272*c8dee2aaSAndroid Build Coastguard Worker             fSwizzler->swizzle(dstRow, srcRow);
273*c8dee2aaSAndroid Build Coastguard Worker             break;
274*c8dee2aaSAndroid Build Coastguard Worker         case kColorOnly_XformMode:
275*c8dee2aaSAndroid Build Coastguard Worker             this->applyColorXform(dstRow, srcRow, fXformWidth);
276*c8dee2aaSAndroid Build Coastguard Worker             break;
277*c8dee2aaSAndroid Build Coastguard Worker         case kSwizzleColor_XformMode:
278*c8dee2aaSAndroid Build Coastguard Worker             fSwizzler->swizzle(fStorage.get(), srcRow);
279*c8dee2aaSAndroid Build Coastguard Worker             this->applyColorXform(dstRow, fStorage.get(), fXformWidth);
280*c8dee2aaSAndroid Build Coastguard Worker             break;
281*c8dee2aaSAndroid Build Coastguard Worker     }
282*c8dee2aaSAndroid Build Coastguard Worker }
283*c8dee2aaSAndroid Build Coastguard Worker 
284*c8dee2aaSAndroid Build Coastguard Worker // Note: SkColorPalette claims to store SkPMColors, which is not necessarily the case here.
createColorTable(const SkImageInfo & dstInfo)285*c8dee2aaSAndroid Build Coastguard Worker bool SkPngCodecBase::createColorTable(const SkImageInfo& dstInfo) {
286*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SkSpan<const PaletteColorEntry>> maybePlteChunk = this->onTryGetPlteChunk();
287*c8dee2aaSAndroid Build Coastguard Worker     if (!maybePlteChunk.has_value()) {
288*c8dee2aaSAndroid Build Coastguard Worker         return false;
289*c8dee2aaSAndroid Build Coastguard Worker     }
290*c8dee2aaSAndroid Build Coastguard Worker     const PaletteColorEntry* palette = maybePlteChunk->data();
291*c8dee2aaSAndroid Build Coastguard Worker     size_t numColors = maybePlteChunk->size();
292*c8dee2aaSAndroid Build Coastguard Worker 
293*c8dee2aaSAndroid Build Coastguard Worker     // Contents depend on tableColorType and our choice of if/when to premultiply:
294*c8dee2aaSAndroid Build Coastguard Worker     // { kPremul, kUnpremul, kOpaque } x { RGBA, BGRA }
295*c8dee2aaSAndroid Build Coastguard Worker     SkPMColor colorTable[256];
296*c8dee2aaSAndroid Build Coastguard Worker     SkColorType tableColorType = this->colorXform() ? kXformSrcColorType : dstInfo.colorType();
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SkSpan<const uint8_t>> maybeTrnsChunk = this->onTryGetTrnsChunk();
299*c8dee2aaSAndroid Build Coastguard Worker     const uint8_t* alphas = nullptr;
300*c8dee2aaSAndroid Build Coastguard Worker     size_t numColorsWithAlpha = 0;
301*c8dee2aaSAndroid Build Coastguard Worker     if (maybeTrnsChunk.has_value()) {
302*c8dee2aaSAndroid Build Coastguard Worker         alphas = maybeTrnsChunk->data();
303*c8dee2aaSAndroid Build Coastguard Worker         numColorsWithAlpha = maybeTrnsChunk->size();
304*c8dee2aaSAndroid Build Coastguard Worker     }
305*c8dee2aaSAndroid Build Coastguard Worker 
306*c8dee2aaSAndroid Build Coastguard Worker     if (alphas) {
307*c8dee2aaSAndroid Build Coastguard Worker         bool premultiply = needs_premul(dstInfo.alphaType(), this->getEncodedInfo().alpha());
308*c8dee2aaSAndroid Build Coastguard Worker 
309*c8dee2aaSAndroid Build Coastguard Worker         // Choose which function to use to create the color table. If the final destination's
310*c8dee2aaSAndroid Build Coastguard Worker         // colortype is unpremultiplied, the color table will store unpremultiplied colors.
311*c8dee2aaSAndroid Build Coastguard Worker         PackColorProc proc = choose_pack_color_proc(premultiply, tableColorType);
312*c8dee2aaSAndroid Build Coastguard Worker 
313*c8dee2aaSAndroid Build Coastguard Worker         for (size_t i = 0; i < numColorsWithAlpha; i++) {
314*c8dee2aaSAndroid Build Coastguard Worker             // We don't have a function in SkOpts that combines a set of alphas with a set
315*c8dee2aaSAndroid Build Coastguard Worker             // of RGBs.  We could write one, but it's hardly worth it, given that this
316*c8dee2aaSAndroid Build Coastguard Worker             // is such a small fraction of the total decode time.
317*c8dee2aaSAndroid Build Coastguard Worker             colorTable[i] = proc(alphas[i], palette->red, palette->green, palette->blue);
318*c8dee2aaSAndroid Build Coastguard Worker             palette++;
319*c8dee2aaSAndroid Build Coastguard Worker         }
320*c8dee2aaSAndroid Build Coastguard Worker     }
321*c8dee2aaSAndroid Build Coastguard Worker 
322*c8dee2aaSAndroid Build Coastguard Worker     if (numColorsWithAlpha < numColors) {
323*c8dee2aaSAndroid Build Coastguard Worker         // The optimized code depends on a 3-byte png_color struct with the colors
324*c8dee2aaSAndroid Build Coastguard Worker         // in RGB order.  These checks make sure it is safe to use.
325*c8dee2aaSAndroid Build Coastguard Worker         static_assert(3 == sizeof(PaletteColorEntry));
326*c8dee2aaSAndroid Build Coastguard Worker         static_assert(offsetof(PaletteColorEntry, red) == 0);
327*c8dee2aaSAndroid Build Coastguard Worker         static_assert(offsetof(PaletteColorEntry, green) == 1);
328*c8dee2aaSAndroid Build Coastguard Worker         static_assert(offsetof(PaletteColorEntry, blue) == 2);
329*c8dee2aaSAndroid Build Coastguard Worker 
330*c8dee2aaSAndroid Build Coastguard Worker         if (is_rgba(tableColorType)) {
331*c8dee2aaSAndroid Build Coastguard Worker             SkOpts::RGB_to_RGB1(colorTable + numColorsWithAlpha,
332*c8dee2aaSAndroid Build Coastguard Worker                                 (const uint8_t*)palette,
333*c8dee2aaSAndroid Build Coastguard Worker                                 numColors - numColorsWithAlpha);
334*c8dee2aaSAndroid Build Coastguard Worker         } else {
335*c8dee2aaSAndroid Build Coastguard Worker             SkOpts::RGB_to_BGR1(colorTable + numColorsWithAlpha,
336*c8dee2aaSAndroid Build Coastguard Worker                                 (const uint8_t*)palette,
337*c8dee2aaSAndroid Build Coastguard Worker                                 numColors - numColorsWithAlpha);
338*c8dee2aaSAndroid Build Coastguard Worker         }
339*c8dee2aaSAndroid Build Coastguard Worker     }
340*c8dee2aaSAndroid Build Coastguard Worker 
341*c8dee2aaSAndroid Build Coastguard Worker     if (this->colorXform() && !this->xformOnDecode()) {
342*c8dee2aaSAndroid Build Coastguard Worker         this->applyColorXform(colorTable, colorTable, numColors);
343*c8dee2aaSAndroid Build Coastguard Worker     }
344*c8dee2aaSAndroid Build Coastguard Worker 
345*c8dee2aaSAndroid Build Coastguard Worker     // Pad the color table with the last color in the table (or black) in the case that
346*c8dee2aaSAndroid Build Coastguard Worker     // invalid pixel indices exceed the number of colors in the table.
347*c8dee2aaSAndroid Build Coastguard Worker     const size_t maxColors = static_cast<size_t>(1) << this->getEncodedInfo().bitsPerComponent();
348*c8dee2aaSAndroid Build Coastguard Worker     if (numColors < maxColors) {
349*c8dee2aaSAndroid Build Coastguard Worker         SkPMColor lastColor = numColors > 0 ? colorTable[numColors - 1] : SK_ColorBLACK;
350*c8dee2aaSAndroid Build Coastguard Worker         SkOpts::memset32(colorTable + numColors, lastColor, maxColors - numColors);
351*c8dee2aaSAndroid Build Coastguard Worker     }
352*c8dee2aaSAndroid Build Coastguard Worker 
353*c8dee2aaSAndroid Build Coastguard Worker     fColorTable.reset(new SkColorPalette(colorTable, maxColors));
354*c8dee2aaSAndroid Build Coastguard Worker     return true;
355*c8dee2aaSAndroid Build Coastguard Worker }
356