Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(461)

Side by Side Diff: gm/labpcsdemo.cpp

Issue 2389983002: Refactored SkColorSpace and added in a Lab PCS GM (Closed)
Patch Set: migrated call from SkColorSpace_Base::makeLinearGamma() to SkColorSpace_XYZ::makeLinearGamma() Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « gm/gamut.cpp ('k') | gyp/core.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 <cmath>
9 #include "gm.h"
10 #include "Resources.h"
11 #include "SkCodec.h"
12 #include "SkColorSpace_Base.h"
13 #include "SkColorSpace_A2B.h"
14 #include "SkColorSpacePriv.h"
15 #include "SkData.h"
16 #include "SkFloatingPoint.h"
17 #include "SkImageInfo.h"
18 #include "SkScalar.h"
19 #include "SkSRGB.h"
20 #include "SkStream.h"
21 #include "SkSurface.h"
22 #include "SkTypes.h"
23
24 static inline void interp_3d_clut(float dst[3], float src[3], const SkColorLookU pTable* colorLUT) {
25 // Call the src components x, y, and z.
26 uint8_t maxX = colorLUT->fGridPoints[0] - 1;
27 uint8_t maxY = colorLUT->fGridPoints[1] - 1;
28 uint8_t maxZ = colorLUT->fGridPoints[2] - 1;
29
30 // An approximate index into each of the three dimensions of the table.
31 float x = src[0] * maxX;
32 float y = src[1] * maxY;
33 float z = src[2] * maxZ;
34
35 // This gives us the low index for our interpolation.
36 int ix = sk_float_floor2int(x);
37 int iy = sk_float_floor2int(y);
38 int iz = sk_float_floor2int(z);
39
40 // Make sure the low index is not also the max index.
41 ix = (maxX == ix) ? ix - 1 : ix;
42 iy = (maxY == iy) ? iy - 1 : iy;
43 iz = (maxZ == iz) ? iz - 1 : iz;
44
45 // Weighting factors for the interpolation.
46 float diffX = x - ix;
47 float diffY = y - iy;
48 float diffZ = z - iz;
49
50 // Constants to help us navigate the 3D table.
51 // Ex: Assume x = a, y = b, z = c.
52 // table[a * n001 + b * n010 + c * n100] logically equals table[a][b][c] .
53 const int n000 = 0;
54 const int n001 = 3 * colorLUT->fGridPoints[1] * colorLUT->fGridPoints[2];
55 const int n010 = 3 * colorLUT->fGridPoints[2];
56 const int n011 = n001 + n010;
57 const int n100 = 3;
58 const int n101 = n100 + n001;
59 const int n110 = n100 + n010;
60 const int n111 = n110 + n001;
61
62 // Base ptr into the table.
63 const float* ptr = &(colorLUT->table()[ix*n001 + iy*n010 + iz*n100]);
64
65 // The code below performs a tetrahedral interpolation for each of the three
66 // dst components. Once the tetrahedron containing the interpolation point is
67 // identified, the interpolation is a weighted sum of grid values at the
68 // vertices of the tetrahedron. The claim is that tetrahedral interpolation
69 // provides a more accurate color conversion.
70 // blogs.mathworks.com/steve/2006/11/24/tetrahedral-interpolation-for-colors pace-conversion/
71 //
72 // I have one test image, and visually I can't tell the difference between
73 // tetrahedral and trilinear interpolation. In terms of computation, the
74 // tetrahedral code requires more branches but less computation. The
75 // SampleICC library provides an option for the client to choose either
76 // tetrahedral or trilinear.
77 for (int i = 0; i < 3; i++) {
78 if (diffZ < diffY) {
79 if (diffZ < diffX) {
80 dst[i] = (ptr[n000] + diffZ * (ptr[n110] - ptr[n010]) +
81 diffY * (ptr[n010] - ptr[n000]) +
82 diffX * (ptr[n111] - ptr[n110]));
83 } else if (diffY < diffX) {
84 dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) +
85 diffY * (ptr[n011] - ptr[n001]) +
86 diffX * (ptr[n001] - ptr[n000]));
87 } else {
88 dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) +
89 diffY * (ptr[n010] - ptr[n000]) +
90 diffX * (ptr[n011] - ptr[n010]));
91 }
92 } else {
93 if (diffZ < diffX) {
94 dst[i] = (ptr[n000] + diffZ * (ptr[n101] - ptr[n001]) +
95 diffY * (ptr[n111] - ptr[n101]) +
96 diffX * (ptr[n001] - ptr[n000]));
97 } else if (diffY < diffX) {
98 dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) +
99 diffY * (ptr[n111] - ptr[n101]) +
100 diffX * (ptr[n101] - ptr[n100]));
101 } else {
102 dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) +
103 diffY * (ptr[n110] - ptr[n100]) +
104 diffX * (ptr[n111] - ptr[n110]));
105 }
106 }
107
108 // Increment the table ptr in order to handle the next component.
109 // Note that this is the how table is designed: all of nXXX
110 // variables are multiples of 3 because there are 3 output
111 // components.
112 ptr++;
113 }
114 }
115
116
117 /**
118 * This tests decoding from a Lab source image and displays on the left
119 * the image as raw RGB values, and on the right a Lab PCS.
120 * It currently does NOT apply a/b/m-curves, as in the .icc profile
121 * We are testing it on these are all identity transforms.
122 */
123 class LabPCSDemoGM : public skiagm::GM {
124 public:
125 LabPCSDemoGM()
126 : fWidth(1080)
127 , fHeight(480)
128 {}
129
130 protected:
131
132
133 SkString onShortName() override {
134 return SkString("labpcsdemo");
135 }
136
137 SkISize onISize() override {
138 return SkISize::Make(fWidth, fHeight);
139 }
140
141 void onDraw(SkCanvas* canvas) override {
142 canvas->drawColor(SK_ColorGREEN);
143 const char* filename = "brickwork-texture.jpg";
144 renderImage(canvas, filename, 0, false);
145 renderImage(canvas, filename, 1, true);
146 }
147
148 void renderImage(SkCanvas* canvas, const char* filename, int col, bool conve rtLabToXYZ) {
149 SkBitmap bitmap;
150 SkStream* stream(GetResourceAsStream(filename));
151 if (stream == nullptr) {
152 return;
153 }
154 std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream));
155
156
157 // srgb_lab_pcs.icc is an elaborate way to specify sRGB but uses
158 // Lab as the PCS, so we can take any arbitrary image that should
159 // be sRGB and this should show a reasonable image
160 const SkString iccFilename(GetResourcePath("icc_profiles/srgb_lab_pcs.ic c"));
161 sk_sp<SkData> iccData = SkData::MakeFromFileName(iccFilename.c_str());
162 if (iccData == nullptr) {
163 return;
164 }
165 sk_sp<SkColorSpace> colorSpace = SkColorSpace::NewICC(iccData->bytes(), iccData->size());
166
167 const int imageWidth = codec->getInfo().width();
168 const int imageHeight = codec->getInfo().height();
169 // Using nullptr as the color space instructs the codec to decode in leg acy mode,
170 // meaning that we will get the raw encoded bytes without any color corr ection.
171 SkImageInfo imageInfo = SkImageInfo::Make(imageWidth, imageHeight, kN32_ SkColorType,
172 kOpaque_SkAlphaType, nullptr);
173 bitmap.allocPixels(imageInfo);
174 codec->getPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes());
175 if (convertLabToXYZ) {
176 SkASSERT(SkColorSpace_Base::Type::kA2B == as_CSB(colorSpace)->type() );
177 SkColorSpace_A2B& cs = *static_cast<SkColorSpace_A2B*>(colorSpace.ge t());
178 bool printConversions = false;
179 SkASSERT(cs.colorLUT());
180 // We're skipping evaluating the TRCs and the matrix here since they aren't
181 // in the ICC profile initially used here.
182 SkASSERT(kLinear_SkGammaNamed == cs.aCurveNamed());
183 SkASSERT(kLinear_SkGammaNamed == cs.mCurveNamed());
184 SkASSERT(kLinear_SkGammaNamed == cs.bCurveNamed());
185 SkASSERT(cs.matrix().isIdentity());
186 for (int y = 0; y < imageHeight; ++y) {
187 for (int x = 0; x < imageWidth; ++x) {
188 uint32_t& p = *bitmap.getAddr32(x, y);
189 const int r = SkColorGetR(p);
190 const int g = SkColorGetG(p);
191 const int b = SkColorGetB(p);
192 if (printConversions) {
193 SkColorSpacePrintf("\nraw = (%d, %d, %d)\t", r, g, b);
194 }
195
196 float lab[4] = { r * (1.f/255.f), g * (1.f/255.f), b * (1.f/ 255.f), 1.f };
197
198 interp_3d_clut(lab, lab, cs.colorLUT());
199
200 // Lab has ranges [0,100] for L and [-128,127] for a and b
201 // but the ICC profile loader stores as [0,1]. The ICC
202 // specifies an offset of -128 to convert.
203 // note: formula could be adjusted to remove this conversion ,
204 // but for now let's keep it like this for clarity unt il
205 // an optimized version is added.
206 lab[0] *= 100.f;
207 lab[1] = 255.f * lab[1] - 128.f;
208 lab[2] = 255.f * lab[2] - 128.f;
209 if (printConversions) {
210 SkColorSpacePrintf("Lab = < %f, %f, %f >\n", lab[0], lab [1], lab[2]);
211 }
212
213 // convert from Lab to XYZ
214 float Y = (lab[0] + 16.f) * (1.f/116.f);
215 float X = lab[1] * (1.f/500.f) + Y;
216 float Z = Y - (lab[2] * (1.f/200.f));
217 float cubed;
218 cubed = X*X*X;
219 if (cubed > 0.008856f)
220 X = cubed;
221 else
222 X = (X - (16.f/116.f)) * (1.f/7.787f);
223 cubed = Y*Y*Y;
224 if (cubed > 0.008856f)
225 Y = cubed;
226 else
227 Y = (Y - (16.f/116.f)) * (1.f/7.787f);
228 cubed = Z*Z*Z;
229 if (cubed > 0.008856f)
230 Z = cubed;
231 else
232 Z = (Z - (16.f/116.f)) * (1.f/7.787f);
233
234 // adjust to D50 illuminant
235 X *= 0.96422f;
236 Y *= 1.00000f;
237 Z *= 0.82521f;
238
239 if (printConversions) {
240 SkColorSpacePrintf("XYZ = (%4f, %4f, %4f)\t", X, Y, Z);
241 }
242
243 // convert XYZ -> linear sRGB
244 Sk4f lRGB( 3.1338561f*X - 1.6168667f*Y - 0.4906146f*Z,
245 -0.9787684f*X + 1.9161415f*Y + 0.0334540f*Z,
246 0.0719453f*X - 0.2289914f*Y + 1.4052427f*Z,
247 1.f);
248 // and apply sRGB gamma
249 Sk4i sRGB = sk_linear_to_srgb(lRGB);
250 if (printConversions) {
251 SkColorSpacePrintf("sRGB = (%d, %d, %d)\n", sRGB[0], sRG B[1], sRGB[2]);
252 }
253 p = SkColorSetRGB(sRGB[0], sRGB[1], sRGB[2]);
254 }
255 }
256 }
257 const int freeWidth = fWidth - 2*imageWidth;
258 const int freeHeight = fHeight - imageHeight;
259 canvas->drawBitmap(bitmap,
260 static_cast<SkScalar>((col+1) * (freeWidth / 3) + col *imageWidth),
261 static_cast<SkScalar>(freeHeight / 2));
262 ++col;
263 }
264
265 private:
266 const int fWidth;
267 const int fHeight;
268
269 typedef skiagm::GM INHERITED;
270 };
271
272 DEF_GM( return new LabPCSDemoGM; )
OLDNEW
« no previous file with comments | « gm/gamut.cpp ('k') | gyp/core.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698