msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 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 | #include "Resources.h" |
| 9 | #include "SkCodec.h" |
msarett | c044461 | 2016-09-16 11:45:58 -0700 | [diff] [blame] | 10 | #include "SkCodecPriv.h" |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 11 | #include "SkColorPriv.h" |
| 12 | #include "SkColorSpace.h" |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 13 | #include "SkColorSpace_A2B.h" |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 14 | #include "SkColorSpace_Base.h" |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 15 | #include "SkColorSpace_XYZ.h" |
msarett | 31d097e8 | 2016-10-11 12:15:03 -0700 | [diff] [blame] | 16 | #include "SkColorSpaceXform_Base.h" |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 17 | #include "Test.h" |
| 18 | |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 19 | static constexpr int kChannels = 3; |
| 20 | |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 21 | class ColorSpaceXformTest { |
| 22 | public: |
mtklein | b435963 | 2016-07-18 11:16:14 -0700 | [diff] [blame] | 23 | static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform(const sk_sp<SkGammas>& gammas) { |
| 24 | // Logically we can pass any matrix here. For simplicty, pass I(), i.e. D50 XYZ gamut. |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 25 | sk_sp<SkColorSpace> space(new SkColorSpace_XYZ( |
Matt Sarett | f388093 | 2017-03-24 10:06:03 -0400 | [diff] [blame] | 26 | kNonStandard_SkGammaNamed, gammas, SkMatrix::I(), nullptr)); |
msarett | 9dc6cf6 | 2016-08-23 17:53:06 -0700 | [diff] [blame] | 27 | |
| 28 | // Use special testing entry point, so we don't skip the xform, even though src == dst. |
raftias | 9488833 | 2016-10-18 10:02:51 -0700 | [diff] [blame] | 29 | return SlowIdentityXform(static_cast<SkColorSpace_XYZ*>(space.get())); |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 30 | } |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 31 | |
| 32 | static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform_A2B( |
| 33 | SkGammaNamed gammaNamed, const sk_sp<SkGammas>& gammas) { |
| 34 | std::vector<SkColorSpace_A2B::Element> srcElements; |
| 35 | // sRGB |
| 36 | const float values[16] = { |
| 37 | 0.4358f, 0.3853f, 0.1430f, 0.0f, |
| 38 | 0.2224f, 0.7170f, 0.0606f, 0.0f, |
| 39 | 0.0139f, 0.0971f, 0.7139f, 0.0f, |
| 40 | 0.0000f, 0.0000f, 0.0000f, 1.0f |
| 41 | }; |
| 42 | SkMatrix44 arbitraryMatrix{SkMatrix44::kUninitialized_Constructor}; |
| 43 | arbitraryMatrix.setRowMajorf(values); |
| 44 | if (kNonStandard_SkGammaNamed == gammaNamed) { |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 45 | SkASSERT(gammas); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 46 | srcElements.push_back(SkColorSpace_A2B::Element(gammas)); |
| 47 | } else { |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 48 | srcElements.push_back(SkColorSpace_A2B::Element(gammaNamed, kChannels)); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 49 | } |
| 50 | srcElements.push_back(SkColorSpace_A2B::Element(arbitraryMatrix)); |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 51 | auto srcSpace = |
| 52 | ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::kXYZ, |
Matt Sarett | 523116d | 2017-01-12 18:36:38 -0500 | [diff] [blame] | 53 | SkColorSpace_Base::kRGB_ICCTypeFlag, |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 54 | std::move(srcElements)); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 55 | sk_sp<SkColorSpace> dstSpace(new SkColorSpace_XYZ(gammaNamed, gammas, arbitraryMatrix, |
Matt Sarett | f388093 | 2017-03-24 10:06:03 -0400 | [diff] [blame] | 56 | nullptr)); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 57 | |
| 58 | return SkColorSpaceXform::New(static_cast<SkColorSpace_A2B*>(srcSpace.get()), |
| 59 | static_cast<SkColorSpace_XYZ*>(dstSpace.get())); |
| 60 | } |
| 61 | |
| 62 | static sk_sp<SkColorSpace> CreateA2BSpace(SkColorSpace_A2B::PCS pcs, |
Matt Sarett | 523116d | 2017-01-12 18:36:38 -0500 | [diff] [blame] | 63 | SkColorSpace_Base::ICCTypeFlag iccType, |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 64 | std::vector<SkColorSpace_A2B::Element> elements) { |
Matt Sarett | 523116d | 2017-01-12 18:36:38 -0500 | [diff] [blame] | 65 | return sk_sp<SkColorSpace>(new SkColorSpace_A2B(iccType, std::move(elements), |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 66 | pcs, nullptr)); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 67 | } |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 68 | }; |
| 69 | |
Mike Klein | c7d9c0b | 2017-04-17 14:43:59 -0400 | [diff] [blame] | 70 | static bool almost_equal(int x, int y, int tol=1) { |
| 71 | return SkTAbs(x-y) <= tol; |
msarett | b390676 | 2016-06-22 14:07:48 -0700 | [diff] [blame] | 72 | } |
| 73 | |
Matt Sarett | f489886 | 2016-10-16 10:20:41 -0400 | [diff] [blame] | 74 | static void test_identity_xform(skiatest::Reporter* r, const sk_sp<SkGammas>& gammas, |
| 75 | bool repeat) { |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 76 | // Arbitrary set of 10 pixels |
| 77 | constexpr int width = 10; |
| 78 | constexpr uint32_t srcPixels[width] = { |
| 79 | 0xFFABCDEF, 0xFF146829, 0xFF382759, 0xFF184968, 0xFFDE8271, |
msarett | b390676 | 2016-06-22 14:07:48 -0700 | [diff] [blame] | 80 | 0xFF32AB52, 0xFF0383BC, 0xFF000102, 0xFFFFFFFF, 0xFFDDEEFF, }; |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 81 | uint32_t dstPixels[width]; |
| 82 | |
mtklein | b435963 | 2016-07-18 11:16:14 -0700 | [diff] [blame] | 83 | // Create and perform an identity xform. |
| 84 | std::unique_ptr<SkColorSpaceXform> xform = ColorSpaceXformTest::CreateIdentityXform(gammas); |
msarett | 31d097e8 | 2016-10-11 12:15:03 -0700 | [diff] [blame] | 85 | bool result = xform->apply(select_xform_format(kN32_SkColorType), dstPixels, |
| 86 | SkColorSpaceXform::kBGRA_8888_ColorFormat, srcPixels, width, |
| 87 | kOpaque_SkAlphaType); |
| 88 | REPORTER_ASSERT(r, result); |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 89 | |
mtklein | b435963 | 2016-07-18 11:16:14 -0700 | [diff] [blame] | 90 | // Since the src->dst matrix is the identity, and the gamma curves match, |
| 91 | // the pixels should be unchanged. |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 92 | for (int i = 0; i < width; i++) { |
msarett | b390676 | 2016-06-22 14:07:48 -0700 | [diff] [blame] | 93 | REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 0) & 0xFF), |
msarett | cf7b877 | 2016-09-22 12:37:04 -0700 | [diff] [blame] | 94 | SkGetPackedB32(dstPixels[i]))); |
msarett | b390676 | 2016-06-22 14:07:48 -0700 | [diff] [blame] | 95 | REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 8) & 0xFF), |
msarett | 9dc6cf6 | 2016-08-23 17:53:06 -0700 | [diff] [blame] | 96 | SkGetPackedG32(dstPixels[i]))); |
msarett | b390676 | 2016-06-22 14:07:48 -0700 | [diff] [blame] | 97 | REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 16) & 0xFF), |
msarett | cf7b877 | 2016-09-22 12:37:04 -0700 | [diff] [blame] | 98 | SkGetPackedR32(dstPixels[i]))); |
msarett | b390676 | 2016-06-22 14:07:48 -0700 | [diff] [blame] | 99 | REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 24) & 0xFF), |
msarett | 9dc6cf6 | 2016-08-23 17:53:06 -0700 | [diff] [blame] | 100 | SkGetPackedA32(dstPixels[i]))); |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 101 | } |
Matt Sarett | f489886 | 2016-10-16 10:20:41 -0400 | [diff] [blame] | 102 | |
| 103 | if (repeat) { |
| 104 | // We should cache part of the transform after the run. So it is interesting |
| 105 | // to make sure it still runs correctly the second time. |
| 106 | test_identity_xform(r, gammas, false); |
| 107 | } |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 108 | } |
| 109 | |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 110 | static void test_identity_xform_A2B(skiatest::Reporter* r, SkGammaNamed gammaNamed, |
Mike Klein | c7d9c0b | 2017-04-17 14:43:59 -0400 | [diff] [blame] | 111 | const sk_sp<SkGammas>& gammas, int tol=1) { |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 112 | // Arbitrary set of 10 pixels |
| 113 | constexpr int width = 10; |
| 114 | constexpr uint32_t srcPixels[width] = { |
| 115 | 0xFFABCDEF, 0xFF146829, 0xFF382759, 0xFF184968, 0xFFDE8271, |
| 116 | 0xFF32AB52, 0xFF0383BC, 0xFF000102, 0xFFFFFFFF, 0xFFDDEEFF, }; |
| 117 | uint32_t dstPixels[width]; |
| 118 | |
| 119 | // Create and perform an identity xform. |
| 120 | auto xform = ColorSpaceXformTest::CreateIdentityXform_A2B(gammaNamed, gammas); |
| 121 | bool result = xform->apply(select_xform_format(kN32_SkColorType), dstPixels, |
| 122 | SkColorSpaceXform::kBGRA_8888_ColorFormat, srcPixels, width, |
| 123 | kOpaque_SkAlphaType); |
| 124 | REPORTER_ASSERT(r, result); |
| 125 | |
| 126 | // Since the src->dst matrix is the identity, and the gamma curves match, |
Mike Klein | c7d9c0b | 2017-04-17 14:43:59 -0400 | [diff] [blame] | 127 | // the pixels should be ~unchanged. |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 128 | for (int i = 0; i < width; i++) { |
| 129 | REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 0) & 0xFF), |
Mike Klein | c7d9c0b | 2017-04-17 14:43:59 -0400 | [diff] [blame] | 130 | SkGetPackedB32(dstPixels[i]), tol)); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 131 | REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 8) & 0xFF), |
Mike Klein | c7d9c0b | 2017-04-17 14:43:59 -0400 | [diff] [blame] | 132 | SkGetPackedG32(dstPixels[i]), tol)); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 133 | REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 16) & 0xFF), |
Mike Klein | c7d9c0b | 2017-04-17 14:43:59 -0400 | [diff] [blame] | 134 | SkGetPackedR32(dstPixels[i]), tol)); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 135 | REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 24) & 0xFF), |
Mike Klein | c7d9c0b | 2017-04-17 14:43:59 -0400 | [diff] [blame] | 136 | SkGetPackedA32(dstPixels[i]), tol)); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 137 | } |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 138 | } |
| 139 | |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 140 | DEF_TEST(ColorSpaceXform_TableGamma, r) { |
| 141 | // Lookup-table based gamma curves |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 142 | constexpr size_t tableSize = 10; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 143 | void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize); |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 144 | sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels)); |
| 145 | for (int i = 0; i < kChannels; ++i) { |
| 146 | gammas->fType[i] = SkGammas::Type::kTable_Type; |
| 147 | gammas->fData[i].fTable.fSize = tableSize; |
| 148 | gammas->fData[i].fTable.fOffset = 0; |
| 149 | } |
| 150 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 151 | float* table = SkTAddOffset<float>(memory, sizeof(SkGammas)); |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 152 | table[0] = 0.00f; |
| 153 | table[1] = 0.05f; |
| 154 | table[2] = 0.10f; |
| 155 | table[3] = 0.15f; |
| 156 | table[4] = 0.25f; |
| 157 | table[5] = 0.35f; |
| 158 | table[6] = 0.45f; |
| 159 | table[7] = 0.60f; |
| 160 | table[8] = 0.75f; |
| 161 | table[9] = 1.00f; |
Mike Klein | c7d9c0b | 2017-04-17 14:43:59 -0400 | [diff] [blame] | 162 | // This table's pretty small compared to real ones in the wild (think 256), |
| 163 | // so we give test_identity_xform_A2B a wide tolerance. |
| 164 | // This lets us implement table transfer functions with a single lookup. |
| 165 | const int tolerance = 13; |
| 166 | |
Matt Sarett | f489886 | 2016-10-16 10:20:41 -0400 | [diff] [blame] | 167 | test_identity_xform(r, gammas, true); |
Mike Klein | c7d9c0b | 2017-04-17 14:43:59 -0400 | [diff] [blame] | 168 | test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, tolerance); |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | DEF_TEST(ColorSpaceXform_ParametricGamma, r) { |
| 172 | // Parametric gamma curves |
Matt Sarett | df44fc5 | 2016-10-11 16:57:50 -0400 | [diff] [blame] | 173 | void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn)); |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 174 | sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels)); |
| 175 | for (int i = 0; i < kChannels; ++i) { |
| 176 | gammas->fType[i] = SkGammas::Type::kParam_Type; |
| 177 | gammas->fData[i].fParamOffset = 0; |
| 178 | } |
| 179 | |
Matt Sarett | df44fc5 | 2016-10-11 16:57:50 -0400 | [diff] [blame] | 180 | SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn> |
| 181 | (memory, sizeof(SkGammas)); |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 182 | |
Matt Sarett | 2410717 | 2016-12-19 14:33:35 -0500 | [diff] [blame] | 183 | // Interval. |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 184 | params->fD = 0.04045f; |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 185 | |
msarett | b390676 | 2016-06-22 14:07:48 -0700 | [diff] [blame] | 186 | // First equation: |
Matt Sarett | 2410717 | 2016-12-19 14:33:35 -0500 | [diff] [blame] | 187 | params->fC = 1.0f / 12.92f; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 188 | params->fF = 0.0f; |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 189 | |
msarett | b390676 | 2016-06-22 14:07:48 -0700 | [diff] [blame] | 190 | // Second equation: |
| 191 | // Note that the function is continuous (it's actually sRGB). |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 192 | params->fA = 1.0f / 1.055f; |
| 193 | params->fB = 0.055f / 1.055f; |
Matt Sarett | 2410717 | 2016-12-19 14:33:35 -0500 | [diff] [blame] | 194 | params->fE = 0.0f; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 195 | params->fG = 2.4f; |
Matt Sarett | f489886 | 2016-10-16 10:20:41 -0400 | [diff] [blame] | 196 | test_identity_xform(r, gammas, true); |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 197 | test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas); |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 198 | } |
| 199 | |
| 200 | DEF_TEST(ColorSpaceXform_ExponentialGamma, r) { |
| 201 | // Exponential gamma curves |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 202 | sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas(kChannels)); |
| 203 | for (int i = 0; i < kChannels; ++i) { |
| 204 | gammas->fType[i] = SkGammas::Type::kValue_Type; |
| 205 | gammas->fData[i].fValue = 1.4f; |
| 206 | } |
Matt Sarett | f489886 | 2016-10-16 10:20:41 -0400 | [diff] [blame] | 207 | test_identity_xform(r, gammas, true); |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 208 | test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas); |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 209 | } |
| 210 | |
msarett | 55bcc8e | 2016-09-09 07:48:05 -0700 | [diff] [blame] | 211 | DEF_TEST(ColorSpaceXform_NamedGamma, r) { |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 212 | sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas(kChannels)); |
| 213 | gammas->fType[0] = gammas->fType[1] = gammas->fType[2] = SkGammas::Type::kNamed_Type; |
| 214 | gammas->fData[0].fNamed = kSRGB_SkGammaNamed; |
| 215 | gammas->fData[1].fNamed = k2Dot2Curve_SkGammaNamed; |
| 216 | gammas->fData[2].fNamed = kLinear_SkGammaNamed; |
Matt Sarett | f489886 | 2016-10-16 10:20:41 -0400 | [diff] [blame] | 217 | test_identity_xform(r, gammas, true); |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 218 | test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas); |
| 219 | test_identity_xform_A2B(r, kSRGB_SkGammaNamed, nullptr); |
| 220 | test_identity_xform_A2B(r, k2Dot2Curve_SkGammaNamed, nullptr); |
| 221 | test_identity_xform_A2B(r, kLinear_SkGammaNamed, nullptr); |
msarett | 55bcc8e | 2016-09-09 07:48:05 -0700 | [diff] [blame] | 222 | } |
| 223 | |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 224 | DEF_TEST(ColorSpaceXform_NonMatchingGamma, r) { |
| 225 | constexpr size_t tableSize = 10; |
| 226 | void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize + |
Matt Sarett | df44fc5 | 2016-10-11 16:57:50 -0400 | [diff] [blame] | 227 | sizeof(SkColorSpaceTransferFn)); |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 228 | sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels)); |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 229 | |
| 230 | float* table = SkTAddOffset<float>(memory, sizeof(SkGammas)); |
| 231 | table[0] = 0.00f; |
| 232 | table[1] = 0.15f; |
| 233 | table[2] = 0.20f; |
| 234 | table[3] = 0.25f; |
| 235 | table[4] = 0.35f; |
| 236 | table[5] = 0.45f; |
| 237 | table[6] = 0.55f; |
| 238 | table[7] = 0.70f; |
| 239 | table[8] = 0.85f; |
| 240 | table[9] = 1.00f; |
| 241 | |
Matt Sarett | df44fc5 | 2016-10-11 16:57:50 -0400 | [diff] [blame] | 242 | SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn>(memory, |
| 243 | sizeof(SkGammas) + sizeof(float) * tableSize); |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 244 | params->fA = 1.0f / 1.055f; |
| 245 | params->fB = 0.055f / 1.055f; |
Matt Sarett | 2410717 | 2016-12-19 14:33:35 -0500 | [diff] [blame] | 246 | params->fC = 1.0f / 12.92f; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 247 | params->fD = 0.04045f; |
Matt Sarett | 2410717 | 2016-12-19 14:33:35 -0500 | [diff] [blame] | 248 | params->fE = 0.0f; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 249 | params->fF = 0.0f; |
| 250 | params->fG = 2.4f; |
| 251 | |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 252 | gammas->fType[0] = SkGammas::Type::kValue_Type; |
| 253 | gammas->fData[0].fValue = 1.2f; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 254 | |
Mike Klein | 4437517 | 2017-04-17 19:32:05 -0400 | [diff] [blame] | 255 | // See ColorSpaceXform_TableGamma... we've decided to allow some tolerance |
| 256 | // for SkJumper's implementation of tables. |
| 257 | const int tolerance = 12; |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 258 | gammas->fType[1] = SkGammas::Type::kTable_Type; |
| 259 | gammas->fData[1].fTable.fSize = tableSize; |
| 260 | gammas->fData[1].fTable.fOffset = 0; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 261 | |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 262 | gammas->fType[2] = SkGammas::Type::kParam_Type; |
| 263 | gammas->fData[2].fParamOffset = sizeof(float) * tableSize; |
msarett | 1b93bd1 | 2016-07-21 07:11:26 -0700 | [diff] [blame] | 264 | |
Matt Sarett | f489886 | 2016-10-16 10:20:41 -0400 | [diff] [blame] | 265 | test_identity_xform(r, gammas, true); |
Mike Klein | 4437517 | 2017-04-17 19:32:05 -0400 | [diff] [blame] | 266 | test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, tolerance); |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 267 | } |
| 268 | |
| 269 | DEF_TEST(ColorSpaceXform_A2BCLUT, r) { |
| 270 | constexpr int inputChannels = 3; |
| 271 | constexpr int gp = 4; // # grid points |
| 272 | |
| 273 | constexpr int numEntries = gp*gp*gp*3; |
raftias | 5476128 | 2016-12-01 13:44:07 -0500 | [diff] [blame] | 274 | const uint8_t gridPoints[3] = {gp, gp, gp}; |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 275 | void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) * numEntries); |
| 276 | sk_sp<SkColorLookUpTable> colorLUT(new (memory) SkColorLookUpTable(inputChannels, gridPoints)); |
| 277 | // make a CLUT that rotates R, G, and B ie R->G, G->B, B->R |
| 278 | float* table = SkTAddOffset<float>(memory, sizeof(SkColorLookUpTable)); |
| 279 | for (int r = 0; r < gp; ++r) { |
| 280 | for (int g = 0; g < gp; ++g) { |
| 281 | for (int b = 0; b < gp; ++b) { |
| 282 | table[3*(gp*gp*r + gp*g + b) + 0] = g * (1.f / (gp - 1.f)); |
| 283 | table[3*(gp*gp*r + gp*g + b) + 1] = b * (1.f / (gp - 1.f)); |
| 284 | table[3*(gp*gp*r + gp*g + b) + 2] = r * (1.f / (gp - 1.f)); |
| 285 | } |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | // build an even distribution of pixels every (7 / 255) steps |
| 290 | // to test the xform on |
| 291 | constexpr int pixelgp = 7; |
| 292 | constexpr int numPixels = pixelgp*pixelgp*pixelgp; |
| 293 | SkAutoTMalloc<uint32_t> srcPixels(numPixels); |
| 294 | int srcIndex = 0; |
| 295 | for (int r = 0; r < pixelgp; ++r) { |
| 296 | for (int g = 0; g < pixelgp; ++g) { |
| 297 | for (int b = 0; b < pixelgp; ++b) { |
| 298 | const int red = (int) (r * (255.f / (pixelgp - 1.f))); |
| 299 | const int green = (int) (g * (255.f / (pixelgp - 1.f))); |
| 300 | const int blue = (int) (b * (255.f / (pixelgp - 1.f))); |
| 301 | srcPixels[srcIndex] = SkColorSetRGB(red, green, blue); |
| 302 | ++srcIndex; |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | SkAutoTMalloc<uint32_t> dstPixels(numPixels); |
| 307 | |
| 308 | // src space is identity besides CLUT |
| 309 | std::vector<SkColorSpace_A2B::Element> srcElements; |
| 310 | srcElements.push_back(SkColorSpace_A2B::Element(std::move(colorLUT))); |
| 311 | auto srcSpace = ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::kXYZ, |
Matt Sarett | 523116d | 2017-01-12 18:36:38 -0500 | [diff] [blame] | 312 | SkColorSpace_Base::kRGB_ICCTypeFlag, |
raftias | 2563601 | 2016-11-11 15:27:39 -0800 | [diff] [blame] | 313 | std::move(srcElements)); |
| 314 | // dst space is entirely identity |
| 315 | auto dstSpace = SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkMatrix44::I()); |
| 316 | auto xform = SkColorSpaceXform::New(srcSpace.get(), dstSpace.get()); |
| 317 | bool result = xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, dstPixels.get(), |
| 318 | SkColorSpaceXform::kRGBA_8888_ColorFormat, srcPixels.get(), |
| 319 | numPixels, kOpaque_SkAlphaType); |
| 320 | REPORTER_ASSERT(r, result); |
| 321 | |
| 322 | for (int i = 0; i < numPixels; ++i) { |
| 323 | REPORTER_ASSERT(r, almost_equal(SkColorGetR(srcPixels[i]), |
| 324 | SkColorGetG(dstPixels[i]))); |
| 325 | REPORTER_ASSERT(r, almost_equal(SkColorGetG(srcPixels[i]), |
| 326 | SkColorGetB(dstPixels[i]))); |
| 327 | REPORTER_ASSERT(r, almost_equal(SkColorGetB(srcPixels[i]), |
| 328 | SkColorGetR(dstPixels[i]))); |
| 329 | } |
msarett | dc27a64 | 2016-06-06 12:02:31 -0700 | [diff] [blame] | 330 | } |
raftias | c6cc28c | 2016-09-29 14:31:44 -0400 | [diff] [blame] | 331 | |
Matt Sarett | c55bc9a | 2017-01-13 13:58:57 -0500 | [diff] [blame] | 332 | DEF_TEST(SkColorSpaceXform_LoadTail, r) { |
Matt Sarett | acdb6d3 | 2017-01-13 15:42:45 -0500 | [diff] [blame] | 333 | std::unique_ptr<uint64_t[]> srcPixel(new uint64_t[1]); |
Matt Sarett | c55bc9a | 2017-01-13 13:58:57 -0500 | [diff] [blame] | 334 | srcPixel[0] = 0; |
| 335 | uint32_t dstPixel; |
Matt Sarett | 77a7a1b | 2017-02-07 13:56:11 -0500 | [diff] [blame] | 336 | sk_sp<SkColorSpace> adobe = SkColorSpace_Base::MakeNamed(SkColorSpace_Base::kAdobeRGB_Named); |
| 337 | sk_sp<SkColorSpace> srgb = SkColorSpace::MakeSRGB(); |
Matt Sarett | c55bc9a | 2017-01-13 13:58:57 -0500 | [diff] [blame] | 338 | std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(adobe.get(), srgb.get()); |
| 339 | |
| 340 | // ASAN will catch us if we read past the tail. |
| 341 | bool success = xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, &dstPixel, |
Matt Sarett | acdb6d3 | 2017-01-13 15:42:45 -0500 | [diff] [blame] | 342 | SkColorSpaceXform::kRGBA_U16_BE_ColorFormat, srcPixel.get(), 1, |
Matt Sarett | c55bc9a | 2017-01-13 13:58:57 -0500 | [diff] [blame] | 343 | kUnpremul_SkAlphaType); |
| 344 | REPORTER_ASSERT(r, success); |
| 345 | } |
| 346 | |