1/*
2 * Copyright 2017 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#include "gm.h"
9#include "SkColorPriv.h"
10#include "SkColorSpaceXform.h"
11#include "SkColorSpaceXformPriv.h"
12#include "SkOpts.h"
13#include "SkUtils.h"
14
15static void clamp_to_alpha(uint32_t* pixels, int count) {
16    for (int i = 0; i < count; i++) {
17        uint8_t a = SkGetPackedA32(pixels[i]);
18        uint8_t r = SkGetPackedR32(pixels[i]);
19        uint8_t g = SkGetPackedG32(pixels[i]);
20        uint8_t b = SkGetPackedB32(pixels[i]);
21        pixels[i] = SkPackARGB32(a,
22                                 SkTMin(a, r),
23                                 SkTMin(a, g),
24                                 SkTMin(a, b));
25    }
26}
27
28class GammaEncodedPremulGM : public skiagm::GM {
29public:
30    GammaEncodedPremulGM(sk_sp<SkColorSpace> dst, sk_sp<SkColorSpace> src, const char* desc)
31        : fDstSpace(dst)
32        , fSrcSpace(src)
33        , fXform(SkColorSpaceXform::New(src.get(), dst.get()))
34        , fName(SkStringPrintf("gamma_encoded_premul_dst-v-src_%s", desc))
35    {
36        int i = 0;
37        for (int r = 0; r < kColorSteps; r++) {
38            for (int g = 0; g < kColorSteps; g++) {
39                for (int b = 0; b < kColorSteps; b++) {
40                    fColors[i++] = SkColorSetARGBInline(0xFF,
41                                                        r * kColorScale,
42                                                        g * kColorScale,
43                                                        b * kColorScale);
44                }
45            }
46        }
47
48    }
49
50protected:
51    virtual SkISize onISize() override {
52        return SkISize::Make(kAlphaMax, kNumColors * 2 * kStripeHeight);
53    }
54
55    SkString onShortName() override {
56        return fName;
57    }
58
59    void onDraw(SkCanvas* canvas) override {
60        if (canvas->imageInfo().isOpaque()) {
61            return;
62        }
63
64        SkBitmap bitmap;
65        SkImageInfo bitmapInfo = SkImageInfo::MakeN32Premul(kAlphaMax, 1,
66                canvas->imageInfo().refColorSpace());
67        bitmap.allocPixels(bitmapInfo);
68        uint32_t* pixels = bitmap.getAddr32(0, 0);
69
70        for (int i = 0; i < kNumColors; i++) {
71            // Create an entire row of the same color, with the alpha from 0 to kAlphaMax.
72            uint32_t row[kAlphaMax];
73            sk_memset32(row, fColors[i], kAlphaMax);
74            for (int a = 0; a < kAlphaMax; a++) {
75                row[a] = (row[a] & 0x00FFFFFF) | (a << 24);
76            }
77
78            // Tranform row to dst, then premultiply.
79            fXform->apply(select_xform_format(kN32_SkColorType), pixels,
80                          SkColorSpaceXform::kBGRA_8888_ColorFormat, row, kAlphaMax,
81                          kUnpremul_SkAlphaType);
82            SkOpts::RGBA_to_rgbA(pixels, pixels, kAlphaMax);
83            bitmap.notifyPixelsChanged();
84
85            // Write the dst space premultiplied row to the canvas.
86            for (int j = 0; j < kStripeHeight; j++) {
87                canvas->drawBitmap(bitmap, 0, 2 * i * kStripeHeight + j);
88            }
89
90            // Premultiply, then transform the row to dst.
91            SkOpts::RGBA_to_rgbA(pixels, row, kAlphaMax);
92            fXform->apply(select_xform_format(kN32_SkColorType), pixels,
93                          SkColorSpaceXform::kBGRA_8888_ColorFormat, pixels, kAlphaMax,
94                          kUnpremul_SkAlphaType);
95            clamp_to_alpha(pixels, kAlphaMax);
96            bitmap.notifyPixelsChanged();
97
98            // Write the src space premultiplied row to the canvas.
99            for (int j = 0; j < kStripeHeight; j++) {
100                canvas->drawBitmap(bitmap, 0, (2 * i + 1) * kStripeHeight + j);
101            }
102        }
103    }
104
105private:
106    static constexpr int kColorSteps = 4;
107    static constexpr int kNumColors = kColorSteps * kColorSteps * kColorSteps;
108    static constexpr int kColorScale = 255 / (kColorSteps - 1);
109    static constexpr int kStripeHeight = 10;
110    static constexpr int kAlphaMax = 255;
111
112    sk_sp<SkColorSpace>                fDstSpace;
113    sk_sp<SkColorSpace>                fSrcSpace;
114    std::unique_ptr<SkColorSpaceXform> fXform;
115    SkString                           fName;
116    SkColor                            fColors[kNumColors];
117
118    typedef GM INHERITED;
119};
120
121DEF_GM(return new GammaEncodedPremulGM(SkColorSpace::MakeSRGB(),
122        SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kRec2020_Gamut),
123        "toWideGamut");)
124DEF_GM(return new GammaEncodedPremulGM(SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
125        SkColorSpace::kRec2020_Gamut), SkColorSpace::MakeSRGB(), "fromWideGamut");)
126DEF_GM(return new GammaEncodedPremulGM(SkColorSpace::MakeSRGB(),
127        SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkColorSpace::kSRGB_Gamut),
128        "toLinear");)
129DEF_GM(return new GammaEncodedPremulGM(
130        SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkColorSpace::kSRGB_Gamut),
131        SkColorSpace::MakeSRGB(), "fromLinear");)
132DEF_GM(return new GammaEncodedPremulGM(
133        SkColorSpace::MakeRGB({ 1.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f },
134        SkColorSpace::kSRGB_Gamut), SkColorSpace::MakeSRGB(), "from1.8");)
135