OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "Resources.h" | 8 #include "Resources.h" |
9 #include "SkCodec.h" | 9 #include "SkCodec.h" |
10 #include "SkCodecPriv.h" | 10 #include "SkCodecPriv.h" |
11 #include "SkColorPriv.h" | 11 #include "SkColorPriv.h" |
12 #include "SkColorSpace.h" | 12 #include "SkColorSpace.h" |
| 13 #include "SkColorSpace_A2B.h" |
13 #include "SkColorSpace_Base.h" | 14 #include "SkColorSpace_Base.h" |
14 #include "SkColorSpace_XYZ.h" | 15 #include "SkColorSpace_XYZ.h" |
15 #include "SkColorSpaceXform_Base.h" | 16 #include "SkColorSpaceXform_Base.h" |
16 #include "Test.h" | 17 #include "Test.h" |
17 | 18 |
18 class ColorSpaceXformTest { | 19 class ColorSpaceXformTest { |
19 public: | 20 public: |
20 static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform(const sk_sp<Sk
Gammas>& gammas) { | 21 static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform(const sk_sp<Sk
Gammas>& gammas) { |
21 // Logically we can pass any matrix here. For simplicty, pass I(), i.e.
D50 XYZ gamut. | 22 // Logically we can pass any matrix here. For simplicty, pass I(), i.e.
D50 XYZ gamut. |
22 sk_sp<SkColorSpace> space(new SkColorSpace_XYZ( | 23 sk_sp<SkColorSpace> space(new SkColorSpace_XYZ( |
23 kNonStandard_SkGammaNamed, gammas, SkMatrix::I(), nullptr)); | 24 kNonStandard_SkGammaNamed, gammas, SkMatrix::I(), nullptr)); |
24 | 25 |
25 // Use special testing entry point, so we don't skip the xform, even tho
ugh src == dst. | 26 // Use special testing entry point, so we don't skip the xform, even tho
ugh src == dst. |
26 return SlowIdentityXform(static_cast<SkColorSpace_XYZ*>(space.get())); | 27 return SlowIdentityXform(static_cast<SkColorSpace_XYZ*>(space.get())); |
27 } | 28 } |
| 29 |
| 30 static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform_A2B( |
| 31 SkGammaNamed gammaNamed, const sk_sp<SkGammas>& gammas) { |
| 32 std::vector<SkColorSpace_A2B::Element> srcElements; |
| 33 const float values[16] = { |
| 34 1.0f, 0.3f, -0.6f, 0.0f, |
| 35 0.5f, -0.8f, 3.2f, 0.0f, |
| 36 -0.5f, 1.3f, -0.7f, 0.0f, |
| 37 0.0f, 0.0f, 0.0f, 1.0f |
| 38 }; |
| 39 SkMatrix44 arbitraryMatrix{SkMatrix44::kUninitialized_Constructor}; |
| 40 arbitraryMatrix.setRowMajorf(values); |
| 41 if (kNonStandard_SkGammaNamed == gammaNamed) { |
| 42 srcElements.push_back(SkColorSpace_A2B::Element(gammas)); |
| 43 } |
| 44 else { |
| 45 srcElements.push_back(SkColorSpace_A2B::Element(gammaNamed)); |
| 46 } |
| 47 srcElements.push_back(SkColorSpace_A2B::Element(arbitraryMatrix)); |
| 48 auto srcSpace = ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PC
S::kXYZ, |
| 49 std::move(srcElement
s)); |
| 50 sk_sp<SkColorSpace> dstSpace(new SkColorSpace_XYZ(gammaNamed, gammas, ar
bitraryMatrix, |
| 51 nullptr)); |
| 52 |
| 53 return SkColorSpaceXform::New(static_cast<SkColorSpace_A2B*>(srcSpace.ge
t()), |
| 54 static_cast<SkColorSpace_XYZ*>(dstSpace.ge
t())); |
| 55 } |
| 56 |
| 57 static sk_sp<SkColorSpace> CreateA2BSpace(SkColorSpace_A2B::PCS pcs, |
| 58 std::vector<SkColorSpace_A2B::Elem
ent> elements) { |
| 59 return sk_sp<SkColorSpace>(new SkColorSpace_A2B(pcs, nullptr, std::move(
elements))); |
| 60 } |
28 }; | 61 }; |
29 | 62 |
30 static bool almost_equal(int x, int y) { | 63 static bool almost_equal(int x, int y) { |
31 return SkTAbs(x - y) <= 1; | 64 const auto diff = SkTAbs(x - y); |
| 65 return diff <= 1 || (x <= 3 && y <= 3 && diff <= 3); |
32 } | 66 } |
33 | 67 |
34 static void test_identity_xform(skiatest::Reporter* r, const sk_sp<SkGammas>& ga
mmas, | 68 static void test_identity_xform(skiatest::Reporter* r, const sk_sp<SkGammas>& ga
mmas, |
35 bool repeat) { | 69 bool repeat) { |
36 // Arbitrary set of 10 pixels | 70 // Arbitrary set of 10 pixels |
37 constexpr int width = 10; | 71 constexpr int width = 10; |
38 constexpr uint32_t srcPixels[width] = { | 72 constexpr uint32_t srcPixels[width] = { |
39 0xFFABCDEF, 0xFF146829, 0xFF382759, 0xFF184968, 0xFFDE8271, | 73 0xFFABCDEF, 0xFF146829, 0xFF382759, 0xFF184968, 0xFFDE8271, |
40 0xFF32AB52, 0xFF0383BC, 0xFF000102, 0xFFFFFFFF, 0xFFDDEEFF, }; | 74 0xFF32AB52, 0xFF0383BC, 0xFF000102, 0xFFFFFFFF, 0xFFDDEEFF, }; |
41 uint32_t dstPixels[width]; | 75 uint32_t dstPixels[width]; |
(...skipping 18 matching lines...) Expand all Loading... |
60 SkGetPackedA32(dstPixels[i]))); | 94 SkGetPackedA32(dstPixels[i]))); |
61 } | 95 } |
62 | 96 |
63 if (repeat) { | 97 if (repeat) { |
64 // We should cache part of the transform after the run. So it is intere
sting | 98 // We should cache part of the transform after the run. So it is intere
sting |
65 // to make sure it still runs correctly the second time. | 99 // to make sure it still runs correctly the second time. |
66 test_identity_xform(r, gammas, false); | 100 test_identity_xform(r, gammas, false); |
67 } | 101 } |
68 } | 102 } |
69 | 103 |
| 104 static void test_identity_xform_A2B(skiatest::Reporter* r, SkGammaNamed gammaNam
ed, |
| 105 const sk_sp<SkGammas>& gammas, bool repeat)
{ |
| 106 // Arbitrary set of 10 pixels |
| 107 constexpr int width = 10; |
| 108 constexpr uint32_t srcPixels[width] = { |
| 109 0xFFABCDEF, 0xFF146829, 0xFF382759, 0xFF184968, 0xFFDE8271, |
| 110 0xFF32AB52, 0xFF0383BC, 0xFF000102, 0xFFFFFFFF, 0xFFDDEEFF, }; |
| 111 uint32_t dstPixels[width]; |
| 112 |
| 113 // Create and perform an identity xform. |
| 114 auto xform = ColorSpaceXformTest::CreateIdentityXform_A2B(gammaNamed, gammas
); |
| 115 bool result = xform->apply(select_xform_format(kN32_SkColorType), dstPixels, |
| 116 SkColorSpaceXform::kBGRA_8888_ColorFormat, srcPix
els, width, |
| 117 kOpaque_SkAlphaType); |
| 118 REPORTER_ASSERT(r, result); |
| 119 |
| 120 // Since the src->dst matrix is the identity, and the gamma curves match, |
| 121 // the pixels should be unchanged. |
| 122 for (int i = 0; i < width; i++) { |
| 123 REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 0) & 0xFF), |
| 124 SkGetPackedB32(dstPixels[i]))); |
| 125 REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 8) & 0xFF), |
| 126 SkGetPackedG32(dstPixels[i]))); |
| 127 REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 16) & 0xFF), |
| 128 SkGetPackedR32(dstPixels[i]))); |
| 129 REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 24) & 0xFF), |
| 130 SkGetPackedA32(dstPixels[i]))); |
| 131 } |
| 132 |
| 133 if (repeat) { |
| 134 // We should cache part of the transform after the run. So it is intere
sting |
| 135 // to make sure it still runs correctly the second time. |
| 136 test_identity_xform_A2B(r, gammaNamed, gammas, false); |
| 137 } |
| 138 } |
| 139 |
70 DEF_TEST(ColorSpaceXform_TableGamma, r) { | 140 DEF_TEST(ColorSpaceXform_TableGamma, r) { |
71 // Lookup-table based gamma curves | 141 // Lookup-table based gamma curves |
72 constexpr size_t tableSize = 10; | 142 constexpr size_t tableSize = 10; |
73 void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize)
; | 143 void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize)
; |
74 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas()); | 144 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas()); |
75 gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::
kTable_Type; | 145 gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::
kTable_Type; |
76 gammas->fRedData.fTable.fSize = gammas->fGreenData.fTable.fSize = | 146 gammas->fRedData.fTable.fSize = gammas->fGreenData.fTable.fSize = |
77 gammas->fBlueData.fTable.fSize = tableSize; | 147 gammas->fBlueData.fTable.fSize = tableSize; |
78 gammas->fRedData.fTable.fOffset = gammas->fGreenData.fTable.fOffset = | 148 gammas->fRedData.fTable.fOffset = gammas->fGreenData.fTable.fOffset = |
79 gammas->fBlueData.fTable.fOffset = 0; | 149 gammas->fBlueData.fTable.fOffset = 0; |
80 float* table = SkTAddOffset<float>(memory, sizeof(SkGammas)); | 150 float* table = SkTAddOffset<float>(memory, sizeof(SkGammas)); |
81 | 151 |
82 table[0] = 0.00f; | 152 table[0] = 0.00f; |
83 table[1] = 0.05f; | 153 table[1] = 0.05f; |
84 table[2] = 0.10f; | 154 table[2] = 0.10f; |
85 table[3] = 0.15f; | 155 table[3] = 0.15f; |
86 table[4] = 0.25f; | 156 table[4] = 0.25f; |
87 table[5] = 0.35f; | 157 table[5] = 0.35f; |
88 table[6] = 0.45f; | 158 table[6] = 0.45f; |
89 table[7] = 0.60f; | 159 table[7] = 0.60f; |
90 table[8] = 0.75f; | 160 table[8] = 0.75f; |
91 table[9] = 1.00f; | 161 table[9] = 1.00f; |
92 test_identity_xform(r, gammas, true); | 162 test_identity_xform(r, gammas, true); |
| 163 test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true); |
93 } | 164 } |
94 | 165 |
95 DEF_TEST(ColorSpaceXform_ParametricGamma, r) { | 166 DEF_TEST(ColorSpaceXform_ParametricGamma, r) { |
96 // Parametric gamma curves | 167 // Parametric gamma curves |
97 void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransfe
rFn)); | 168 void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransfe
rFn)); |
98 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas()); | 169 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas()); |
99 gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::
kParam_Type; | 170 gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::
kParam_Type; |
100 gammas->fRedData.fParamOffset = gammas->fGreenData.fParamOffset = | 171 gammas->fRedData.fParamOffset = gammas->fGreenData.fParamOffset = |
101 gammas->fBlueData.fParamOffset = 0; | 172 gammas->fBlueData.fParamOffset = 0; |
102 SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn> | 173 SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn> |
103 (memory, sizeof(SkGammas)); | 174 (memory, sizeof(SkGammas)); |
104 | 175 |
105 // Interval, switch xforms at 0.0031308f | 176 // Interval, switch xforms at 0.0031308f |
106 params->fD = 0.04045f; | 177 params->fD = 0.04045f; |
107 | 178 |
108 // First equation: | 179 // First equation: |
109 params->fE = 1.0f / 12.92f; | 180 params->fE = 1.0f / 12.92f; |
110 params->fF = 0.0f; | 181 params->fF = 0.0f; |
111 | 182 |
112 // Second equation: | 183 // Second equation: |
113 // Note that the function is continuous (it's actually sRGB). | 184 // Note that the function is continuous (it's actually sRGB). |
114 params->fA = 1.0f / 1.055f; | 185 params->fA = 1.0f / 1.055f; |
115 params->fB = 0.055f / 1.055f; | 186 params->fB = 0.055f / 1.055f; |
116 params->fC = 0.0f; | 187 params->fC = 0.0f; |
117 params->fG = 2.4f; | 188 params->fG = 2.4f; |
118 test_identity_xform(r, gammas, true); | 189 test_identity_xform(r, gammas, true); |
| 190 test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true); |
119 } | 191 } |
120 | 192 |
121 DEF_TEST(ColorSpaceXform_ExponentialGamma, r) { | 193 DEF_TEST(ColorSpaceXform_ExponentialGamma, r) { |
122 // Exponential gamma curves | 194 // Exponential gamma curves |
123 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas()); | 195 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas()); |
124 gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::
kValue_Type; | 196 gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::
kValue_Type; |
125 gammas->fRedData.fValue = gammas->fGreenData.fValue = gammas->fBlueData.fVal
ue = 1.4f; | 197 gammas->fRedData.fValue = gammas->fGreenData.fValue = gammas->fBlueData.fVal
ue = 1.4f; |
126 test_identity_xform(r, gammas, true); | 198 test_identity_xform(r, gammas, true); |
| 199 test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true); |
127 } | 200 } |
128 | 201 |
129 DEF_TEST(ColorSpaceXform_NamedGamma, r) { | 202 DEF_TEST(ColorSpaceXform_NamedGamma, r) { |
130 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas()); | 203 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas()); |
131 gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::
kNamed_Type; | 204 gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::
kNamed_Type; |
132 gammas->fRedData.fNamed = kSRGB_SkGammaNamed; | 205 gammas->fRedData.fNamed = kSRGB_SkGammaNamed; |
133 gammas->fGreenData.fNamed = k2Dot2Curve_SkGammaNamed; | 206 gammas->fGreenData.fNamed = k2Dot2Curve_SkGammaNamed; |
134 gammas->fBlueData.fNamed = kLinear_SkGammaNamed; | 207 gammas->fBlueData.fNamed = kLinear_SkGammaNamed; |
135 test_identity_xform(r, gammas, true); | 208 test_identity_xform(r, gammas, true); |
| 209 test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true); |
| 210 test_identity_xform_A2B(r, kSRGB_SkGammaNamed, nullptr, true); |
| 211 test_identity_xform_A2B(r, k2Dot2Curve_SkGammaNamed, nullptr, true); |
| 212 test_identity_xform_A2B(r, kLinear_SkGammaNamed, nullptr, true); |
136 } | 213 } |
137 | 214 |
138 DEF_TEST(ColorSpaceXform_NonMatchingGamma, r) { | 215 DEF_TEST(ColorSpaceXform_NonMatchingGamma, r) { |
139 constexpr size_t tableSize = 10; | 216 constexpr size_t tableSize = 10; |
140 void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize
+ | 217 void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize
+ |
141 sizeof(SkColorSpaceTransferFn)); | 218 sizeof(SkColorSpaceTransferFn)); |
142 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas()); | 219 sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas()); |
143 | 220 |
144 float* table = SkTAddOffset<float>(memory, sizeof(SkGammas)); | 221 float* table = SkTAddOffset<float>(memory, sizeof(SkGammas)); |
145 table[0] = 0.00f; | 222 table[0] = 0.00f; |
(...skipping 21 matching lines...) Expand all Loading... |
167 gammas->fRedData.fValue = 1.2f; | 244 gammas->fRedData.fValue = 1.2f; |
168 | 245 |
169 gammas->fGreenType = SkGammas::Type::kTable_Type; | 246 gammas->fGreenType = SkGammas::Type::kTable_Type; |
170 gammas->fGreenData.fTable.fSize = tableSize; | 247 gammas->fGreenData.fTable.fSize = tableSize; |
171 gammas->fGreenData.fTable.fOffset = 0; | 248 gammas->fGreenData.fTable.fOffset = 0; |
172 | 249 |
173 gammas->fBlueType = SkGammas::Type::kParam_Type; | 250 gammas->fBlueType = SkGammas::Type::kParam_Type; |
174 gammas->fBlueData.fParamOffset = sizeof(float) * tableSize; | 251 gammas->fBlueData.fParamOffset = sizeof(float) * tableSize; |
175 | 252 |
176 test_identity_xform(r, gammas, true); | 253 test_identity_xform(r, gammas, true); |
| 254 test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true); |
177 } | 255 } |
178 | 256 |
| 257 DEF_TEST(ColorSpaceXform_A2BCLUT, r) { |
| 258 constexpr int inputChannels = 3; |
| 259 constexpr int gp = 4; // # grid points |
| 260 |
| 261 constexpr int numEntries = gp*gp*gp*3; |
| 262 uint8_t gridPoints[3] = {gp, gp, gp}; |
| 263 void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) *
numEntries); |
| 264 sk_sp<SkColorLookUpTable> colorLUT(new (memory) SkColorLookUpTable(inputChan
nels, gridPoints)); |
| 265 // make a CLUT that rotates R, G, and B ie R->G, G->B, B->R |
| 266 float* table = SkTAddOffset<float>(memory, sizeof(SkColorLookUpTable)); |
| 267 for (int r = 0; r < gp; ++r) { |
| 268 for (int g = 0; g < gp; ++g) { |
| 269 for (int b = 0; b < gp; ++b) { |
| 270 table[3*(gp*gp*r + gp*g + b) + 0] = g * (1.f / (gp - 1.f)); |
| 271 table[3*(gp*gp*r + gp*g + b) + 1] = b * (1.f / (gp - 1.f)); |
| 272 table[3*(gp*gp*r + gp*g + b) + 2] = r * (1.f / (gp - 1.f)); |
| 273 } |
| 274 } |
| 275 } |
| 276 |
| 277 // build an even distribution of pixels every (7 / 255) steps |
| 278 // to test the xform on |
| 279 constexpr int pixelgp = 7; |
| 280 constexpr int numPixels = pixelgp*pixelgp*pixelgp; |
| 281 SkAutoTMalloc<uint32_t> srcPixels(numPixels); |
| 282 int srcIndex = 0; |
| 283 for (int r = 0; r < pixelgp; ++r) { |
| 284 for (int g = 0; g < pixelgp; ++g) { |
| 285 for (int b = 0; b < pixelgp; ++b) { |
| 286 const int red = (int) (r * (255.f / (pixelgp - 1.f))); |
| 287 const int green = (int) (g * (255.f / (pixelgp - 1.f))); |
| 288 const int blue = (int) (b * (255.f / (pixelgp - 1.f))); |
| 289 srcPixels[srcIndex] = SkColorSetRGB(red, green, blue); |
| 290 ++srcIndex; |
| 291 } |
| 292 } |
| 293 } |
| 294 SkAutoTMalloc<uint32_t> dstPixels(numPixels); |
| 295 |
| 296 // src space is identity besides CLUT |
| 297 std::vector<SkColorSpace_A2B::Element> srcElements; |
| 298 srcElements.push_back(SkColorSpace_A2B::Element(std::move(colorLUT))); |
| 299 auto srcSpace = ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::k
XYZ, |
| 300 std::move(srcElements)); |
| 301 // dst space is entirely identity |
| 302 auto dstSpace = SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamm
a, SkMatrix44::I()); |
| 303 auto xform = SkColorSpaceXform::New(srcSpace.get(), dstSpace.get()); |
| 304 bool result = xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, dstPix
els.get(), |
| 305 SkColorSpaceXform::kRGBA_8888_ColorFormat, srcPix
els.get(), |
| 306 numPixels, kOpaque_SkAlphaType); |
| 307 REPORTER_ASSERT(r, result); |
| 308 |
| 309 for (int i = 0; i < numPixels; ++i) { |
| 310 REPORTER_ASSERT(r, almost_equal(SkColorGetR(srcPixels[i]), |
| 311 SkColorGetG(dstPixels[i]))); |
| 312 REPORTER_ASSERT(r, almost_equal(SkColorGetG(srcPixels[i]), |
| 313 SkColorGetB(dstPixels[i]))); |
| 314 REPORTER_ASSERT(r, almost_equal(SkColorGetB(srcPixels[i]), |
| 315 SkColorGetR(dstPixels[i]))); |
| 316 } |
| 317 } |
| 318 |
OLD | NEW |