1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2016 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkArenaAlloc.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkColorSpaceXformSteps.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipeline.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineOpContexts.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineOpList.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
19*c8dee2aaSAndroid Build Coastguard Worker
20*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
21*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
22*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(srgb_roundtrip,r)23*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(srgb_roundtrip, r) {
24*c8dee2aaSAndroid Build Coastguard Worker uint32_t reds[256];
25*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 256; i++) {
26*c8dee2aaSAndroid Build Coastguard Worker reds[i] = i;
27*c8dee2aaSAndroid Build Coastguard Worker }
28*c8dee2aaSAndroid Build Coastguard Worker
29*c8dee2aaSAndroid Build Coastguard Worker SkRasterPipeline_MemoryCtx ptr = { reds, 0 };
30*c8dee2aaSAndroid Build Coastguard Worker
31*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB(),
32*c8dee2aaSAndroid Build Coastguard Worker linear = sRGB->makeLinearGamma();
33*c8dee2aaSAndroid Build Coastguard Worker const SkAlphaType upm = kUnpremul_SkAlphaType;
34*c8dee2aaSAndroid Build Coastguard Worker
35*c8dee2aaSAndroid Build Coastguard Worker SkColorSpaceXformSteps linearize{ sRGB.get(),upm, linear.get(),upm},
36*c8dee2aaSAndroid Build Coastguard Worker reencode {linear.get(),upm, sRGB.get(),upm};
37*c8dee2aaSAndroid Build Coastguard Worker
38*c8dee2aaSAndroid Build Coastguard Worker SkRasterPipeline_<256> p;
39*c8dee2aaSAndroid Build Coastguard Worker p.append(SkRasterPipelineOp::load_8888, &ptr);
40*c8dee2aaSAndroid Build Coastguard Worker linearize.apply(&p);
41*c8dee2aaSAndroid Build Coastguard Worker reencode .apply(&p);
42*c8dee2aaSAndroid Build Coastguard Worker p.append(SkRasterPipelineOp::store_8888, &ptr);
43*c8dee2aaSAndroid Build Coastguard Worker
44*c8dee2aaSAndroid Build Coastguard Worker p.run(0,0,256,1);
45*c8dee2aaSAndroid Build Coastguard Worker
46*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < 256; i++) {
47*c8dee2aaSAndroid Build Coastguard Worker if (reds[i] != (uint32_t)i) {
48*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "%d doesn't round trip, %u", i, reds[i]);
49*c8dee2aaSAndroid Build Coastguard Worker }
50*c8dee2aaSAndroid Build Coastguard Worker }
51*c8dee2aaSAndroid Build Coastguard Worker }
52*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(srgb_edge_cases,r)53*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(srgb_edge_cases, r) {
54*c8dee2aaSAndroid Build Coastguard Worker // We need to run at least 4 pixels to make sure we hit all specializations.
55*c8dee2aaSAndroid Build Coastguard Worker float colors[4][4] = { {0,1,1,1}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0} };
56*c8dee2aaSAndroid Build Coastguard Worker auto& color = colors[0];
57*c8dee2aaSAndroid Build Coastguard Worker
58*c8dee2aaSAndroid Build Coastguard Worker SkRasterPipeline_MemoryCtx dst = { &color, 0 };
59*c8dee2aaSAndroid Build Coastguard Worker
60*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB(),
61*c8dee2aaSAndroid Build Coastguard Worker linear = sRGB->makeLinearGamma();
62*c8dee2aaSAndroid Build Coastguard Worker const SkAlphaType upm = kUnpremul_SkAlphaType;
63*c8dee2aaSAndroid Build Coastguard Worker
64*c8dee2aaSAndroid Build Coastguard Worker SkColorSpaceXformSteps steps {linear.get(),upm, sRGB.get(),upm};
65*c8dee2aaSAndroid Build Coastguard Worker
66*c8dee2aaSAndroid Build Coastguard Worker SkSTArenaAlloc<256> alloc;
67*c8dee2aaSAndroid Build Coastguard Worker SkRasterPipeline p(&alloc);
68*c8dee2aaSAndroid Build Coastguard Worker p.appendConstantColor(&alloc, color);
69*c8dee2aaSAndroid Build Coastguard Worker steps.apply(&p);
70*c8dee2aaSAndroid Build Coastguard Worker p.append(SkRasterPipelineOp::store_f32, &dst);
71*c8dee2aaSAndroid Build Coastguard Worker p.run(0,0,4,1);
72*c8dee2aaSAndroid Build Coastguard Worker
73*c8dee2aaSAndroid Build Coastguard Worker if (color[0] != 0.0f) {
74*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "expected to_srgb() to map 0.0f to 0.0f, got %f", color[0]);
75*c8dee2aaSAndroid Build Coastguard Worker }
76*c8dee2aaSAndroid Build Coastguard Worker if (color[1] != 1.0f) {
77*c8dee2aaSAndroid Build Coastguard Worker float f = color[1];
78*c8dee2aaSAndroid Build Coastguard Worker uint32_t x;
79*c8dee2aaSAndroid Build Coastguard Worker memcpy(&x, &f, 4);
80*c8dee2aaSAndroid Build Coastguard Worker ERRORF(r, "expected to_srgb() to map 1.0f to 1.0f, got %f (%08x)", color[1], x);
81*c8dee2aaSAndroid Build Coastguard Worker }
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker
84*c8dee2aaSAndroid Build Coastguard Worker // Linearize and then re-encode pixel values, testing that the output is close to the input.
DEF_TEST(srgb_roundtrip_extended,r)85*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(srgb_roundtrip_extended, r) {
86*c8dee2aaSAndroid Build Coastguard Worker static const int kSteps = 128;
87*c8dee2aaSAndroid Build Coastguard Worker SkColor4f rgba[kSteps];
88*c8dee2aaSAndroid Build Coastguard Worker
89*c8dee2aaSAndroid Build Coastguard Worker auto expected = [=](int i) {
90*c8dee2aaSAndroid Build Coastguard Worker float scale = 10000.0f / (3*kSteps);
91*c8dee2aaSAndroid Build Coastguard Worker return SkColor4f{
92*c8dee2aaSAndroid Build Coastguard Worker (3*i+0) * scale,
93*c8dee2aaSAndroid Build Coastguard Worker (3*i+1) * scale,
94*c8dee2aaSAndroid Build Coastguard Worker (3*i+2) * scale,
95*c8dee2aaSAndroid Build Coastguard Worker 1.0f,
96*c8dee2aaSAndroid Build Coastguard Worker };
97*c8dee2aaSAndroid Build Coastguard Worker };
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kSteps; i++) {
100*c8dee2aaSAndroid Build Coastguard Worker rgba[i] = expected(i);
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker
103*c8dee2aaSAndroid Build Coastguard Worker SkRasterPipeline_MemoryCtx ptr = { rgba, 0 };
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkColorSpace> cs = SkColorSpace::MakeSRGB();
106*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkColorSpace> linear = cs->makeLinearGamma();
107*c8dee2aaSAndroid Build Coastguard Worker const SkAlphaType upm = kUnpremul_SkAlphaType;
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker SkColorSpaceXformSteps linearize{ cs.get(),upm, linear.get(),upm},
110*c8dee2aaSAndroid Build Coastguard Worker reencode {linear.get(),upm, cs.get(),upm};
111*c8dee2aaSAndroid Build Coastguard Worker
112*c8dee2aaSAndroid Build Coastguard Worker SkRasterPipeline_<256> p;
113*c8dee2aaSAndroid Build Coastguard Worker p.append(SkRasterPipelineOp::load_f32, &ptr);
114*c8dee2aaSAndroid Build Coastguard Worker linearize.apply(&p);
115*c8dee2aaSAndroid Build Coastguard Worker reencode .apply(&p);
116*c8dee2aaSAndroid Build Coastguard Worker p.append(SkRasterPipelineOp::store_f32, &ptr);
117*c8dee2aaSAndroid Build Coastguard Worker p.run(0,0,kSteps,1);
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker auto close = [=](float x, float y) {
120*c8dee2aaSAndroid Build Coastguard Worker return x == y
121*c8dee2aaSAndroid Build Coastguard Worker || (x/y < 1.001f && y/x < 1.001f);
122*c8dee2aaSAndroid Build Coastguard Worker };
123*c8dee2aaSAndroid Build Coastguard Worker
124*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < kSteps; i++) {
125*c8dee2aaSAndroid Build Coastguard Worker SkColor4f want = expected(i);
126*c8dee2aaSAndroid Build Coastguard Worker #if 0
127*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("got %g %g %g, want %g %g %g\n",
128*c8dee2aaSAndroid Build Coastguard Worker rgba[i].fR, rgba[i].fG, rgba[i].fB,
129*c8dee2aaSAndroid Build Coastguard Worker want.fR, want.fG, want.fB);
130*c8dee2aaSAndroid Build Coastguard Worker #endif
131*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, close(rgba[i].fR, want.fR));
132*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, close(rgba[i].fG, want.fG));
133*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(r, close(rgba[i].fB, want.fB));
134*c8dee2aaSAndroid Build Coastguard Worker }
135*c8dee2aaSAndroid Build Coastguard Worker }
136