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/encode/SkPngEncoderBase.h"
9
10 #include <utility>
11
12 #include "include/core/SkAlphaType.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkPixmap.h"
16 #include "include/core/SkSpan.h"
17 #include "include/private/SkEncodedInfo.h"
18 #include "include/private/base/SkAssert.h"
19 #include "include/private/base/SkTemplates.h"
20 #include "src/base/SkMSAN.h"
21 #include "src/base/SkSafeMath.h"
22
23 namespace {
24
makeInfo(const SkImageInfo & srcInfo,SkEncodedInfo::Color color,int bitsPerComponent)25 SkEncodedInfo makeInfo(const SkImageInfo& srcInfo,
26 SkEncodedInfo::Color color,
27 int bitsPerComponent) {
28 SkEncodedInfo::Alpha alpha =
29 color == SkEncodedInfo::kGray_Color || color == SkEncodedInfo::kRGB_Color
30 ? SkEncodedInfo::kOpaque_Alpha
31 : SkEncodedInfo::kUnpremul_Alpha;
32
33 return SkEncodedInfo::Make(srcInfo.width(), srcInfo.height(), color, alpha, bitsPerComponent);
34 }
35
makeGray8Info(const SkImageInfo & srcInfo)36 SkEncodedInfo makeGray8Info(const SkImageInfo& srcInfo) {
37 return makeInfo(srcInfo, SkEncodedInfo::kGray_Color, 8);
38 }
39
makeGrayAlpha8Info(const SkImageInfo & srcInfo)40 SkEncodedInfo makeGrayAlpha8Info(const SkImageInfo& srcInfo) {
41 return makeInfo(srcInfo, SkEncodedInfo::kGrayAlpha_Color, 8);
42 }
43
makeRgb8Info(const SkImageInfo & srcInfo)44 SkEncodedInfo makeRgb8Info(const SkImageInfo& srcInfo) {
45 return makeInfo(srcInfo, SkEncodedInfo::kRGB_Color, 8);
46 }
47
makeRgba8Info(const SkImageInfo & srcInfo)48 SkEncodedInfo makeRgba8Info(const SkImageInfo& srcInfo) {
49 return makeInfo(srcInfo, SkEncodedInfo::kRGBA_Color, 8);
50 }
51
makeRgb16Info(const SkImageInfo & srcInfo)52 SkEncodedInfo makeRgb16Info(const SkImageInfo& srcInfo) {
53 return makeInfo(srcInfo, SkEncodedInfo::kRGB_Color, 16);
54 }
55
makeRgba16Info(const SkImageInfo & srcInfo)56 SkEncodedInfo makeRgba16Info(const SkImageInfo& srcInfo) {
57 return makeInfo(srcInfo, SkEncodedInfo::kRGBA_Color, 16);
58 }
59
makeTargetInfo(SkEncodedInfo dstInfo,transform_scanline_proc transformProc)60 std::optional<SkPngEncoderBase::TargetInfo> makeTargetInfo(SkEncodedInfo dstInfo,
61 transform_scanline_proc transformProc) {
62 // `static_cast<size_t>`(dstInfo.bitsPerPixel())` uses trustworthy, bounded
63 // data as input - no need to use `SkSafeMath` for this part.
64 SkASSERT(dstInfo.bitsPerComponent() == 8 || dstInfo.bitsPerComponent() == 16);
65 SkASSERT(dstInfo.bitsPerPixel() <= (16 * 4));
66 size_t bitsPerPixel = static_cast<size_t>(dstInfo.bitsPerPixel());
67 SkASSERT((bitsPerPixel % 8) == 0);
68 size_t bytesPerPixel = bitsPerPixel / 8;
69
70 SkSafeMath safe;
71 size_t dstRowSize = safe.mul(safe.castTo<size_t>(dstInfo.width()), bytesPerPixel);
72 if (!safe.ok()) {
73 return std::nullopt;
74 }
75
76 return SkPngEncoderBase::TargetInfo{std::move(dstInfo), transformProc, dstRowSize};
77 }
78
79 } // namespace
80
81 // static
getTargetInfo(const SkImageInfo & srcInfo)82 std::optional<SkPngEncoderBase::TargetInfo> SkPngEncoderBase::getTargetInfo(
83 const SkImageInfo& srcInfo) {
84 switch (srcInfo.colorType()) {
85 case kUnknown_SkColorType:
86 return std::nullopt;
87
88 // TODO: I don't think this can just use kRGBA's procs.
89 // kPremul is especially tricky here, since it's presumably TF⁻¹(rgb * a),
90 // so to get at unpremul rgb we'd need to undo the transfer function first.
91 case kSRGBA_8888_SkColorType:
92 return std::nullopt;
93
94 case kRGBA_8888_SkColorType:
95 switch (srcInfo.alphaType()) {
96 case kOpaque_SkAlphaType:
97 return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_RGBX);
98 case kUnpremul_SkAlphaType:
99 return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_memcpy);
100 case kPremul_SkAlphaType:
101 return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_rgbA);
102 default:
103 SkDEBUGFAIL("unknown alpha type");
104 return std::nullopt;
105 }
106 case kBGRA_8888_SkColorType:
107 switch (srcInfo.alphaType()) {
108 case kOpaque_SkAlphaType:
109 return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_BGRX);
110 case kUnpremul_SkAlphaType:
111 return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_BGRA);
112 case kPremul_SkAlphaType:
113 return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_bgrA);
114 default:
115 SkDEBUGFAIL("unknown alpha type");
116 return std::nullopt;
117 }
118 case kRGB_565_SkColorType:
119 SkASSERT(srcInfo.isOpaque());
120 return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_565);
121 case kRGB_888x_SkColorType:
122 SkASSERT(srcInfo.isOpaque());
123 return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_RGBX);
124 case kARGB_4444_SkColorType:
125 switch (srcInfo.alphaType()) {
126 case kOpaque_SkAlphaType:
127 return makeTargetInfo(makeRgb8Info(srcInfo), transform_scanline_444);
128 case kPremul_SkAlphaType:
129 return makeTargetInfo(makeRgba8Info(srcInfo), transform_scanline_4444);
130 default:
131 SkDEBUGFAIL("unknown alpha type");
132 return std::nullopt;
133 }
134 case kGray_8_SkColorType:
135 SkASSERT(srcInfo.isOpaque());
136 return makeTargetInfo(makeGray8Info(srcInfo), transform_scanline_memcpy);
137
138 case kRGBA_F16Norm_SkColorType:
139 case kRGBA_F16_SkColorType:
140 switch (srcInfo.alphaType()) {
141 case kOpaque_SkAlphaType:
142 case kUnpremul_SkAlphaType:
143 return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_F16);
144 case kPremul_SkAlphaType:
145 return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_F16_premul);
146 default:
147 SkDEBUGFAIL("unknown alpha type");
148 return std::nullopt;
149 }
150 case kRGB_F16F16F16x_SkColorType:
151 SkASSERT(srcInfo.isOpaque());
152 return makeTargetInfo(makeRgb16Info(srcInfo), transform_scanline_F16F16F16x);
153 case kRGBA_F32_SkColorType:
154 switch (srcInfo.alphaType()) {
155 case kOpaque_SkAlphaType:
156 case kUnpremul_SkAlphaType:
157 return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_F32);
158 case kPremul_SkAlphaType:
159 return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_F32_premul);
160 default:
161 SkDEBUGFAIL("unknown alpha type");
162 return std::nullopt;
163 }
164 case kRGBA_1010102_SkColorType:
165 switch (srcInfo.alphaType()) {
166 case kOpaque_SkAlphaType:
167 case kUnpremul_SkAlphaType:
168 return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_1010102);
169 case kPremul_SkAlphaType:
170 return makeTargetInfo(makeRgba16Info(srcInfo),
171 transform_scanline_1010102_premul);
172 default:
173 SkDEBUGFAIL("unknown alpha type");
174 return std::nullopt;
175 }
176 case kBGRA_1010102_SkColorType:
177 switch (srcInfo.alphaType()) {
178 case kOpaque_SkAlphaType:
179 case kUnpremul_SkAlphaType:
180 return makeTargetInfo(makeRgba16Info(srcInfo), transform_scanline_bgra_1010102);
181 case kPremul_SkAlphaType:
182 return makeTargetInfo(makeRgba16Info(srcInfo),
183 transform_scanline_bgra_1010102_premul);
184 default:
185 SkDEBUGFAIL("unknown alpha type");
186 return std::nullopt;
187 }
188 case kRGB_101010x_SkColorType:
189 return makeTargetInfo(makeRgb16Info(srcInfo), transform_scanline_101010x);
190 case kBGR_101010x_SkColorType:
191 return makeTargetInfo(makeRgb16Info(srcInfo), transform_scanline_bgr_101010x);
192 case kBGR_101010x_XR_SkColorType:
193 switch (srcInfo.alphaType()) {
194 case kOpaque_SkAlphaType:
195 return makeTargetInfo(makeRgb16Info(srcInfo),
196 transform_scanline_bgr_101010x_xr);
197 default:
198 SkDEBUGFAIL("unsupported color type");
199 return std::nullopt;
200 }
201 case kBGRA_10101010_XR_SkColorType:
202 switch (srcInfo.alphaType()) {
203 case kOpaque_SkAlphaType:
204 case kUnpremul_SkAlphaType:
205 return makeTargetInfo(makeRgba16Info(srcInfo),
206 transform_scanline_bgra_10101010_xr);
207 case kPremul_SkAlphaType:
208 return makeTargetInfo(makeRgba16Info(srcInfo),
209 transform_scanline_bgra_10101010_xr_premul);
210 default:
211 SkDEBUGFAIL("unknown alpha type");
212 return std::nullopt;
213 }
214 case kAlpha_8_SkColorType:
215 return makeTargetInfo(makeGrayAlpha8Info(srcInfo), transform_scanline_A8_to_GrayAlpha);
216 case kR8G8_unorm_SkColorType:
217 case kR16G16_unorm_SkColorType:
218 case kR16G16_float_SkColorType:
219 case kA16_unorm_SkColorType:
220 case kA16_float_SkColorType:
221 case kR16G16B16A16_unorm_SkColorType:
222 case kR8_unorm_SkColorType:
223 case kRGBA_10x6_SkColorType:
224 return std::nullopt;
225 }
226 SkDEBUGFAIL("unsupported color type");
227 return std::nullopt;
228 }
229
SkPngEncoderBase(TargetInfo targetInfo,const SkPixmap & src)230 SkPngEncoderBase::SkPngEncoderBase(TargetInfo targetInfo, const SkPixmap& src)
231 : SkEncoder(src, targetInfo.fDstRowSize), fTargetInfo(std::move(targetInfo)) {
232 SkASSERT(fTargetInfo.fTransformProc);
233 SkASSERT(fTargetInfo.fDstRowSize > 0);
234 }
235
onEncodeRows(int numRows)236 bool SkPngEncoderBase::onEncodeRows(int numRows) {
237 // https://www.w3.org/TR/png-3/#11IHDR says that "zero is an invalid value"
238 // for width and height.
239 if (fSrc.width() == 0 || fSrc.height() == 0) {
240 return false;
241 }
242
243 if (numRows < 0) {
244 return false;
245 }
246
247 while (numRows > 0) {
248 if (fCurrRow == fSrc.height()) {
249 return false;
250 }
251
252 const void* srcRow = fSrc.addr(0, fCurrRow);
253 sk_msan_assert_initialized(srcRow,
254 (const uint8_t*)srcRow + (fSrc.width() << fSrc.shiftPerPixel()));
255
256 fTargetInfo.fTransformProc((char*)fStorage.get(),
257 (const char*)srcRow,
258 fSrc.width(),
259 SkColorTypeBytesPerPixel(fSrc.colorType()));
260
261 SkSpan<const uint8_t> rowToEncode(fStorage.get(), fTargetInfo.fDstRowSize);
262 if (!this->onEncodeRow(rowToEncode)) {
263 return false;
264 }
265
266 fCurrRow++;
267 numRows--;
268 }
269
270 if (fCurrRow == fSrc.height() && !fFinishedEncoding) {
271 fFinishedEncoding = true;
272 return this->onFinishEncoding();
273 }
274
275 return true;
276 }
277