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
17struct SkEncodedInfo {
18public:
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);
226        }
227        return copy;
228    }
229
230private:
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