1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2016 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h"
9cb93a386Sopenharmony_ci#include "include/core/SkBlendMode.h"
10cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
11cb93a386Sopenharmony_ci#include "include/core/SkColor.h"
12cb93a386Sopenharmony_ci#include "include/core/SkColorFilter.h"
13cb93a386Sopenharmony_ci#include "include/core/SkColorPriv.h"
14cb93a386Sopenharmony_ci#include "include/core/SkImageInfo.h"
15cb93a386Sopenharmony_ci#include "include/core/SkPaint.h"
16cb93a386Sopenharmony_ci#include "include/core/SkRefCnt.h"
17cb93a386Sopenharmony_ci#include "include/core/SkScalar.h"
18cb93a386Sopenharmony_ci#include "include/core/SkSurface.h"
19cb93a386Sopenharmony_ci#include "include/core/SkTypes.h"
20cb93a386Sopenharmony_ci#include "include/gpu/GrDirectContext.h"
21cb93a386Sopenharmony_ci#include "include/private/GrTypesPriv.h"
22cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h"
23cb93a386Sopenharmony_ci#include "src/core/SkOpts.h"
24cb93a386Sopenharmony_ci#include "src/gpu/GrCaps.h"
25cb93a386Sopenharmony_ci#include "src/gpu/GrDirectContextPriv.h"
26cb93a386Sopenharmony_ci#include "src/gpu/GrShaderCaps.h"
27cb93a386Sopenharmony_ci#include "tests/Test.h"
28cb93a386Sopenharmony_ci#include "tools/gpu/GrContextFactory.h"
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_ci#include <math.h>
31cb93a386Sopenharmony_ci#include <initializer_list>
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci/** convert 0..1 linear value to 0..1 srgb */
34cb93a386Sopenharmony_cistatic float linear_to_srgb(float linear) {
35cb93a386Sopenharmony_ci    if (linear <= 0.0031308) {
36cb93a386Sopenharmony_ci        return linear * 12.92f;
37cb93a386Sopenharmony_ci    } else {
38cb93a386Sopenharmony_ci        return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f;
39cb93a386Sopenharmony_ci    }
40cb93a386Sopenharmony_ci}
41cb93a386Sopenharmony_ci
42cb93a386Sopenharmony_ci/** convert 0..1 srgb value to 0..1 linear */
43cb93a386Sopenharmony_cistatic float srgb_to_linear(float srgb) {
44cb93a386Sopenharmony_ci    if (srgb <= 0.04045f) {
45cb93a386Sopenharmony_ci        return srgb / 12.92f;
46cb93a386Sopenharmony_ci    } else {
47cb93a386Sopenharmony_ci        return powf((srgb + 0.055f) / 1.055f, 2.4f);
48cb93a386Sopenharmony_ci    }
49cb93a386Sopenharmony_ci}
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_cibool check_gamma(uint32_t src, uint32_t dst, bool toSRGB, float error,
52cb93a386Sopenharmony_ci                 uint32_t* expected) {
53cb93a386Sopenharmony_ci    bool result = true;
54cb93a386Sopenharmony_ci    uint32_t expectedColor = src & 0xff000000;
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci    // Alpha should always be exactly preserved.
57cb93a386Sopenharmony_ci    if ((src & 0xff000000) != (dst & 0xff000000)) {
58cb93a386Sopenharmony_ci        result = false;
59cb93a386Sopenharmony_ci    }
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci    // need to unpremul before we can perform srgb magic
62cb93a386Sopenharmony_ci    float invScale = 0;
63cb93a386Sopenharmony_ci    float alpha = SkGetPackedA32(src);
64cb93a386Sopenharmony_ci    if (alpha) {
65cb93a386Sopenharmony_ci        invScale = 255.0f / alpha;
66cb93a386Sopenharmony_ci    }
67cb93a386Sopenharmony_ci
68cb93a386Sopenharmony_ci    for (int c = 0; c < 3; ++c) {
69cb93a386Sopenharmony_ci        float srcComponent = ((src & (0xff << (c * 8))) >> (c * 8)) * invScale;
70cb93a386Sopenharmony_ci        float lower = std::max(0.f, srcComponent - error);
71cb93a386Sopenharmony_ci        float upper = std::min(255.f, srcComponent + error);
72cb93a386Sopenharmony_ci        if (toSRGB) {
73cb93a386Sopenharmony_ci            lower = linear_to_srgb(lower / 255.f);
74cb93a386Sopenharmony_ci            upper = linear_to_srgb(upper / 255.f);
75cb93a386Sopenharmony_ci        } else {
76cb93a386Sopenharmony_ci            lower = srgb_to_linear(lower / 255.f);
77cb93a386Sopenharmony_ci            upper = srgb_to_linear(upper / 255.f);
78cb93a386Sopenharmony_ci        }
79cb93a386Sopenharmony_ci        lower *= alpha;
80cb93a386Sopenharmony_ci        upper *= alpha;
81cb93a386Sopenharmony_ci        SkASSERT(lower >= 0.f && lower <= 255.f);
82cb93a386Sopenharmony_ci        SkASSERT(upper >= 0.f && upper <= 255.f);
83cb93a386Sopenharmony_ci        uint8_t dstComponent = (dst & (0xff << (c * 8))) >> (c * 8);
84cb93a386Sopenharmony_ci        if (dstComponent < SkScalarFloorToInt(lower) ||
85cb93a386Sopenharmony_ci            dstComponent > SkScalarCeilToInt(upper)) {
86cb93a386Sopenharmony_ci            result = false;
87cb93a386Sopenharmony_ci        }
88cb93a386Sopenharmony_ci        uint8_t expectedComponent = SkScalarRoundToInt((lower + upper) * 0.5f);
89cb93a386Sopenharmony_ci        expectedColor |= expectedComponent << (c * 8);
90cb93a386Sopenharmony_ci    }
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci    *expected = expectedColor;
93cb93a386Sopenharmony_ci    return result;
94cb93a386Sopenharmony_ci}
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ciDEF_GPUTEST_FOR_RENDERING_CONTEXTS(ApplyGamma, reporter, ctxInfo) {
97cb93a386Sopenharmony_ci    auto context = ctxInfo.directContext();
98cb93a386Sopenharmony_ci    static constexpr SkISize kBaseSize{256, 256};
99cb93a386Sopenharmony_ci    static const size_t kRowBytes = sizeof(uint32_t) * kBaseSize.fWidth;
100cb93a386Sopenharmony_ci
101cb93a386Sopenharmony_ci    const SkImageInfo ii = SkImageInfo::MakeN32Premul(kBaseSize);
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ci    SkAutoTMalloc<uint32_t> srcPixels(kBaseSize.area());
104cb93a386Sopenharmony_ci    for (int y = 0; y < kBaseSize.fHeight; ++y) {
105cb93a386Sopenharmony_ci        for (int x = 0; x < kBaseSize.fWidth; ++x) {
106cb93a386Sopenharmony_ci            srcPixels.get()[y*kBaseSize.fWidth+x] = SkPreMultiplyARGB(x, y, x, 0xFF);
107cb93a386Sopenharmony_ci        }
108cb93a386Sopenharmony_ci    }
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci    SkBitmap bm;
111cb93a386Sopenharmony_ci    bm.installPixels(ii, srcPixels.get(), kRowBytes);
112cb93a386Sopenharmony_ci    auto img = bm.asImage();
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    SkAutoTMalloc<uint32_t> read(kBaseSize.area());
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci    // We allow more error on GPUs with lower precision shader variables.
117cb93a386Sopenharmony_ci    float error = context->priv().caps()->shaderCaps()->halfIs32Bits() ? 0.5f : 1.2f;
118cb93a386Sopenharmony_ci
119cb93a386Sopenharmony_ci    for (auto toSRGB : { false, true }) {
120cb93a386Sopenharmony_ci        sk_sp<SkSurface> dst(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii));
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci        if (!dst) {
123cb93a386Sopenharmony_ci            ERRORF(reporter, "Could not create surfaces for copy surface test.");
124cb93a386Sopenharmony_ci            continue;
125cb93a386Sopenharmony_ci        }
126cb93a386Sopenharmony_ci
127cb93a386Sopenharmony_ci        SkCanvas* dstCanvas = dst->getCanvas();
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_ci        dstCanvas->clear(SK_ColorRED);
130cb93a386Sopenharmony_ci        dst->flushAndSubmit();
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ci        SkPaint gammaPaint;
133cb93a386Sopenharmony_ci        gammaPaint.setBlendMode(SkBlendMode::kSrc);
134cb93a386Sopenharmony_ci        gammaPaint.setColorFilter(toSRGB ? SkColorFilters::LinearToSRGBGamma()
135cb93a386Sopenharmony_ci                                         : SkColorFilters::SRGBToLinearGamma());
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci        dstCanvas->drawImage(img, 0, 0, SkSamplingOptions(), &gammaPaint);
138cb93a386Sopenharmony_ci        dst->flushAndSubmit();
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ci        sk_memset32(read.get(), 0, kBaseSize.fWidth * kBaseSize.fHeight);
141cb93a386Sopenharmony_ci        if (!dst->readPixels(ii, read.get(), kRowBytes, 0, 0)) {
142cb93a386Sopenharmony_ci            ERRORF(reporter, "Error calling readPixels");
143cb93a386Sopenharmony_ci            continue;
144cb93a386Sopenharmony_ci        }
145cb93a386Sopenharmony_ci
146cb93a386Sopenharmony_ci        bool abort = false;
147cb93a386Sopenharmony_ci        // Validate that pixels were copied/transformed correctly.
148cb93a386Sopenharmony_ci        for (int y = 0; y < kBaseSize.fHeight && !abort; ++y) {
149cb93a386Sopenharmony_ci            for (int x = 0; x < kBaseSize.fWidth && !abort; ++x) {
150cb93a386Sopenharmony_ci                uint32_t r = read.get()[y * kBaseSize.fWidth + x];
151cb93a386Sopenharmony_ci                uint32_t s = srcPixels.get()[y * kBaseSize.fWidth + x];
152cb93a386Sopenharmony_ci                uint32_t expected;
153cb93a386Sopenharmony_ci                if (!check_gamma(s, r, toSRGB, error, &expected)) {
154cb93a386Sopenharmony_ci                    ERRORF(reporter, "Expected dst %d,%d to contain 0x%08x "
155cb93a386Sopenharmony_ci                           "from src 0x%08x and mode %s. Got %08x", x, y, expected, s,
156cb93a386Sopenharmony_ci                           toSRGB ? "ToSRGB" : "ToLinear", r);
157cb93a386Sopenharmony_ci                    abort = true;
158cb93a386Sopenharmony_ci                    break;
159cb93a386Sopenharmony_ci                }
160cb93a386Sopenharmony_ci            }
161cb93a386Sopenharmony_ci        }
162cb93a386Sopenharmony_ci    }
163cb93a386Sopenharmony_ci}
164