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