Skip to content

Commit ded7a55

Browse files
Mike KleinSkia Commit-Bot
authored andcommitted
hard-roll skcms into Skia
Adding roll.sh to make it easy. Change-Id: I7887c5f9a41c5b68a5dec89ebc8ac86a1707fef6 Reviewed-on: https://skia-review.googlesource.com/120120 Reviewed-by: Brian Osman <[email protected]> Commit-Queue: Mike Klein <[email protected]>
1 parent 52e16d9 commit ded7a55

20 files changed

+3701
-3
lines changed

DEPS

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ deps = {
2020
"third_party/externals/piex" : "https://android.googlesource.com/platform/external/piex.git@bb217acdca1cc0c16b704669dd6f91a1b509c406",
2121
"third_party/externals/sdl" : "https://skia.googlesource.com/third_party/sdl@5d7cfcca344034aff9327f77fc181ae3754e7a90",
2222
"third_party/externals/sfntly" : "https://chromium.googlesource.com/external/github.com/googlei18n/sfntly.git@b18b09b6114b9b7fe6fc2f96d8b15e8a72f66916",
23-
"third_party/externals/skcms" : "https://skia.googlesource.com/skcms@16a9cebffa3487c593bf7a8a298ce9052106f07d",
2423
"third_party/externals/spirv-headers" : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Headers.git@661ad91124e6af2272afd00f804d8aa276e17107",
2524
"third_party/externals/spirv-tools" : "https://skia.googlesource.com/external/github.com/KhronosGroup/SPIRV-Tools.git@e9e4393b1c5aad7553c05782acefbe32b42644bd",
2625
#"third_party/externals/v8" : "https://chromium.googlesource.com/v8/v8.git@5f1ae66d5634e43563b2d25ea652dfb94c31a3b4",

third_party/skcms/BUILD.gn

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# found in the LICENSE file.
55

66
config("skcms_public") {
7-
include_dirs = [ "../externals/skcms" ]
7+
include_dirs = [ "." ]
88
}
99

1010
source_set("skcms") {
@@ -24,6 +24,6 @@ source_set("skcms") {
2424

2525
defines = []
2626
sources = [
27-
"../externals/skcms/skcms.c",
27+
"skcms.c",
2828
]
2929
}

third_party/skcms/LICENSE

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) 2018 Google Inc. All rights reserved.
2+
//
3+
// Redistribution and use in source and binary forms, with or without
4+
// modification, are permitted provided that the following conditions are
5+
// met:
6+
//
7+
// * Redistributions of source code must retain the above copyright
8+
// notice, this list of conditions and the following disclaimer.
9+
// * Redistributions in binary form must reproduce the above
10+
// copyright notice, this list of conditions and the following disclaimer
11+
// in the documentation and/or other materials provided with the
12+
// distribution.
13+
// * Neither the name of Google Inc. nor the names of its
14+
// contributors may be used to endorse or promote products derived from
15+
// this software without specific prior written permission.
16+
//
17+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
29+
--------------------------------------------------------------------------------

third_party/skcms/roll.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
# Copyright 2018 Google Inc.
4+
#
5+
# Use of this source code is governed by a BSD-style license that can be
6+
# found in the LICENSE file.
7+
8+
set -e
9+
#set -x
10+
11+
tmp=$(mktemp -d)
12+
git clone --quiet --depth 1 https://skia.googlesource.com/skcms.git $tmp
13+
14+
git rm --quiet LICENSE
15+
git rm --quiet skcms.h
16+
git rm --quiet skcms.c
17+
git rm --quiet -r src
18+
19+
cp $tmp/LICENSE .
20+
cp $tmp/skcms.h .
21+
cp $tmp/skcms.c .
22+
cp -r $tmp/src .
23+
24+
git add .
25+
26+
rm -rf $tmp

third_party/skcms/skcms.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2018 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+
// skcms.c is a unity build target for skcms, #including every other C source file.
9+
10+
#include "src/GaussNewton.c"
11+
#include "src/ICCProfile.c"
12+
#include "src/LinearAlgebra.c"
13+
#include "src/PortableMath.c"
14+
#include "src/TF13.c"
15+
#include "src/TransferFunction.c"
16+
#include "src/Transform.c"

third_party/skcms/skcms.h

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
* Copyright 2018 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+
#pragma once
9+
10+
// skcms.h contains the entire public API for skcms.
11+
12+
#include <stdbool.h>
13+
#include <stddef.h>
14+
#include <stdint.h>
15+
16+
#ifdef __cplusplus
17+
extern "C" {
18+
#endif
19+
20+
// A row-major 3x3 matrix (ie vals[row][col])
21+
typedef struct {
22+
float vals[3][3];
23+
} skcms_Matrix3x3;
24+
25+
// A row-major 3x4 matrix (ie vals[row][col])
26+
typedef struct {
27+
float vals[3][4];
28+
} skcms_Matrix3x4;
29+
30+
// A transfer function mapping encoded values to linear values,
31+
// represented by this 7-parameter piecewise function:
32+
//
33+
// linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d
34+
// = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
35+
//
36+
// (A simple gamma transfer function sets g to gamma and a to 1.)
37+
typedef struct {
38+
float g, a,b,c,d,e,f;
39+
} skcms_TransferFunction;
40+
41+
// Unified representation of 'curv' or 'para' tag data, or a 1D table from 'mft1' or 'mft2'
42+
typedef struct {
43+
union {
44+
struct {
45+
uint32_t alias_of_table_entries;
46+
skcms_TransferFunction parametric;
47+
};
48+
struct {
49+
uint32_t table_entries;
50+
const uint8_t* table_8;
51+
const uint8_t* table_16;
52+
};
53+
};
54+
} skcms_Curve;
55+
56+
typedef struct {
57+
// Optional: N 1D curves, followed by an N-dimensional CLUT.
58+
// If input_channels == 0, these curves and CLUT are skipped,
59+
// Otherwise, input_channels must be in [1, 4].
60+
uint32_t input_channels;
61+
skcms_Curve input_curves[4];
62+
uint8_t grid_points[4];
63+
const uint8_t* grid_8;
64+
const uint8_t* grid_16;
65+
66+
// Optional: 3 1D curves, followed by a color matrix.
67+
// If matrix_channels == 0, these curves and matrix are skipped,
68+
// Otherwise, matrix_channels must be 3.
69+
uint32_t matrix_channels;
70+
skcms_Curve matrix_curves[3];
71+
skcms_Matrix3x4 matrix;
72+
73+
// Required: 3 1D curves. Always present, and output_channels must be 3.
74+
uint32_t output_channels;
75+
skcms_Curve output_curves[3];
76+
} skcms_A2B;
77+
78+
typedef struct {
79+
const uint8_t* buffer;
80+
81+
uint32_t size;
82+
uint32_t data_color_space;
83+
uint32_t pcs;
84+
uint32_t tag_count;
85+
86+
// skcms_Parse() will set commonly-used fields for you when possible:
87+
88+
// If we can parse red, green and blue transfer curves from the profile,
89+
// trc will be set to those three curves, and has_trc will be true.
90+
bool has_trc;
91+
skcms_Curve trc[3];
92+
93+
// If this profile's gamut can be represented by a 3x3 transform to XYZD50,
94+
// skcms_Parse() sets toXYZD50 to that transform and has_toXYZD50 to true.
95+
bool has_toXYZD50;
96+
skcms_Matrix3x3 toXYZD50;
97+
98+
// If the profile has a valid A2B0 tag, skcms_Parse() sets A2B to that data,
99+
// and has_A2B to true.
100+
bool has_A2B;
101+
skcms_A2B A2B;
102+
} skcms_ICCProfile;
103+
104+
// Parse an ICC profile and return true if possible, otherwise return false.
105+
// The buffer is not copied, it must remain valid as long as the skcms_ICCProfile
106+
// will be used.
107+
bool skcms_Parse(const void*, size_t, skcms_ICCProfile*);
108+
109+
bool skcms_ApproximateCurve(const skcms_Curve* curve, skcms_TransferFunction* approx,
110+
float* max_error);
111+
112+
// A specialized approximation for transfer functions with gamma between 1 and 3.
113+
// f(x) = Ax^3 + Bx^2 + (1-A-B)x
114+
typedef struct {
115+
float A,B;
116+
} skcms_TF13;
117+
118+
bool skcms_ApproximateCurve13(const skcms_Curve* curve, skcms_TF13* approx, float* max_error);
119+
120+
typedef struct {
121+
uint32_t signature;
122+
uint32_t type;
123+
uint32_t size;
124+
const uint8_t* buf;
125+
} skcms_ICCTag;
126+
127+
void skcms_GetTagByIndex (const skcms_ICCProfile*, uint32_t idx, skcms_ICCTag*);
128+
bool skcms_GetTagBySignature(const skcms_ICCProfile*, uint32_t sig, skcms_ICCTag*);
129+
130+
typedef enum {
131+
skcms_PixelFormat_RGB_565,
132+
skcms_PixelFormat_BGR_565,
133+
134+
skcms_PixelFormat_RGB_888,
135+
skcms_PixelFormat_BGR_888,
136+
skcms_PixelFormat_RGBA_8888,
137+
skcms_PixelFormat_BGRA_8888,
138+
139+
skcms_PixelFormat_RGBA_1010102,
140+
skcms_PixelFormat_BGRA_1010102,
141+
142+
skcms_PixelFormat_RGB_161616, // Big-endian. Pointers must be 16-bit aligned.
143+
skcms_PixelFormat_BGR_161616,
144+
skcms_PixelFormat_RGBA_16161616,
145+
skcms_PixelFormat_BGRA_16161616,
146+
147+
skcms_PixelFormat_RGB_hhh, // 1-5-10 half-precision float.
148+
skcms_PixelFormat_BGR_hhh, // Pointers must be 16-bit aligned.
149+
skcms_PixelFormat_RGBA_hhhh,
150+
skcms_PixelFormat_BGRA_hhhh,
151+
152+
skcms_PixelFormat_RGB_fff, // 1-8-23 single-precision float (the normal kind).
153+
skcms_PixelFormat_BGR_fff, // Pointers must be 32-bit aligned.
154+
skcms_PixelFormat_RGBA_ffff,
155+
skcms_PixelFormat_BGRA_ffff,
156+
} skcms_PixelFormat;
157+
158+
// We always store any alpha channel linearly. In the chart below, tf-1() is the inverse
159+
// transfer function for the given color profile (applying the transfer function linearizes).
160+
161+
// We treat opaque as a strong requirement, not just a performance hint: we will ignore
162+
// any source alpha and treat it as 1.0, and will make sure that any destination alpha
163+
// channel is filled with the equivalent of 1.0.
164+
165+
// When premultiplying and/or using a non-linear transfer function, it's important
166+
// that we know the order the operations are applied. If you're used to working
167+
// with non-color-managed drawing systems, PremulAsEncoded is probably the "premul"
168+
// you're looking for; if you want linear blending, PremulLinear is the choice for you.
169+
170+
typedef enum {
171+
skcms_AlphaFormat_Opaque, // alpha is always opaque
172+
// tf-1(r), tf-1(g), tf-1(b), 1.0
173+
skcms_AlphaFormat_Unpremul, // alpha and color are unassociated
174+
// tf-1(r), tf-1(g), tf-1(b), a
175+
skcms_AlphaFormat_PremulAsEncoded, // premultiplied while encoded
176+
// tf-1(r)*a, tf-1(g)*a, tf-1(b)*a, a
177+
skcms_AlphaFormat_PremulLinear, // premultiplied while linear
178+
// tf-1(r*a), tf-1(g*a), tf-1(b*a), a
179+
} skcms_AlphaFormat;
180+
181+
// Convert npixels pixels from src format and color profile to dst format and color profile
182+
// and return true, otherwise return false. It is safe to alias dst == src if dstFmt == srcFmt.
183+
bool skcms_Transform(const void* src,
184+
skcms_PixelFormat srcFmt,
185+
skcms_AlphaFormat srcAlpha,
186+
const skcms_ICCProfile* srcProfile,
187+
void* dst,
188+
skcms_PixelFormat dstFmt,
189+
skcms_AlphaFormat dstAlpha,
190+
const skcms_ICCProfile* dstProfile,
191+
size_t npixels);
192+
193+
#ifdef __cplusplus
194+
}
195+
#endif
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2018 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 "../skcms.h"
9+
#include "GaussNewton.h"
10+
#include "LinearAlgebra.h"
11+
#include <assert.h>
12+
13+
bool skcms_gauss_newton_step(float (* t)(float x, const void*), const void* t_ctx,
14+
float (* f)(float x, const float P[4]),
15+
void (*grad_f)(float x, const float P[4], float dfdP[4]),
16+
float P[4],
17+
float x0, float x1, int N) {
18+
// We'll sample x from the range [x0,x1] (both inclusive) N times with even spacing.
19+
//
20+
// We want to do P' = P + (Jf^T Jf)^-1 Jf^T r(P),
21+
// where r(P) is the residual vector t(x) - f(x,P)
22+
// and Jf is the Jacobian matrix of f(), ∂r/∂P.
23+
//
24+
// Let's review the shape of each of these expressions:
25+
// r(P) is [N x 1], a column vector with one entry per value of x tested
26+
// Jf is [N x 4], a matrix with an entry for each (x,P) pair
27+
// Jf^T is [4 x N], the transpose of Jf
28+
//
29+
// Jf^T Jf is [4 x N] * [N x 4] == [4 x 4], a 4x4 matrix,
30+
// and so is its inverse (Jf^T Jf)^-1
31+
// Jf^T r(P) is [4 x N] * [N x 1] == [4 x 1], a column vector with the same shape as P
32+
//
33+
// Our implementation strategy to get to the final ∆P is
34+
// 1) evaluate Jf^T Jf, call that lhs
35+
// 2) evaluate Jf^T r(P), call that rhs
36+
// 3) invert lhs
37+
// 4) multiply inverse lhs by rhs
38+
//
39+
// This is a friendly implementation strategy because we don't have to have any
40+
// buffers that scale with N, and equally nice don't have to perform any matrix
41+
// operations that are variable size.
42+
//
43+
// Other implementation strategies could trade this off, e.g. evaluating the
44+
// pseudoinverse of Jf ( (Jf^T Jf)^-1 Jf^T ) directly, then multiplying that by
45+
// the residuals. That would probably require implementing singular value
46+
// decomposition, and would create a [4 x N] matrix to be multiplied by the
47+
// [N x 1] residual vector, but on the upside I think that'd eliminate the
48+
// possibility of this skcms_gauss_newton_step() function ever failing.
49+
50+
// 0) start off with lhs and rhs safely zeroed.
51+
skcms_Matrix4x4 lhs = {{ {0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0} }};
52+
skcms_Vector4 rhs = { {0,0,0,0} };
53+
54+
// 1,2) evaluate lhs and evaluate rhs
55+
// We want to evaluate Jf only once, but both lhs and rhs involve Jf^T,
56+
// so we'll have to update lhs and rhs at the same time.
57+
float dx = (x1-x0)/(N-1);
58+
for (int i = 0; i < N; i++) {
59+
float x = x0 + i*dx;
60+
61+
float resid = t(x,t_ctx) - f(x,P);
62+
63+
float dfdP[4] = {0,0,0,0};
64+
grad_f(x,P, dfdP);
65+
66+
for (int r = 0; r < 4; r++) {
67+
for (int c = 0; c < 4; c++) {
68+
lhs.vals[r][c] += dfdP[r] * dfdP[c];
69+
}
70+
rhs.vals[r] += dfdP[r] * resid;
71+
}
72+
}
73+
74+
// If any of the 4 P parameters are unused, this matrix will be singular.
75+
// Detect those cases and fix them up to indentity instead, so we can invert.
76+
for (int k = 0; k < 4; k++) {
77+
if (lhs.vals[0][k]==0 && lhs.vals[1][k]==0 && lhs.vals[2][k]==0 && lhs.vals[3][k]==0 &&
78+
lhs.vals[k][0]==0 && lhs.vals[k][1]==0 && lhs.vals[k][2]==0 && lhs.vals[k][3]==0) {
79+
lhs.vals[k][k] = 1;
80+
}
81+
}
82+
83+
// 3) invert lhs
84+
skcms_Matrix4x4 lhs_inv;
85+
if (!skcms_Matrix4x4_invert(&lhs, &lhs_inv)) {
86+
return false;
87+
}
88+
89+
// 4) multiply inverse lhs by rhs
90+
skcms_Vector4 dP = skcms_Matrix4x4_Vector4_mul(&lhs_inv, &rhs);
91+
P[0] += dP.vals[0];
92+
P[1] += dP.vals[1];
93+
P[2] += dP.vals[2];
94+
P[3] += dP.vals[3];
95+
return true;
96+
}

0 commit comments

Comments
 (0)