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