xref: /aosp_15_r20/external/skia/src/encode/SkPngEncoderBase.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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