1 | /* |
2 | * Copyright 2016 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 | #ifndef SkEncodedInfo_DEFINED |
9 | #define SkEncodedInfo_DEFINED |
10 | |
11 | #include <memory> |
12 | |
13 | #include "include/core/SkData.h" |
14 | #include "include/core/SkImageInfo.h" |
15 | #include "include/third_party/skcms/skcms.h" |
16 | |
17 | struct SkEncodedInfo { |
18 | public: |
19 | class ICCProfile { |
20 | public: |
21 | static std::unique_ptr<ICCProfile> Make(sk_sp<SkData>); |
22 | static std::unique_ptr<ICCProfile> Make(const skcms_ICCProfile&); |
23 | |
24 | const skcms_ICCProfile* profile() const { return &fProfile; } |
25 | private: |
26 | ICCProfile(const skcms_ICCProfile&, sk_sp<SkData> = nullptr); |
27 | |
28 | skcms_ICCProfile fProfile; |
29 | sk_sp<SkData> fData; |
30 | }; |
31 | |
32 | enum Alpha { |
33 | kOpaque_Alpha, |
34 | kUnpremul_Alpha, |
35 | |
36 | // Each pixel is either fully opaque or fully transparent. |
37 | // There is no difference between requesting kPremul or kUnpremul. |
38 | kBinary_Alpha, |
39 | }; |
40 | |
41 | /* |
42 | * We strive to make the number of components per pixel obvious through |
43 | * our naming conventions. |
44 | * Ex: kRGB has 3 components. kRGBA has 4 components. |
45 | * |
46 | * This sometimes results in redundant Alpha and Color information. |
47 | * Ex: kRGB images must also be kOpaque. |
48 | */ |
49 | enum Color { |
50 | // PNG, WBMP |
51 | kGray_Color, |
52 | |
53 | // PNG |
54 | kGrayAlpha_Color, |
55 | |
56 | // PNG with Skia-specific sBIT |
57 | // Like kGrayAlpha, except this expects to be treated as |
58 | // kAlpha_8_SkColorType, which ignores the gray component. If |
59 | // decoded to full color (e.g. kN32), the gray component is respected |
60 | // (so it can share code with kGrayAlpha). |
61 | kXAlpha_Color, |
62 | |
63 | // PNG |
64 | // 565 images may be encoded to PNG by specifying the number of |
65 | // significant bits for each channel. This is a strange 565 |
66 | // representation because the image is still encoded with 8 bits per |
67 | // component. |
68 | k565_Color, |
69 | |
70 | // PNG, GIF, BMP |
71 | kPalette_Color, |
72 | |
73 | // PNG, RAW |
74 | kRGB_Color, |
75 | kRGBA_Color, |
76 | |
77 | // BMP |
78 | kBGR_Color, |
79 | kBGRX_Color, |
80 | kBGRA_Color, |
81 | |
82 | // JPEG, WEBP |
83 | kYUV_Color, |
84 | |
85 | // WEBP |
86 | kYUVA_Color, |
87 | |
88 | // JPEG |
89 | // Photoshop actually writes inverted CMYK data into JPEGs, where zero |
90 | // represents 100% ink coverage. For this reason, we treat CMYK JPEGs |
91 | // as having inverted CMYK. libjpeg-turbo warns that this may break |
92 | // other applications, but the CMYK JPEGs we see on the web expect to |
93 | // be treated as inverted CMYK. |
94 | kInvertedCMYK_Color, |
95 | kYCCK_Color, |
96 | }; |
97 | |
98 | static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha, |
99 | int bitsPerComponent) { |
100 | return Make(width, height, color, alpha, bitsPerComponent, nullptr); |
101 | } |
102 | |
103 | static SkEncodedInfo Make(int width, int height, Color color, Alpha alpha, |
104 | int bitsPerComponent, std::unique_ptr<ICCProfile> profile) { |
105 | SkASSERT(1 == bitsPerComponent || |
106 | 2 == bitsPerComponent || |
107 | 4 == bitsPerComponent || |
108 | 8 == bitsPerComponent || |
109 | 16 == bitsPerComponent); |
110 | |
111 | switch (color) { |
112 | case kGray_Color: |
113 | SkASSERT(kOpaque_Alpha == alpha); |
114 | break; |
115 | case kGrayAlpha_Color: |
116 | SkASSERT(kOpaque_Alpha != alpha); |
117 | break; |
118 | case kPalette_Color: |
119 | SkASSERT(16 != bitsPerComponent); |
120 | break; |
121 | case kRGB_Color: |
122 | case kBGR_Color: |
123 | case kBGRX_Color: |
124 | SkASSERT(kOpaque_Alpha == alpha); |
125 | SkASSERT(bitsPerComponent >= 8); |
126 | break; |
127 | case kYUV_Color: |
128 | case kInvertedCMYK_Color: |
129 | case kYCCK_Color: |
130 | SkASSERT(kOpaque_Alpha == alpha); |
131 | SkASSERT(8 == bitsPerComponent); |
132 | break; |
133 | case kRGBA_Color: |
134 | SkASSERT(bitsPerComponent >= 8); |
135 | break; |
136 | case kBGRA_Color: |
137 | case kYUVA_Color: |
138 | SkASSERT(8 == bitsPerComponent); |
139 | break; |
140 | case kXAlpha_Color: |
141 | SkASSERT(kUnpremul_Alpha == alpha); |
142 | SkASSERT(8 == bitsPerComponent); |
143 | break; |
144 | case k565_Color: |
145 | SkASSERT(kOpaque_Alpha == alpha); |
146 | SkASSERT(8 == bitsPerComponent); |
147 | break; |
148 | default: |
149 | SkASSERT(false); |
150 | break; |
151 | } |
152 | |
153 | return SkEncodedInfo(width, height, color, alpha, bitsPerComponent, std::move(profile)); |
154 | } |
155 | |
156 | /* |
157 | * Returns a recommended SkImageInfo. |
158 | * |
159 | * TODO: Leave this up to the client. |
160 | */ |
161 | SkImageInfo makeImageInfo() const { |
162 | auto ct = kGray_Color == fColor ? kGray_8_SkColorType : |
163 | kXAlpha_Color == fColor ? kAlpha_8_SkColorType : |
164 | k565_Color == fColor ? kRGB_565_SkColorType : |
165 | kN32_SkColorType ; |
166 | auto alpha = kOpaque_Alpha == fAlpha ? kOpaque_SkAlphaType |
167 | : kUnpremul_SkAlphaType; |
168 | sk_sp<SkColorSpace> cs = fProfile ? SkColorSpace::Make(*fProfile->profile()) |
169 | : nullptr; |
170 | if (!cs) { |
171 | cs = SkColorSpace::MakeSRGB(); |
172 | } |
173 | return SkImageInfo::Make(fWidth, fHeight, ct, alpha, std::move(cs)); |
174 | } |
175 | |
176 | int width() const { return fWidth; } |
177 | int height() const { return fHeight; } |
178 | Color color() const { return fColor; } |
179 | Alpha alpha() const { return fAlpha; } |
180 | bool opaque() const { return fAlpha == kOpaque_Alpha; } |
181 | const skcms_ICCProfile* profile() const { |
182 | if (!fProfile) return nullptr; |
183 | return fProfile->profile(); |
184 | } |
185 | |
186 | uint8_t bitsPerComponent() const { return fBitsPerComponent; } |
187 | |
188 | uint8_t bitsPerPixel() const { |
189 | switch (fColor) { |
190 | case kGray_Color: |
191 | return fBitsPerComponent; |
192 | case kXAlpha_Color: |
193 | case kGrayAlpha_Color: |
194 | return 2 * fBitsPerComponent; |
195 | case kPalette_Color: |
196 | return fBitsPerComponent; |
197 | case kRGB_Color: |
198 | case kBGR_Color: |
199 | case kYUV_Color: |
200 | case k565_Color: |
201 | return 3 * fBitsPerComponent; |
202 | case kRGBA_Color: |
203 | case kBGRA_Color: |
204 | case kBGRX_Color: |
205 | case kYUVA_Color: |
206 | case kInvertedCMYK_Color: |
207 | case kYCCK_Color: |
208 | return 4 * fBitsPerComponent; |
209 | default: |
210 | SkASSERT(false); |
211 | return 0; |
212 | } |
213 | } |
214 | |
215 | SkEncodedInfo(const SkEncodedInfo& orig) = delete; |
216 | SkEncodedInfo& operator=(const SkEncodedInfo&) = delete; |
217 | |
218 | SkEncodedInfo(SkEncodedInfo&& orig) = default; |
219 | SkEncodedInfo& operator=(SkEncodedInfo&&) = default; |
220 | |
221 | // Explicit copy method, to avoid accidental copying. |
222 | SkEncodedInfo copy() const { |
223 | auto copy = SkEncodedInfo::Make(fWidth, fHeight, fColor, fAlpha, fBitsPerComponent); |
224 | if (fProfile) { |
225 | copy.fProfile = std::make_unique<ICCProfile>(*fProfile.get()); |
226 | } |
227 | return copy; |
228 | } |
229 | |
230 | private: |
231 | SkEncodedInfo(int width, int height, Color color, Alpha alpha, |
232 | uint8_t bitsPerComponent, std::unique_ptr<ICCProfile> profile) |
233 | : fWidth(width) |
234 | , fHeight(height) |
235 | , fColor(color) |
236 | , fAlpha(alpha) |
237 | , fBitsPerComponent(bitsPerComponent) |
238 | , fProfile(std::move(profile)) |
239 | {} |
240 | |
241 | int fWidth; |
242 | int fHeight; |
243 | Color fColor; |
244 | Alpha fAlpha; |
245 | uint8_t fBitsPerComponent; |
246 | std::unique_ptr<ICCProfile> fProfile; |
247 | }; |
248 | |
249 | #endif |
250 | |