Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Macros | Typedefs | Enumerations | Functions
skcms_public.h File Reference
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

Go to the source code of this file.

Classes

struct  skcms_Matrix3x3
 
struct  skcms_Matrix3x4
 
struct  skcms_TransferFunction
 
union  skcms_Curve
 
struct  skcms_A2B
 
struct  skcms_B2A
 
struct  skcms_CICP
 
struct  skcms_ICCProfile
 

Macros

#define SKCMS_API
 

Typedefs

typedef struct skcms_Matrix3x3 skcms_Matrix3x3
 
typedef struct skcms_Matrix3x4 skcms_Matrix3x4
 
typedef struct skcms_TransferFunction skcms_TransferFunction
 
typedef enum skcms_TFType skcms_TFType
 
typedef union skcms_Curve skcms_Curve
 
typedef struct skcms_A2B skcms_A2B
 
typedef struct skcms_B2A skcms_B2A
 
typedef struct skcms_CICP skcms_CICP
 
typedef struct skcms_ICCProfile skcms_ICCProfile
 
typedef enum skcms_PixelFormat skcms_PixelFormat
 
typedef enum skcms_AlphaFormat skcms_AlphaFormat
 

Enumerations

enum  skcms_TFType {
  skcms_TFType_Invalid , skcms_TFType_sRGBish , skcms_TFType_PQish , skcms_TFType_HLGish ,
  skcms_TFType_HLGinvish
}
 
enum  {
  skcms_Signature_CMYK = 0x434D594B , skcms_Signature_Gray = 0x47524159 , skcms_Signature_RGB = 0x52474220 , skcms_Signature_Lab = 0x4C616220 ,
  skcms_Signature_XYZ = 0x58595A20
}
 
enum  skcms_PixelFormat {
  skcms_PixelFormat_A_8 , skcms_PixelFormat_A_8_ , skcms_PixelFormat_G_8 , skcms_PixelFormat_G_8_ ,
  skcms_PixelFormat_RGB_565 , skcms_PixelFormat_BGR_565 , skcms_PixelFormat_ABGR_4444 , skcms_PixelFormat_ARGB_4444 ,
  skcms_PixelFormat_RGB_888 , skcms_PixelFormat_BGR_888 , skcms_PixelFormat_RGBA_8888 , skcms_PixelFormat_BGRA_8888 ,
  skcms_PixelFormat_RGBA_8888_sRGB , skcms_PixelFormat_BGRA_8888_sRGB , skcms_PixelFormat_RGBA_1010102 , skcms_PixelFormat_BGRA_1010102 ,
  skcms_PixelFormat_RGB_161616LE , skcms_PixelFormat_BGR_161616LE , skcms_PixelFormat_RGBA_16161616LE , skcms_PixelFormat_BGRA_16161616LE ,
  skcms_PixelFormat_RGB_161616BE , skcms_PixelFormat_BGR_161616BE , skcms_PixelFormat_RGBA_16161616BE , skcms_PixelFormat_BGRA_16161616BE ,
  skcms_PixelFormat_RGB_hhh_Norm , skcms_PixelFormat_BGR_hhh_Norm , skcms_PixelFormat_RGBA_hhhh_Norm , skcms_PixelFormat_BGRA_hhhh_Norm ,
  skcms_PixelFormat_RGB_hhh , skcms_PixelFormat_BGR_hhh , skcms_PixelFormat_RGBA_hhhh , skcms_PixelFormat_BGRA_hhhh ,
  skcms_PixelFormat_RGB_fff , skcms_PixelFormat_BGR_fff , skcms_PixelFormat_RGBA_ffff , skcms_PixelFormat_BGRA_ffff ,
  skcms_PixelFormat_RGB_101010x_XR , skcms_PixelFormat_BGR_101010x_XR
}
 
enum  skcms_AlphaFormat { skcms_AlphaFormat_Opaque , skcms_AlphaFormat_Unpremul , skcms_AlphaFormat_PremulAsEncoded }
 

Functions

SKCMS_API bool skcms_Matrix3x3_invert (const skcms_Matrix3x3 *, skcms_Matrix3x3 *)
 
SKCMS_API skcms_Matrix3x3 skcms_Matrix3x3_concat (const skcms_Matrix3x3 *, const skcms_Matrix3x3 *)
 
SKCMS_API float skcms_TransferFunction_eval (const skcms_TransferFunction *, float)
 
SKCMS_API bool skcms_TransferFunction_invert (const skcms_TransferFunction *, skcms_TransferFunction *)
 
SKCMS_API skcms_TFType skcms_TransferFunction_getType (const skcms_TransferFunction *)
 
SKCMS_API bool skcms_TransferFunction_makePQish (skcms_TransferFunction *, float A, float B, float C, float D, float E, float F)
 
SKCMS_API bool skcms_TransferFunction_makeScaledHLGish (skcms_TransferFunction *, float K, float R, float G, float a, float b, float c)
 
static bool skcms_TransferFunction_makeHLGish (skcms_TransferFunction *fn, float R, float G, float a, float b, float c)
 
static bool skcms_TransferFunction_makePQ (skcms_TransferFunction *tf)
 
static bool skcms_TransferFunction_makeHLG (skcms_TransferFunction *tf)
 
SKCMS_API bool skcms_TransferFunction_isSRGBish (const skcms_TransferFunction *)
 
SKCMS_API bool skcms_TransferFunction_isPQish (const skcms_TransferFunction *)
 
SKCMS_API bool skcms_TransferFunction_isHLGish (const skcms_TransferFunction *)
 
SKCMS_API const skcms_ICCProfileskcms_sRGB_profile (void)
 
SKCMS_API const skcms_ICCProfileskcms_XYZD50_profile (void)
 
SKCMS_API const skcms_TransferFunctionskcms_sRGB_TransferFunction (void)
 
SKCMS_API const skcms_TransferFunctionskcms_sRGB_Inverse_TransferFunction (void)
 
SKCMS_API const skcms_TransferFunctionskcms_Identity_TransferFunction (void)
 
SKCMS_API bool skcms_ApproximatelyEqualProfiles (const skcms_ICCProfile *A, const skcms_ICCProfile *B)
 
SKCMS_API bool skcms_AreApproximateInverses (const skcms_Curve *curve, const skcms_TransferFunction *inv_tf)
 
SKCMS_API bool skcms_TRCs_AreApproximateInverse (const skcms_ICCProfile *profile, const skcms_TransferFunction *inv_tf)
 
SKCMS_API bool skcms_ParseWithA2BPriority (const void *, size_t, const int priority[], int priorities, skcms_ICCProfile *)
 
static bool skcms_Parse (const void *buf, size_t len, skcms_ICCProfile *profile)
 
SKCMS_API bool skcms_ApproximateCurve (const skcms_Curve *curve, skcms_TransferFunction *approx, float *max_error)
 
SKCMS_API bool skcms_GetCHAD (const skcms_ICCProfile *, skcms_Matrix3x3 *)
 
SKCMS_API bool skcms_GetWTPT (const skcms_ICCProfile *, float xyz[3])
 
SKCMS_API bool skcms_Transform (const void *src, skcms_PixelFormat srcFmt, skcms_AlphaFormat srcAlpha, const skcms_ICCProfile *srcProfile, void *dst, skcms_PixelFormat dstFmt, skcms_AlphaFormat dstAlpha, const skcms_ICCProfile *dstProfile, size_t npixels)
 
SKCMS_API bool skcms_MakeUsableAsDestination (skcms_ICCProfile *profile)
 
SKCMS_API bool skcms_MakeUsableAsDestinationWithSingleCurve (skcms_ICCProfile *profile)
 
SKCMS_API bool skcms_AdaptToXYZD50 (float wx, float wy, skcms_Matrix3x3 *toXYZD50)
 
SKCMS_API bool skcms_PrimariesToXYZD50 (float rx, float ry, float gx, float gy, float bx, float by, float wx, float wy, skcms_Matrix3x3 *toXYZD50)
 
SKCMS_API void skcms_DisableRuntimeCPUDetection (void)
 
static void skcms_Init (skcms_ICCProfile *p)
 
static void skcms_SetTransferFunction (skcms_ICCProfile *p, const skcms_TransferFunction *tf)
 
static void skcms_SetXYZD50 (skcms_ICCProfile *p, const skcms_Matrix3x3 *m)
 

Macro Definition Documentation

◆ SKCMS_API

#define SKCMS_API

Definition at line 13 of file skcms_public.h.

Typedef Documentation

◆ skcms_A2B

typedef struct skcms_A2B skcms_A2B

◆ skcms_AlphaFormat

◆ skcms_B2A

typedef struct skcms_B2A skcms_B2A

◆ skcms_CICP

typedef struct skcms_CICP skcms_CICP

◆ skcms_Curve

typedef union skcms_Curve skcms_Curve

◆ skcms_ICCProfile

◆ skcms_Matrix3x3

◆ skcms_Matrix3x4

◆ skcms_PixelFormat

◆ skcms_TFType

typedef enum skcms_TFType skcms_TFType

◆ skcms_TransferFunction

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
skcms_Signature_CMYK 
skcms_Signature_Gray 
skcms_Signature_RGB 
skcms_Signature_Lab 
skcms_Signature_XYZ 

Definition at line 262 of file skcms_public.h.

262 {
263 // data_color_space
264 skcms_Signature_CMYK = 0x434D594B,
265 skcms_Signature_Gray = 0x47524159,
266 skcms_Signature_RGB = 0x52474220,
267
268 // pcs
269 skcms_Signature_Lab = 0x4C616220,
270 skcms_Signature_XYZ = 0x58595A20,
271};
@ skcms_Signature_Lab
@ skcms_Signature_RGB
@ skcms_Signature_Gray
@ skcms_Signature_XYZ
@ skcms_Signature_CMYK

◆ skcms_AlphaFormat

Enumerator
skcms_AlphaFormat_Opaque 
skcms_AlphaFormat_Unpremul 
skcms_AlphaFormat_PremulAsEncoded 

Definition at line 334 of file skcms_public.h.

334 {
335 skcms_AlphaFormat_Opaque, // alpha is always opaque
336 // tf-1(r), tf-1(g), tf-1(b), 1.0
337 skcms_AlphaFormat_Unpremul, // alpha and color are unassociated
338 // tf-1(r), tf-1(g), tf-1(b), a
339 skcms_AlphaFormat_PremulAsEncoded, // premultiplied while encoded
340 // tf-1(r)*a, tf-1(g)*a, tf-1(b)*a, a
skcms_AlphaFormat
@ skcms_AlphaFormat_Unpremul
@ skcms_AlphaFormat_PremulAsEncoded
@ skcms_AlphaFormat_Opaque

◆ skcms_PixelFormat

Enumerator
skcms_PixelFormat_A_8 
skcms_PixelFormat_A_8_ 
skcms_PixelFormat_G_8 
skcms_PixelFormat_G_8_ 
skcms_PixelFormat_RGB_565 
skcms_PixelFormat_BGR_565 
skcms_PixelFormat_ABGR_4444 
skcms_PixelFormat_ARGB_4444 
skcms_PixelFormat_RGB_888 
skcms_PixelFormat_BGR_888 
skcms_PixelFormat_RGBA_8888 
skcms_PixelFormat_BGRA_8888 
skcms_PixelFormat_RGBA_8888_sRGB 
skcms_PixelFormat_BGRA_8888_sRGB 
skcms_PixelFormat_RGBA_1010102 
skcms_PixelFormat_BGRA_1010102 
skcms_PixelFormat_RGB_161616LE 
skcms_PixelFormat_BGR_161616LE 
skcms_PixelFormat_RGBA_16161616LE 
skcms_PixelFormat_BGRA_16161616LE 
skcms_PixelFormat_RGB_161616BE 
skcms_PixelFormat_BGR_161616BE 
skcms_PixelFormat_RGBA_16161616BE 
skcms_PixelFormat_BGRA_16161616BE 
skcms_PixelFormat_RGB_hhh_Norm 
skcms_PixelFormat_BGR_hhh_Norm 
skcms_PixelFormat_RGBA_hhhh_Norm 
skcms_PixelFormat_BGRA_hhhh_Norm 
skcms_PixelFormat_RGB_hhh 
skcms_PixelFormat_BGR_hhh 
skcms_PixelFormat_RGBA_hhhh 
skcms_PixelFormat_BGRA_hhhh 
skcms_PixelFormat_RGB_fff 
skcms_PixelFormat_BGR_fff 
skcms_PixelFormat_RGBA_ffff 
skcms_PixelFormat_BGRA_ffff 
skcms_PixelFormat_RGB_101010x_XR 
skcms_PixelFormat_BGR_101010x_XR 

Definition at line 273 of file skcms_public.h.

273 {
278
281
284
289 skcms_PixelFormat_RGBA_8888_sRGB, // Automatic sRGB encoding / decoding.
290 skcms_PixelFormat_BGRA_8888_sRGB, // (Generally used with linear transfer functions.)
291
294
295 skcms_PixelFormat_RGB_161616LE, // Little-endian. Pointers must be 16-bit aligned.
299
300 skcms_PixelFormat_RGB_161616BE, // Big-endian. Pointers must be 16-bit aligned.
304
305 skcms_PixelFormat_RGB_hhh_Norm, // 1-5-10 half-precision float in [0,1]
306 skcms_PixelFormat_BGR_hhh_Norm, // Pointers must be 16-bit aligned.
309
310 skcms_PixelFormat_RGB_hhh, // 1-5-10 half-precision float.
311 skcms_PixelFormat_BGR_hhh, // Pointers must be 16-bit aligned.
314
315 skcms_PixelFormat_RGB_fff, // 1-8-23 single-precision float (the normal kind).
316 skcms_PixelFormat_BGR_fff, // Pointers must be 32-bit aligned.
319
320 skcms_PixelFormat_RGB_101010x_XR, // Note: This is located here to signal no clamping.
321 skcms_PixelFormat_BGR_101010x_XR, // Compatible with MTLPixelFormatBGR10_XR.
skcms_PixelFormat
@ skcms_PixelFormat_BGRA_ffff
@ skcms_PixelFormat_RGBA_8888_sRGB
@ skcms_PixelFormat_BGRA_hhhh
@ skcms_PixelFormat_RGBA_16161616BE
@ skcms_PixelFormat_A_8_
@ skcms_PixelFormat_RGB_161616LE
@ skcms_PixelFormat_BGR_hhh_Norm
@ skcms_PixelFormat_BGR_101010x_XR
@ skcms_PixelFormat_RGBA_ffff
@ skcms_PixelFormat_BGR_fff
@ skcms_PixelFormat_RGBA_1010102
@ skcms_PixelFormat_BGR_888
@ skcms_PixelFormat_BGRA_8888
@ skcms_PixelFormat_RGBA_hhhh
@ skcms_PixelFormat_RGB_fff
@ skcms_PixelFormat_RGB_hhh_Norm
@ skcms_PixelFormat_BGR_161616BE
@ skcms_PixelFormat_RGBA_8888
@ skcms_PixelFormat_BGRA_hhhh_Norm
@ skcms_PixelFormat_RGB_hhh
@ skcms_PixelFormat_BGRA_16161616BE
@ skcms_PixelFormat_RGB_888
@ skcms_PixelFormat_G_8
@ skcms_PixelFormat_BGR_hhh
@ skcms_PixelFormat_G_8_
@ skcms_PixelFormat_BGR_565
@ skcms_PixelFormat_ABGR_4444
@ skcms_PixelFormat_BGRA_8888_sRGB
@ skcms_PixelFormat_A_8
@ skcms_PixelFormat_ARGB_4444
@ skcms_PixelFormat_BGR_161616LE
@ skcms_PixelFormat_RGB_565
@ skcms_PixelFormat_RGB_161616BE
@ skcms_PixelFormat_BGRA_1010102
@ skcms_PixelFormat_RGBA_hhhh_Norm
@ skcms_PixelFormat_BGRA_16161616LE
@ skcms_PixelFormat_RGB_101010x_XR
@ skcms_PixelFormat_RGBA_16161616LE

◆ skcms_TFType

Enumerator
skcms_TFType_Invalid 
skcms_TFType_sRGBish 
skcms_TFType_PQish 
skcms_TFType_HLGish 
skcms_TFType_HLGinvish 

Definition at line 54 of file skcms_public.h.

54 {
skcms_TFType
@ skcms_TFType_Invalid
@ skcms_TFType_HLGish
@ skcms_TFType_sRGBish
@ skcms_TFType_HLGinvish
@ skcms_TFType_PQish

Function Documentation

◆ skcms_AdaptToXYZD50()

SKCMS_API bool skcms_AdaptToXYZD50 ( float  wx,
float  wy,
skcms_Matrix3x3 toXYZD50 
)

Definition at line 1706 of file skcms.cc.

1707 {
1708 if (!is_zero_to_one(wx) || !is_zero_to_one(wy) ||
1709 !toXYZD50) {
1710 return false;
1711 }
1712
1713 // Assumes that Y is 1.0f.
1714 skcms_Vector3 wXYZ = { { wx / wy, 1, (1 - wx - wy) / wy } };
1715
1716 // Now convert toXYZ matrix to toXYZD50.
1717 skcms_Vector3 wXYZD50 = { { 0.96422f, 1.0f, 0.82521f } };
1718
1719 // Calculate the chromatic adaptation matrix. We will use the Bradford method, thus
1720 // the matrices below. The Bradford method is used by Adobe and is widely considered
1721 // to be the best.
1722 skcms_Matrix3x3 xyz_to_lms = {{
1723 { 0.8951f, 0.2664f, -0.1614f },
1724 { -0.7502f, 1.7135f, 0.0367f },
1725 { 0.0389f, -0.0685f, 1.0296f },
1726 }};
1727 skcms_Matrix3x3 lms_to_xyz = {{
1728 { 0.9869929f, -0.1470543f, 0.1599627f },
1729 { 0.4323053f, 0.5183603f, 0.0492912f },
1730 { -0.0085287f, 0.0400428f, 0.9684867f },
1731 }};
1732
1733 skcms_Vector3 srcCone = mv_mul(&xyz_to_lms, &wXYZ);
1734 skcms_Vector3 dstCone = mv_mul(&xyz_to_lms, &wXYZD50);
1735
1736 *toXYZD50 = {{
1737 { dstCone.vals[0] / srcCone.vals[0], 0, 0 },
1738 { 0, dstCone.vals[1] / srcCone.vals[1], 0 },
1739 { 0, 0, dstCone.vals[2] / srcCone.vals[2] },
1740 }};
1741 *toXYZD50 = skcms_Matrix3x3_concat(toXYZD50, &xyz_to_lms);
1742 *toXYZD50 = skcms_Matrix3x3_concat(&lms_to_xyz, toXYZD50);
1743
1744 return true;
1745}
static skcms_Vector3 mv_mul(const skcms_Matrix3x3 *m, const skcms_Vector3 *v)
Definition skcms.cc:1696
static bool is_zero_to_one(float x)
Definition skcms.cc:1690
skcms_Matrix3x3 skcms_Matrix3x3_concat(const skcms_Matrix3x3 *A, const skcms_Matrix3x3 *B)
Definition skcms.cc:1849
float vals[3]
Definition skcms.cc:1694

◆ skcms_ApproximateCurve()

SKCMS_API bool skcms_ApproximateCurve ( const skcms_Curve curve,
skcms_TransferFunction approx,
float *  max_error 
)

Definition at line 2199 of file skcms.cc.

2201 {
2202 if (!curve || !approx || !max_error) {
2203 return false;
2204 }
2205
2206 if (curve->table_entries == 0) {
2207 // No point approximating an skcms_TransferFunction with an skcms_TransferFunction!
2208 return false;
2209 }
2210
2211 if (curve->table_entries == 1 || curve->table_entries > (uint32_t)INT_MAX) {
2212 // We need at least two points, and must put some reasonable cap on the maximum number.
2213 return false;
2214 }
2215
2216 int N = (int)curve->table_entries;
2217 const float dx = 1.0f / static_cast<float>(N - 1);
2218
2219 *max_error = INFINITY_;
2220 const float kTolerances[] = { 1.5f / 65535.0f, 1.0f / 512.0f };
2221 for (int t = 0; t < ARRAY_COUNT(kTolerances); t++) {
2223 tf_inv;
2224
2225 // It's problematic to fit curves with non-zero f, so always force it to zero explicitly.
2226 tf.f = 0.0f;
2227 int L = fit_linear(curve, N, kTolerances[t], &tf.c, &tf.d);
2228
2229 if (L == N) {
2230 // If the entire data set was linear, move the coefficients to the nonlinear portion
2231 // with G == 1. This lets use a canonical representation with d == 0.
2232 tf.g = 1;
2233 tf.a = tf.c;
2234 tf.b = tf.f;
2235 tf.c = tf.d = tf.e = tf.f = 0;
2236 } else if (L == N - 1) {
2237 // Degenerate case with only two points in the nonlinear segment. Solve directly.
2238 tf.g = 1;
2239 tf.a = (eval_curve(curve, static_cast<float>(N-1)*dx) -
2240 eval_curve(curve, static_cast<float>(N-2)*dx))
2241 / dx;
2242 tf.b = eval_curve(curve, static_cast<float>(N-2)*dx)
2243 - tf.a * static_cast<float>(N-2)*dx;
2244 tf.e = 0;
2245 } else {
2246 // Start by guessing a gamma-only curve through the midpoint.
2247 int mid = (L + N) / 2;
2248 float mid_x = static_cast<float>(mid) / static_cast<float>(N - 1);
2249 float mid_y = eval_curve(curve, mid_x);
2250 tf.g = log2f_(mid_y) / log2f_(mid_x);
2251 tf.a = 1;
2252 tf.b = 0;
2253 tf.e = tf.c*tf.d + tf.f
2254 - powf_(tf.a*tf.d + tf.b, tf.g);
2255
2256
2257 if (!skcms_TransferFunction_invert(&tf, &tf_inv) ||
2258 !fit_nonlinear(curve, L,N, &tf_inv)) {
2259 continue;
2260 }
2261
2262 // We fit tf_inv, so calculate tf to keep in sync.
2263 // fit_nonlinear() should guarantee invertibility.
2264 if (!skcms_TransferFunction_invert(&tf_inv, &tf)) {
2265 assert(false);
2266 continue;
2267 }
2268 }
2269
2270 // We'd better have a sane, sRGB-ish TF by now.
2271 // Other non-Bad TFs would be fine, but we know we've only ever tried to fit sRGBish;
2272 // anything else is just some accident of math and the way we pun tf.g as a type flag.
2273 // fit_nonlinear() should guarantee this, but the special cases may fail this test.
2274 if (skcms_TFType_sRGBish != classify(tf)) {
2275 continue;
2276 }
2277
2278 // We find our error by roundtripping the table through tf_inv.
2279 //
2280 // (The most likely use case for this approximation is to be inverted and
2281 // used as the transfer function for a destination color space.)
2282 //
2283 // We've kept tf and tf_inv in sync above, but we can't guarantee that tf is
2284 // invertible, so re-verify that here (and use the new inverse for testing).
2285 // fit_nonlinear() should guarantee this, but the special cases that don't use
2286 // it may fail this test.
2287 if (!skcms_TransferFunction_invert(&tf, &tf_inv)) {
2288 continue;
2289 }
2290
2291 float err = skcms_MaxRoundtripError(curve, &tf_inv);
2292 if (*max_error > err) {
2293 *max_error = err;
2294 *approx = tf;
2295 }
2296 }
2297 return isfinitef_(*max_error);
2298}
Type::kYUV Type::kRGBA() int(0.7 *637)
#define N
Definition beziers.cpp:19
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition SkRecords.h:208
static int fit_linear(const skcms_Curve *curve, int N, float tol, float *c, float *d, float *f=nullptr)
Definition skcms.cc:1069
static skcms_TFType classify(const skcms_TransferFunction &tf, TF_PQish *pq=nullptr, TF_HLGish *hlg=nullptr)
Definition skcms.cc:135
static float eval_curve(const skcms_Curve *curve, float x)
Definition skcms.cc:247
float powf_(float x, float y)
Definition skcms.cc:93
static float log2f_(float x)
Definition skcms.cc:44
bool skcms_TransferFunction_invert(const skcms_TransferFunction *src, skcms_TransferFunction *dst)
Definition skcms.cc:1863
static bool fit_nonlinear(const skcms_Curve *curve, int L, int N, skcms_TransferFunction *tf)
Definition skcms.cc:2140
float skcms_MaxRoundtripError(const skcms_Curve *curve, const skcms_TransferFunction *inv_tf)
Definition skcms.cc:273
static bool isfinitef_(float x)
Definition skcms.cc:111
#define INFINITY_
#define ARRAY_COUNT(arr)
uint32_t table_entries

◆ skcms_ApproximatelyEqualProfiles()

SKCMS_API bool skcms_ApproximatelyEqualProfiles ( const skcms_ICCProfile A,
const skcms_ICCProfile B 
)

Definition at line 1621 of file skcms.cc.

1621 {
1622 // Test for exactly equal profiles first.
1623 if (A == B || 0 == memcmp(A,B, sizeof(skcms_ICCProfile))) {
1624 return true;
1625 }
1626
1627 // For now this is the essentially the same strategy we use in test_only.c
1628 // for our skcms_Transform() smoke tests:
1629 // 1) transform A to XYZD50
1630 // 2) transform B to XYZD50
1631 // 3) return true if they're similar enough
1632 // Our current criterion in 3) is maximum 1 bit error per XYZD50 byte.
1633
1634 // skcms_252_random_bytes are 252 of a random shuffle of all possible bytes.
1635 // 252 is evenly divisible by 3 and 4. Only 192, 10, 241, and 43 are missing.
1636
1637 // We want to allow otherwise equivalent profiles tagged as grayscale and RGB
1638 // to be treated as equal. But CMYK profiles are a totally different ballgame.
1639 const auto CMYK = skcms_Signature_CMYK;
1640 if ((A->data_color_space == CMYK) != (B->data_color_space == CMYK)) {
1641 return false;
1642 }
1643
1644 // Interpret as RGB_888 if data color space is RGB or GRAY, RGBA_8888 if CMYK.
1645 // TODO: working with RGBA_8888 either way is probably fastest.
1647 size_t npixels = 84;
1648 if (A->data_color_space == skcms_Signature_CMYK) {
1650 npixels = 63;
1651 }
1652
1653 // TODO: if A or B is a known profile (skcms_sRGB_profile, skcms_XYZD50_profile),
1654 // use pre-canned results and skip that skcms_Transform() call?
1655 uint8_t dstA[252],
1656 dstB[252];
1657 if (!skcms_Transform(
1660 npixels)) {
1661 return false;
1662 }
1663 if (!skcms_Transform(
1666 npixels)) {
1667 return false;
1668 }
1669
1670 // TODO: make sure this final check has reasonable codegen.
1671 for (size_t i = 0; i < 252; i++) {
1672 if (abs((int)dstA[i] - (int)dstB[i]) > 1) {
1673 return false;
1674 }
1675 }
1676 return true;
1677}
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition SkVx.h:707
static SkString fmt(SkColor4f c)
Definition p3.cpp:43
bool skcms_Transform(const void *src, skcms_PixelFormat srcFmt, skcms_AlphaFormat srcAlpha, const skcms_ICCProfile *srcProfile, void *dst, skcms_PixelFormat dstFmt, skcms_AlphaFormat dstAlpha, const skcms_ICCProfile *dstProfile, size_t nz)
Definition skcms.cc:2494
const uint8_t skcms_252_random_bytes[]
Definition skcms.cc:1602
const skcms_ICCProfile * skcms_XYZD50_profile()
Definition skcms.cc:1490

◆ skcms_AreApproximateInverses()

SKCMS_API bool skcms_AreApproximateInverses ( const skcms_Curve curve,
const skcms_TransferFunction inv_tf 
)

Definition at line 285 of file skcms.cc.

285 {
286 return skcms_MaxRoundtripError(curve, inv_tf) < (1/512.0f);
287}

◆ skcms_DisableRuntimeCPUDetection()

SKCMS_API void skcms_DisableRuntimeCPUDetection ( void  )

Definition at line 40 of file skcms.cc.

40 {
42}
static bool sAllowRuntimeCPUDetection
Definition skcms.cc:38

◆ skcms_GetCHAD()

SKCMS_API bool skcms_GetCHAD ( const skcms_ICCProfile profile,
skcms_Matrix3x3 m 
)

Definition at line 396 of file skcms.cc.

396 {
397 skcms_ICCTag tag;
398 if (!skcms_GetTagBySignature(profile, skcms_Signature_CHAD, &tag)) {
399 return false;
400 }
401
403 return false;
404 }
405
406 const sf32_Layout* sf32Tag = (const sf32_Layout*)tag.buf;
407 const uint8_t* values = sf32Tag->values;
408 for (int r = 0; r < 3; ++r)
409 for (int c = 0; c < 3; ++c, values += 4) {
410 m->vals[r][c] = read_big_fixed(values);
411 }
412 return true;
413}
@ skcms_Signature_sf32
Definition skcms.cc:319
@ skcms_Signature_CHAD
Definition skcms.cc:307
bool skcms_GetTagBySignature(const skcms_ICCProfile *profile, uint32_t sig, skcms_ICCTag *tag)
Definition skcms.cc:1221
static float read_big_fixed(const uint8_t *ptr)
Definition skcms.cc:348
#define SAFE_SIZEOF(x)
const uint8_t * buf

◆ skcms_GetWTPT()

SKCMS_API bool skcms_GetWTPT ( const skcms_ICCProfile profile,
float  xyz[3] 
)

Definition at line 438 of file skcms.cc.

438 {
439 skcms_ICCTag tag;
440 return skcms_GetTagBySignature(profile, skcms_Signature_WTPT, &tag) &&
441 read_tag_xyz(&tag, &xyz[0], &xyz[1], &xyz[2]);
442}
static bool read_tag_xyz(const skcms_ICCTag *tag, float *x, float *y, float *z)
Definition skcms.cc:425
@ skcms_Signature_WTPT
Definition skcms.cc:308

◆ skcms_Identity_TransferFunction()

SKCMS_API const skcms_TransferFunction * skcms_Identity_TransferFunction ( void  )

Definition at line 1597 of file skcms.cc.

1597 {
1598 static const skcms_TransferFunction identity = {1,1,0,0,0,0,0};
1599 return &identity;
1600}

◆ skcms_Init()

static void skcms_Init ( skcms_ICCProfile p)
inlinestatic

Definition at line 382 of file skcms_public.h.

382 {
383 memset(p, 0, sizeof(*p));
384 p->data_color_space = skcms_Signature_RGB;
385 p->pcs = skcms_Signature_XYZ;
386}

◆ skcms_MakeUsableAsDestination()

SKCMS_API bool skcms_MakeUsableAsDestination ( skcms_ICCProfile profile)

Definition at line 2819 of file skcms.cc.

2819 {
2820 if (!profile->has_B2A) {
2821 skcms_Matrix3x3 fromXYZD50;
2822 if (!profile->has_trc || !profile->has_toXYZD50
2823 || !skcms_Matrix3x3_invert(&profile->toXYZD50, &fromXYZD50)) {
2824 return false;
2825 }
2826
2828 for (int i = 0; i < 3; i++) {
2830 if (profile->trc[i].table_entries == 0
2831 && skcms_TransferFunction_invert(&profile->trc[i].parametric, &inv)) {
2832 tf[i] = profile->trc[i].parametric;
2833 continue;
2834 }
2835
2836 float max_error;
2837 // Parametric curves from skcms_ApproximateCurve() are guaranteed to be invertible.
2838 if (!skcms_ApproximateCurve(&profile->trc[i], &tf[i], &max_error)) {
2839 return false;
2840 }
2841 }
2842
2843 for (int i = 0; i < 3; ++i) {
2844 profile->trc[i].table_entries = 0;
2845 profile->trc[i].parametric = tf[i];
2846 }
2847 }
2849 return true;
2850}
static SkM44 inv(const SkM44 &m)
Definition 3d.cpp:26
bool skcms_Matrix3x3_invert(const skcms_Matrix3x3 *src, skcms_Matrix3x3 *dst)
Definition skcms.cc:1792
static void assert_usable_as_destination(const skcms_ICCProfile *profile)
Definition skcms.cc:2809
bool skcms_ApproximateCurve(const skcms_Curve *curve, skcms_TransferFunction *approx, float *max_error)
Definition skcms.cc:2199

◆ skcms_MakeUsableAsDestinationWithSingleCurve()

SKCMS_API bool skcms_MakeUsableAsDestinationWithSingleCurve ( skcms_ICCProfile profile)

Definition at line 2852 of file skcms.cc.

2852 {
2853 // Call skcms_MakeUsableAsDestination() with B2A disabled;
2854 // on success that'll return a TRC/XYZ profile with three skcms_TransferFunctions.
2856 result.has_B2A = false;
2858 return false;
2859 }
2860
2861 // Of the three, pick the transfer function that best fits the other two.
2862 int best_tf = 0;
2863 float min_max_error = INFINITY_;
2864 for (int i = 0; i < 3; i++) {
2866 if (!skcms_TransferFunction_invert(&result.trc[i].parametric, &inv)) {
2867 return false;
2868 }
2869
2870 float err = 0;
2871 for (int j = 0; j < 3; ++j) {
2872 err = fmaxf_(err, skcms_MaxRoundtripError(&profile->trc[j], &inv));
2873 }
2874 if (min_max_error > err) {
2875 min_max_error = err;
2876 best_tf = i;
2877 }
2878 }
2879
2880 for (int i = 0; i < 3; i++) {
2881 result.trc[i].parametric = result.trc[best_tf].parametric;
2882 }
2883
2884 *profile = result;
2886 return true;
2887}
GAsyncResult * result
static float fmaxf_(float x, float y)
Definition skcms.cc:108
bool skcms_MakeUsableAsDestination(skcms_ICCProfile *profile)
Definition skcms.cc:2819

◆ skcms_Matrix3x3_concat()

SKCMS_API skcms_Matrix3x3 skcms_Matrix3x3_concat ( const skcms_Matrix3x3 A,
const skcms_Matrix3x3 B 
)

Definition at line 1849 of file skcms.cc.

1849 {
1850 skcms_Matrix3x3 m = { { { 0,0,0 },{ 0,0,0 },{ 0,0,0 } } };
1851 for (int r = 0; r < 3; r++)
1852 for (int c = 0; c < 3; c++) {
1853 m.vals[r][c] = A->vals[r][0] * B->vals[0][c]
1854 + A->vals[r][1] * B->vals[1][c]
1855 + A->vals[r][2] * B->vals[2][c];
1856 }
1857 return m;
1858}

◆ skcms_Matrix3x3_invert()

SKCMS_API bool skcms_Matrix3x3_invert ( const skcms_Matrix3x3 src,
skcms_Matrix3x3 dst 
)

Definition at line 1792 of file skcms.cc.

1792 {
1793 double a00 = src->vals[0][0],
1794 a01 = src->vals[1][0],
1795 a02 = src->vals[2][0],
1796 a10 = src->vals[0][1],
1797 a11 = src->vals[1][1],
1798 a12 = src->vals[2][1],
1799 a20 = src->vals[0][2],
1800 a21 = src->vals[1][2],
1801 a22 = src->vals[2][2];
1802
1803 double b0 = a00*a11 - a01*a10,
1804 b1 = a00*a12 - a02*a10,
1805 b2 = a01*a12 - a02*a11,
1806 b3 = a20,
1807 b4 = a21,
1808 b5 = a22;
1809
1810 double determinant = b0*b5
1811 - b1*b4
1812 + b2*b3;
1813
1814 if (determinant == 0) {
1815 return false;
1816 }
1817
1818 double invdet = 1.0 / determinant;
1819 if (invdet > +FLT_MAX || invdet < -FLT_MAX || !isfinitef_((float)invdet)) {
1820 return false;
1821 }
1822
1823 b0 *= invdet;
1824 b1 *= invdet;
1825 b2 *= invdet;
1826 b3 *= invdet;
1827 b4 *= invdet;
1828 b5 *= invdet;
1829
1830 dst->vals[0][0] = (float)( a11*b5 - a12*b4 );
1831 dst->vals[1][0] = (float)( a02*b4 - a01*b5 );
1832 dst->vals[2][0] = (float)( + b2 );
1833 dst->vals[0][1] = (float)( a12*b3 - a10*b5 );
1834 dst->vals[1][1] = (float)( a00*b5 - a02*b3 );
1835 dst->vals[2][1] = (float)( - b1 );
1836 dst->vals[0][2] = (float)( a10*b4 - a11*b3 );
1837 dst->vals[1][2] = (float)( a01*b3 - a00*b4 );
1838 dst->vals[2][2] = (float)( + b0 );
1839
1840 for (int r = 0; r < 3; ++r)
1841 for (int c = 0; c < 3; ++c) {
1842 if (!isfinitef_(dst->vals[r][c])) {
1843 return false;
1844 }
1845 }
1846 return true;
1847}
dst
Definition cp.py:12

◆ skcms_Parse()

static bool skcms_Parse ( const void *  buf,
size_t  len,
skcms_ICCProfile profile 
)
inlinestatic

Definition at line 245 of file skcms_public.h.

245 {
246 // For continuity of existing user expectations,
247 // prefer A2B0 (perceptual) over A2B1 (relative colormetric), and ignore A2B2 (saturation).
248 const int priority[] = {0,1};
249 return skcms_ParseWithA2BPriority(buf, len,
250 priority, sizeof(priority)/sizeof(*priority),
251 profile);
252}
SKCMS_API bool skcms_ParseWithA2BPriority(const void *, size_t, const int priority[], int priorities, skcms_ICCProfile *)
Definition skcms.cc:1241

◆ skcms_ParseWithA2BPriority()

SKCMS_API bool skcms_ParseWithA2BPriority ( const void *  buf,
size_t  len,
const int  priority[],
int  priorities,
skcms_ICCProfile profile 
)

Definition at line 1241 of file skcms.cc.

1243 {
1244 static_assert(SAFE_SIZEOF(header_Layout) == 132, "need to update header code");
1245
1246 if (!profile) {
1247 return false;
1248 }
1249 memset(profile, 0, SAFE_SIZEOF(*profile));
1250
1251 if (len < SAFE_SIZEOF(header_Layout)) {
1252 return false;
1253 }
1254
1255 // Byte-swap all header fields
1256 const header_Layout* header = (const header_Layout*)buf;
1257 profile->buffer = (const uint8_t*)buf;
1258 profile->size = read_big_u32(header->size);
1259 uint32_t version = read_big_u32(header->version);
1260 profile->data_color_space = read_big_u32(header->data_color_space);
1261 profile->pcs = read_big_u32(header->pcs);
1262 uint32_t signature = read_big_u32(header->signature);
1263 float illuminant_X = read_big_fixed(header->illuminant_X);
1264 float illuminant_Y = read_big_fixed(header->illuminant_Y);
1265 float illuminant_Z = read_big_fixed(header->illuminant_Z);
1266 profile->tag_count = read_big_u32(header->tag_count);
1267
1268 // Validate signature, size (smaller than buffer, large enough to hold tag table),
1269 // and major version
1270 uint64_t tag_table_size = profile->tag_count * SAFE_SIZEOF(tag_Layout);
1271 if (signature != skcms_Signature_acsp ||
1272 profile->size > len ||
1273 profile->size < SAFE_SIZEOF(header_Layout) + tag_table_size ||
1274 (version >> 24) > 4) {
1275 return false;
1276 }
1277
1278 // Validate that illuminant is D50 white
1279 if (fabsf_(illuminant_X - 0.9642f) > 0.0100f ||
1280 fabsf_(illuminant_Y - 1.0000f) > 0.0100f ||
1281 fabsf_(illuminant_Z - 0.8249f) > 0.0100f) {
1282 return false;
1283 }
1284
1285 // Validate that all tag entries have sane offset + size
1286 const tag_Layout* tags = get_tag_table(profile);
1287 for (uint32_t i = 0; i < profile->tag_count; ++i) {
1288 uint32_t tag_offset = read_big_u32(tags[i].offset);
1289 uint32_t tag_size = read_big_u32(tags[i].size);
1290 uint64_t tag_end = (uint64_t)tag_offset + (uint64_t)tag_size;
1291 if (tag_size < 4 || tag_end > profile->size) {
1292 return false;
1293 }
1294 }
1295
1296 if (profile->pcs != skcms_Signature_XYZ && profile->pcs != skcms_Signature_Lab) {
1297 return false;
1298 }
1299
1300 bool pcs_is_xyz = profile->pcs == skcms_Signature_XYZ;
1301
1302 // Pre-parse commonly used tags.
1303 skcms_ICCTag kTRC;
1304 if (profile->data_color_space == skcms_Signature_Gray &&
1306 if (!read_curve(kTRC.buf, kTRC.size, &profile->trc[0], nullptr)) {
1307 // Malformed tag
1308 return false;
1309 }
1310 profile->trc[1] = profile->trc[0];
1311 profile->trc[2] = profile->trc[0];
1312 profile->has_trc = true;
1313
1314 if (pcs_is_xyz) {
1315 profile->toXYZD50.vals[0][0] = illuminant_X;
1316 profile->toXYZD50.vals[1][1] = illuminant_Y;
1317 profile->toXYZD50.vals[2][2] = illuminant_Z;
1318 profile->has_toXYZD50 = true;
1319 }
1320 } else {
1321 skcms_ICCTag rTRC, gTRC, bTRC;
1322 if (skcms_GetTagBySignature(profile, skcms_Signature_rTRC, &rTRC) &&
1325 if (!read_curve(rTRC.buf, rTRC.size, &profile->trc[0], nullptr) ||
1326 !read_curve(gTRC.buf, gTRC.size, &profile->trc[1], nullptr) ||
1327 !read_curve(bTRC.buf, bTRC.size, &profile->trc[2], nullptr)) {
1328 // Malformed TRC tags
1329 return false;
1330 }
1331 profile->has_trc = true;
1332 }
1333
1334 skcms_ICCTag rXYZ, gXYZ, bXYZ;
1335 if (skcms_GetTagBySignature(profile, skcms_Signature_rXYZ, &rXYZ) &&
1338 if (!read_to_XYZD50(&rXYZ, &gXYZ, &bXYZ, &profile->toXYZD50)) {
1339 // Malformed XYZ tags
1340 return false;
1341 }
1342 profile->has_toXYZD50 = true;
1343 }
1344 }
1345
1346 for (int i = 0; i < priorities; i++) {
1347 // enum { perceptual, relative_colormetric, saturation }
1348 if (priority[i] < 0 || priority[i] > 2) {
1349 return false;
1350 }
1351 uint32_t sig = skcms_Signature_A2B0 + static_cast<uint32_t>(priority[i]);
1352 skcms_ICCTag tag;
1353 if (skcms_GetTagBySignature(profile, sig, &tag)) {
1354 if (!read_a2b(&tag, &profile->A2B, pcs_is_xyz)) {
1355 // Malformed A2B tag
1356 return false;
1357 }
1358 profile->has_A2B = true;
1359 break;
1360 }
1361 }
1362
1363 for (int i = 0; i < priorities; i++) {
1364 // enum { perceptual, relative_colormetric, saturation }
1365 if (priority[i] < 0 || priority[i] > 2) {
1366 return false;
1367 }
1368 uint32_t sig = skcms_Signature_B2A0 + static_cast<uint32_t>(priority[i]);
1369 skcms_ICCTag tag;
1370 if (skcms_GetTagBySignature(profile, sig, &tag)) {
1371 if (!read_b2a(&tag, &profile->B2A, pcs_is_xyz)) {
1372 // Malformed B2A tag
1373 return false;
1374 }
1375 profile->has_B2A = true;
1376 break;
1377 }
1378 }
1379
1380 skcms_ICCTag cicp_tag;
1381 if (skcms_GetTagBySignature(profile, skcms_Signature_CICP, &cicp_tag)) {
1382 if (!read_cicp(&cicp_tag, &profile->CICP)) {
1383 // Malformed CICP tag
1384 return false;
1385 }
1386 profile->has_CICP = true;
1387 }
1388
1389 return usable_as_src(profile);
1390}
static bool read_b2a(const skcms_ICCTag *tag, skcms_B2A *b2a, bool pcs_is_xyz)
Definition skcms.cc:1163
static bool read_cicp(const skcms_ICCTag *tag, skcms_CICP *cicp)
Definition skcms.cc:1197
static bool read_a2b(const skcms_ICCTag *tag, skcms_A2B *a2b, bool pcs_is_xyz)
Definition skcms.cc:1138
static bool read_to_XYZD50(const skcms_ICCTag *rXYZ, const skcms_ICCTag *gXYZ, const skcms_ICCTag *bXYZ, skcms_Matrix3x3 *toXYZ)
Definition skcms.cc:444
static uint32_t read_big_u32(const uint8_t *ptr)
Definition skcms.cc:334
static const tag_Layout * get_tag_table(const skcms_ICCProfile *profile)
Definition skcms.cc:384
@ skcms_Signature_B2A0
Definition skcms.cc:305
@ skcms_Signature_rTRC
Definition skcms.cc:295
@ skcms_Signature_gTRC
Definition skcms.cc:296
@ skcms_Signature_gXYZ
Definition skcms.cc:301
@ skcms_Signature_A2B0
Definition skcms.cc:304
@ skcms_Signature_kTRC
Definition skcms.cc:298
@ skcms_Signature_acsp
Definition skcms.cc:292
@ skcms_Signature_bTRC
Definition skcms.cc:297
@ skcms_Signature_bXYZ
Definition skcms.cc:302
@ skcms_Signature_CICP
Definition skcms.cc:310
@ skcms_Signature_rXYZ
Definition skcms.cc:300
static bool usable_as_src(const skcms_ICCProfile *profile)
Definition skcms.cc:1236
static bool read_curve(const uint8_t *buf, uint32_t size, skcms_Curve *curve, uint32_t *curve_size)
Definition skcms.cc:578
static float fabsf_(float x)
static const char header[]
Definition skpbench.cpp:88
Point offset

◆ skcms_PrimariesToXYZD50()

SKCMS_API bool skcms_PrimariesToXYZD50 ( float  rx,
float  ry,
float  gx,
float  gy,
float  bx,
float  by,
float  wx,
float  wy,
skcms_Matrix3x3 toXYZD50 
)

Definition at line 1747 of file skcms.cc.

1751 {
1752 if (!is_zero_to_one(rx) || !is_zero_to_one(ry) ||
1753 !is_zero_to_one(gx) || !is_zero_to_one(gy) ||
1754 !is_zero_to_one(bx) || !is_zero_to_one(by) ||
1755 !is_zero_to_one(wx) || !is_zero_to_one(wy) ||
1756 !toXYZD50) {
1757 return false;
1758 }
1759
1760 // First, we need to convert xy values (primaries) to XYZ.
1761 skcms_Matrix3x3 primaries = {{
1762 { rx, gx, bx },
1763 { ry, gy, by },
1764 { 1 - rx - ry, 1 - gx - gy, 1 - bx - by },
1765 }};
1766 skcms_Matrix3x3 primaries_inv;
1767 if (!skcms_Matrix3x3_invert(&primaries, &primaries_inv)) {
1768 return false;
1769 }
1770
1771 // Assumes that Y is 1.0f.
1772 skcms_Vector3 wXYZ = { { wx / wy, 1, (1 - wx - wy) / wy } };
1773 skcms_Vector3 XYZ = mv_mul(&primaries_inv, &wXYZ);
1774
1775 skcms_Matrix3x3 toXYZ = {{
1776 { XYZ.vals[0], 0, 0 },
1777 { 0, XYZ.vals[1], 0 },
1778 { 0, 0, XYZ.vals[2] },
1779 }};
1780 toXYZ = skcms_Matrix3x3_concat(&primaries, &toXYZ);
1781
1782 skcms_Matrix3x3 DXtoD50;
1783 if (!skcms_AdaptToXYZD50(wx, wy, &DXtoD50)) {
1784 return false;
1785 }
1786
1787 *toXYZD50 = skcms_Matrix3x3_concat(&DXtoD50, &toXYZ);
1788 return true;
1789}
bool skcms_AdaptToXYZD50(float wx, float wy, skcms_Matrix3x3 *toXYZD50)
Definition skcms.cc:1706

◆ skcms_SetTransferFunction()

static void skcms_SetTransferFunction ( skcms_ICCProfile p,
const skcms_TransferFunction tf 
)
inlinestatic

Definition at line 388 of file skcms_public.h.

389 {
390 p->has_trc = true;
391 for (int i = 0; i < 3; ++i) {
392 p->trc[i].table_entries = 0;
393 p->trc[i].parametric = *tf;
394 }
395}

◆ skcms_SetXYZD50()

static void skcms_SetXYZD50 ( skcms_ICCProfile p,
const skcms_Matrix3x3 m 
)
inlinestatic

Definition at line 397 of file skcms_public.h.

397 {
398 p->has_toXYZD50 = true;
399 p->toXYZD50 = *m;
400}

◆ skcms_sRGB_Inverse_TransferFunction()

SKCMS_API const skcms_TransferFunction * skcms_sRGB_Inverse_TransferFunction ( void  )

Definition at line 1591 of file skcms.cc.

1591 {
1592 static const skcms_TransferFunction sRGB_inv =
1593 {0.416666657f, 1.137283325f, -0.0f, 12.920000076f, 0.003130805f, -0.054969788f, -0.0f};
1594 return &sRGB_inv;
1595}

◆ skcms_sRGB_profile()

SKCMS_API const skcms_ICCProfile * skcms_sRGB_profile ( void  )

Definition at line 1393 of file skcms.cc.

1393 {
1394 static const skcms_ICCProfile sRGB_profile = {
1395 nullptr, // buffer, moot here
1396
1397 0, // size, moot here
1398 skcms_Signature_RGB, // data_color_space
1399 skcms_Signature_XYZ, // pcs
1400 0, // tag count, moot here
1401
1402 // We choose to represent sRGB with its canonical transfer function,
1403 // and with its canonical XYZD50 gamut matrix.
1404 true, // has_trc, followed by the 3 trc curves
1405 {
1406 {{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}},
1407 {{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}},
1408 {{0, {2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0, 0}}},
1409 },
1410
1411 true, // has_toXYZD50, followed by 3x3 toXYZD50 matrix
1412 {{
1413 { 0.436065674f, 0.385147095f, 0.143066406f },
1414 { 0.222488403f, 0.716873169f, 0.060607910f },
1415 { 0.013916016f, 0.097076416f, 0.714096069f },
1416 }},
1417
1418 false, // has_A2B, followed by A2B itself, which we don't care about.
1419 {
1420 0,
1421 {
1422 {{0, {0,0, 0,0,0,0,0}}},
1423 {{0, {0,0, 0,0,0,0,0}}},
1424 {{0, {0,0, 0,0,0,0,0}}},
1425 {{0, {0,0, 0,0,0,0,0}}},
1426 },
1427 {0,0,0,0},
1428 nullptr,
1429 nullptr,
1430
1431 0,
1432 {
1433 {{0, {0,0, 0,0,0,0,0}}},
1434 {{0, {0,0, 0,0,0,0,0}}},
1435 {{0, {0,0, 0,0,0,0,0}}},
1436 },
1437 {{
1438 { 0,0,0,0 },
1439 { 0,0,0,0 },
1440 { 0,0,0,0 },
1441 }},
1442
1443 0,
1444 {
1445 {{0, {0,0, 0,0,0,0,0}}},
1446 {{0, {0,0, 0,0,0,0,0}}},
1447 {{0, {0,0, 0,0,0,0,0}}},
1448 },
1449 },
1450
1451 false, // has_B2A, followed by B2A itself, which we also don't care about.
1452 {
1453 0,
1454 {
1455 {{0, {0,0, 0,0,0,0,0}}},
1456 {{0, {0,0, 0,0,0,0,0}}},
1457 {{0, {0,0, 0,0,0,0,0}}},
1458 },
1459
1460 0,
1461 {{
1462 { 0,0,0,0 },
1463 { 0,0,0,0 },
1464 { 0,0,0,0 },
1465 }},
1466 {
1467 {{0, {0,0, 0,0,0,0,0}}},
1468 {{0, {0,0, 0,0,0,0,0}}},
1469 {{0, {0,0, 0,0,0,0,0}}},
1470 },
1471
1472 0,
1473 {0,0,0,0},
1474 nullptr,
1475 nullptr,
1476 {
1477 {{0, {0,0, 0,0,0,0,0}}},
1478 {{0, {0,0, 0,0,0,0,0}}},
1479 {{0, {0,0, 0,0,0,0,0}}},
1480 {{0, {0,0, 0,0,0,0,0}}},
1481 },
1482 },
1483
1484 false, // has_CICP, followed by cicp itself which we don't care about.
1485 { 0, 0, 0, 0 },
1486 };
1487 return &sRGB_profile;
1488}

◆ skcms_sRGB_TransferFunction()

SKCMS_API const skcms_TransferFunction * skcms_sRGB_TransferFunction ( void  )

Definition at line 1587 of file skcms.cc.

1587 {
1588 return &skcms_sRGB_profile()->trc[0].parametric;
1589}
const skcms_ICCProfile * skcms_sRGB_profile()
Definition skcms.cc:1393
skcms_Curve trc[3]
skcms_TransferFunction parametric

◆ skcms_TransferFunction_eval()

SKCMS_API float skcms_TransferFunction_eval ( const skcms_TransferFunction tf,
float  x 
)

Definition at line 212 of file skcms.cc.

212 {
213 float sign = x < 0 ? -1.0f : 1.0f;
214 x *= sign;
215
216 TF_PQish pq;
217 TF_HLGish hlg;
218 switch (classify(*tf, &pq, &hlg)) {
219 case skcms_TFType_Invalid: break;
220
221 case skcms_TFType_HLGish: {
222 const float K = hlg.K_minus_1 + 1.0f;
223 return K * sign * (x*hlg.R <= 1 ? powf_(x*hlg.R, hlg.G)
224 : expf_((x-hlg.c)*hlg.a) + hlg.b);
225 }
226
227 // skcms_TransferFunction_invert() inverts R, G, and a for HLGinvish so this math is fast.
229 const float K = hlg.K_minus_1 + 1.0f;
230 x /= K;
231 return sign * (x <= 1 ? hlg.R * powf_(x, hlg.G)
232 : hlg.a * logf_(x - hlg.b) + hlg.c);
233 }
234
236 return sign * (x < tf->d ? tf->c * x + tf->f
237 : powf_(tf->a * x + tf->b, tf->g) + tf->e);
238
240 return sign *
241 powf_((pq.A + pq.B * powf_(x, pq.C)) / (pq.D + pq.E * powf_(x, pq.C)), pq.F);
242 }
243 return 0;
244}
static int sign(SkScalar x)
Definition SkPath.cpp:2141
static const int K
Definition daa.cpp:21
static bool b
struct MyStruct a[10]
double x
static float logf_(float x)
Definition skcms.cc:60
static float expf_(float x)
Definition skcms.cc:103
float G
Definition skcms.cc:125
float K_minus_1
Definition skcms.cc:125
float R
Definition skcms.cc:125
float C
Definition skcms.cc:124
float F
Definition skcms.cc:124
float B
Definition skcms.cc:124
float A
Definition skcms.cc:124
float E
Definition skcms.cc:124
float D
Definition skcms.cc:124

◆ skcms_TransferFunction_getType()

SKCMS_API skcms_TFType skcms_TransferFunction_getType ( const skcms_TransferFunction tf)

Definition at line 183 of file skcms.cc.

183 {
184 return classify(*tf);
185}

◆ skcms_TransferFunction_invert()

SKCMS_API bool skcms_TransferFunction_invert ( const skcms_TransferFunction src,
skcms_TransferFunction dst 
)

Definition at line 1863 of file skcms.cc.

1863 {
1864 TF_PQish pq;
1865 TF_HLGish hlg;
1866 switch (classify(*src, &pq, &hlg)) {
1867 case skcms_TFType_Invalid: return false;
1868 case skcms_TFType_sRGBish: break; // handled below
1869
1870 case skcms_TFType_PQish:
1871 *dst = { TFKind_marker(skcms_TFType_PQish), -pq.A, pq.D, 1.0f/pq.F
1872 , pq.B, -pq.E, 1.0f/pq.C};
1873 return true;
1874
1876 *dst = { TFKind_marker(skcms_TFType_HLGinvish), 1.0f/hlg.R, 1.0f/hlg.G
1877 , 1.0f/hlg.a, hlg.b, hlg.c
1878 , hlg.K_minus_1 };
1879 return true;
1880
1882 *dst = { TFKind_marker(skcms_TFType_HLGish), 1.0f/hlg.R, 1.0f/hlg.G
1883 , 1.0f/hlg.a, hlg.b, hlg.c
1884 , hlg.K_minus_1 };
1885 return true;
1886 }
1887
1888 assert (classify(*src) == skcms_TFType_sRGBish);
1889
1890 // We're inverting this function, solving for x in terms of y.
1891 // y = (cx + f) x < d
1892 // (ax + b)^g + e x ≥ d
1893 // The inverse of this function can be expressed in the same piecewise form.
1894 skcms_TransferFunction inv = {0,0,0,0,0,0,0};
1895
1896 // We'll start by finding the new threshold inv.d.
1897 // In principle we should be able to find that by solving for y at x=d from either side.
1898 // (If those two d values aren't the same, it's a discontinuous transfer function.)
1899 float d_l = src->c * src->d + src->f,
1900 d_r = powf_(src->a * src->d + src->b, src->g) + src->e;
1901 if (fabsf_(d_l - d_r) > 1/512.0f) {
1902 return false;
1903 }
1904 inv.d = d_l; // TODO(mtklein): better in practice to choose d_r?
1905
1906 // When d=0, the linear section collapses to a point. We leave c,d,f all zero in that case.
1907 if (inv.d > 0) {
1908 // Inverting the linear section is pretty straightfoward:
1909 // y = cx + f
1910 // y - f = cx
1911 // (1/c)y - f/c = x
1912 inv.c = 1.0f/src->c;
1913 inv.f = -src->f/src->c;
1914 }
1915
1916 // The interesting part is inverting the nonlinear section:
1917 // y = (ax + b)^g + e.
1918 // y - e = (ax + b)^g
1919 // (y - e)^1/g = ax + b
1920 // (y - e)^1/g - b = ax
1921 // (1/a)(y - e)^1/g - b/a = x
1922 //
1923 // To make that fit our form, we need to move the (1/a) term inside the exponentiation:
1924 // let k = (1/a)^g
1925 // (1/a)( y - e)^1/g - b/a = x
1926 // (ky - ke)^1/g - b/a = x
1927
1928 float k = powf_(src->a, -src->g); // (1/a)^g == a^-g
1929 inv.g = 1.0f / src->g;
1930 inv.a = k;
1931 inv.b = -k * src->e;
1932 inv.e = -src->b / src->a;
1933
1934 // We need to enforce the same constraints here that we do when fitting a curve,
1935 // a >= 0 and ad+b >= 0. These constraints are checked by classify(), so they're true
1936 // of the source function if we're here.
1937
1938 // Just like when fitting the curve, there's really no way to rescue a < 0.
1939 if (inv.a < 0) {
1940 return false;
1941 }
1942 // On the other hand we can rescue an ad+b that's gone slightly negative here.
1943 if (inv.a * inv.d + inv.b < 0) {
1944 inv.b = -inv.a * inv.d;
1945 }
1946
1947 // That should usually make classify(inv) == sRGBish true, but there are a couple situations
1948 // where we might still fail here, like non-finite parameter values.
1950 return false;
1951 }
1952
1953 assert (inv.a >= 0);
1954 assert (inv.a * inv.d + inv.b >= 0);
1955
1956 // Now in principle we're done.
1957 // But to preserve the valuable invariant inv(src(1.0f)) == 1.0f, we'll tweak
1958 // e or f of the inverse, depending on which segment contains src(1.0f).
1959 float s = skcms_TransferFunction_eval(src, 1.0f);
1960 if (!isfinitef_(s)) {
1961 return false;
1962 }
1963
1964 float sign = s < 0 ? -1.0f : 1.0f;
1965 s *= sign;
1966 if (s < inv.d) {
1967 inv.f = 1.0f - sign * inv.c * s;
1968 } else {
1969 inv.e = 1.0f - sign * powf_(inv.a * s + inv.b, inv.g);
1970 }
1971
1972 *dst = inv;
1973 return classify(*dst) == skcms_TFType_sRGBish;
1974}
struct MyStruct s
float skcms_TransferFunction_eval(const skcms_TransferFunction *tf, float x)
Definition skcms.cc:212
static float TFKind_marker(skcms_TFType kind)
Definition skcms.cc:130
float a
Definition skcms.cc:125
float b
Definition skcms.cc:125
float c
Definition skcms.cc:125

◆ skcms_TransferFunction_isHLGish()

SKCMS_API bool skcms_TransferFunction_isHLGish ( const skcms_TransferFunction tf)

Definition at line 192 of file skcms.cc.

192 {
193 return classify(*tf) == skcms_TFType_HLGish;
194}

◆ skcms_TransferFunction_isPQish()

SKCMS_API bool skcms_TransferFunction_isPQish ( const skcms_TransferFunction tf)

Definition at line 189 of file skcms.cc.

189 {
190 return classify(*tf) == skcms_TFType_PQish;
191}

◆ skcms_TransferFunction_isSRGBish()

SKCMS_API bool skcms_TransferFunction_isSRGBish ( const skcms_TransferFunction tf)

Definition at line 186 of file skcms.cc.

186 {
187 return classify(*tf) == skcms_TFType_sRGBish;
188}

◆ skcms_TransferFunction_makeHLG()

static bool skcms_TransferFunction_makeHLG ( skcms_TransferFunction tf)
inlinestatic

Definition at line 95 of file skcms_public.h.

95 {
96 return skcms_TransferFunction_makeHLGish(tf, 2.0f, 2.0f
97 , 1/0.17883277f, 0.28466892f, 0.55991073f);
98}
static bool skcms_TransferFunction_makeHLGish(skcms_TransferFunction *fn, float R, float G, float a, float b, float c)

◆ skcms_TransferFunction_makeHLGish()

static bool skcms_TransferFunction_makeHLGish ( skcms_TransferFunction fn,
float  R,
float  G,
float  a,
float  b,
float  c 
)
inlinestatic

Definition at line 83 of file skcms_public.h.

85 {
86 return skcms_TransferFunction_makeScaledHLGish(fn, 1.0f, R,G, a,b,c);
87}
#define R(r)
SKCMS_API bool skcms_TransferFunction_makeScaledHLGish(skcms_TransferFunction *, float K, float R, float G, float a, float b, float c)
Definition skcms.cc:204
Definition SkMD5.cpp:125

◆ skcms_TransferFunction_makePQ()

static bool skcms_TransferFunction_makePQ ( skcms_TransferFunction tf)
inlinestatic

Definition at line 90 of file skcms_public.h.

90 {
91 return skcms_TransferFunction_makePQish(tf, -107/128.0f, 1.0f, 32/2523.0f
92 , 2413/128.0f, -2392/128.0f, 8192/1305.0f);
93}
SKCMS_API bool skcms_TransferFunction_makePQish(skcms_TransferFunction *, float A, float B, float C, float D, float E, float F)
Definition skcms.cc:196

◆ skcms_TransferFunction_makePQish()

SKCMS_API bool skcms_TransferFunction_makePQish ( skcms_TransferFunction tf,
float  A,
float  B,
float  C,
float  D,
float  E,
float  F 
)

Definition at line 196 of file skcms.cc.

198 {
199 *tf = { TFKind_marker(skcms_TFType_PQish), A,B,C,D,E,F };
201 return true;
202}
bool skcms_TransferFunction_isPQish(const skcms_TransferFunction *tf)
Definition skcms.cc:189
Definition SkMD5.cpp:120

◆ skcms_TransferFunction_makeScaledHLGish()

SKCMS_API bool skcms_TransferFunction_makeScaledHLGish ( skcms_TransferFunction tf,
float  K,
float  R,
float  G,
float  a,
float  b,
float  c 
)

Definition at line 204 of file skcms.cc.

206 {
207 *tf = { TFKind_marker(skcms_TFType_HLGish), R,G, a,b,c, K-1.0f };
209 return true;
210}
bool skcms_TransferFunction_isHLGish(const skcms_TransferFunction *tf)
Definition skcms.cc:192

◆ skcms_Transform()

SKCMS_API bool skcms_Transform ( const void *  src,
skcms_PixelFormat  srcFmt,
skcms_AlphaFormat  srcAlpha,
const skcms_ICCProfile srcProfile,
void *  dst,
skcms_PixelFormat  dstFmt,
skcms_AlphaFormat  dstAlpha,
const skcms_ICCProfile dstProfile,
size_t  npixels 
)

Definition at line 2494 of file skcms.cc.

2502 {
2503 const size_t dst_bpp = bytes_per_pixel(dstFmt),
2504 src_bpp = bytes_per_pixel(srcFmt);
2505 // Let's just refuse if the request is absurdly big.
2506 if (nz * dst_bpp > INT_MAX || nz * src_bpp > INT_MAX) {
2507 return false;
2508 }
2509 int n = (int)nz;
2510
2511 // Null profiles default to sRGB. Passing null for both is handy when doing format conversion.
2512 if (!srcProfile) {
2513 srcProfile = skcms_sRGB_profile();
2514 }
2515 if (!dstProfile) {
2516 dstProfile = skcms_sRGB_profile();
2517 }
2518
2519 // We can't transform in place unless the PixelFormats are the same size.
2520 if (dst == src && dst_bpp != src_bpp) {
2521 return false;
2522 }
2523 // TODO: more careful alias rejection (like, dst == src + 1)?
2524
2525 Op program[32];
2526 const void* context[32];
2527
2528 Op* ops = program;
2529 const void** contexts = context;
2530
2531 auto add_op = [&](Op o) {
2532 *ops++ = o;
2533 *contexts++ = nullptr;
2534 };
2535
2536 auto add_op_ctx = [&](Op o, const void* c) {
2537 *ops++ = o;
2538 *contexts++ = c;
2539 };
2540
2541 auto add_curve_ops = [&](const skcms_Curve* curves, int numChannels) {
2542 OpAndArg oa[4];
2543 assert(numChannels <= ARRAY_COUNT(oa));
2544
2545 int numOps = select_curve_ops(curves, numChannels, oa);
2546
2547 for (int i = 0; i < numOps; ++i) {
2548 add_op_ctx(oa[i].op, oa[i].arg);
2549 }
2550 };
2551
2552 // These are always parametric curves of some sort.
2553 skcms_Curve dst_curves[3];
2554 dst_curves[0].table_entries =
2555 dst_curves[1].table_entries =
2556 dst_curves[2].table_entries = 0;
2557
2558 skcms_Matrix3x3 from_xyz;
2559
2560 switch (srcFmt >> 1) {
2561 default: return false;
2562 case skcms_PixelFormat_A_8 >> 1: add_op(Op::load_a8); break;
2563 case skcms_PixelFormat_G_8 >> 1: add_op(Op::load_g8); break;
2564 case skcms_PixelFormat_ABGR_4444 >> 1: add_op(Op::load_4444); break;
2565 case skcms_PixelFormat_RGB_565 >> 1: add_op(Op::load_565); break;
2566 case skcms_PixelFormat_RGB_888 >> 1: add_op(Op::load_888); break;
2567 case skcms_PixelFormat_RGBA_8888 >> 1: add_op(Op::load_8888); break;
2568 case skcms_PixelFormat_RGBA_1010102 >> 1: add_op(Op::load_1010102); break;
2569 case skcms_PixelFormat_RGB_101010x_XR >> 1: add_op(Op::load_101010x_XR); break;
2570 case skcms_PixelFormat_RGB_161616LE >> 1: add_op(Op::load_161616LE); break;
2571 case skcms_PixelFormat_RGBA_16161616LE >> 1: add_op(Op::load_16161616LE); break;
2572 case skcms_PixelFormat_RGB_161616BE >> 1: add_op(Op::load_161616BE); break;
2573 case skcms_PixelFormat_RGBA_16161616BE >> 1: add_op(Op::load_16161616BE); break;
2574 case skcms_PixelFormat_RGB_hhh_Norm >> 1: add_op(Op::load_hhh); break;
2575 case skcms_PixelFormat_RGBA_hhhh_Norm >> 1: add_op(Op::load_hhhh); break;
2576 case skcms_PixelFormat_RGB_hhh >> 1: add_op(Op::load_hhh); break;
2577 case skcms_PixelFormat_RGBA_hhhh >> 1: add_op(Op::load_hhhh); break;
2578 case skcms_PixelFormat_RGB_fff >> 1: add_op(Op::load_fff); break;
2579 case skcms_PixelFormat_RGBA_ffff >> 1: add_op(Op::load_ffff); break;
2580
2582 add_op(Op::load_8888);
2583 add_op_ctx(Op::tf_rgb, skcms_sRGB_TransferFunction());
2584 break;
2585 }
2586 if (srcFmt == skcms_PixelFormat_RGB_hhh_Norm ||
2588 add_op(Op::clamp);
2589 }
2590 if (srcFmt & 1) {
2591 add_op(Op::swap_rb);
2592 }
2593 skcms_ICCProfile gray_dst_profile;
2594 if ((dstFmt >> 1) == (skcms_PixelFormat_G_8 >> 1)) {
2595 // When transforming to gray, stop at XYZ (by setting toXYZ to identity), then transform
2596 // luminance (Y) by the destination transfer function.
2597 gray_dst_profile = *dstProfile;
2598 skcms_SetXYZD50(&gray_dst_profile, &skcms_XYZD50_profile()->toXYZD50);
2599 dstProfile = &gray_dst_profile;
2600 }
2601
2602 if (srcProfile->data_color_space == skcms_Signature_CMYK) {
2603 // Photoshop creates CMYK images as inverse CMYK.
2604 // These happen to be the only ones we've _ever_ seen.
2605 add_op(Op::invert);
2606 // With CMYK, ignore the alpha type, to avoid changing K or conflating CMY with K.
2607 srcAlpha = skcms_AlphaFormat_Unpremul;
2608 }
2609
2610 if (srcAlpha == skcms_AlphaFormat_Opaque) {
2611 add_op(Op::force_opaque);
2612 } else if (srcAlpha == skcms_AlphaFormat_PremulAsEncoded) {
2613 add_op(Op::unpremul);
2614 }
2615
2616 if (dstProfile != srcProfile) {
2617
2618 if (!prep_for_destination(dstProfile,
2619 &from_xyz,
2620 &dst_curves[0].parametric,
2621 &dst_curves[1].parametric,
2622 &dst_curves[2].parametric)) {
2623 return false;
2624 }
2625
2626 if (srcProfile->has_A2B) {
2627 if (srcProfile->A2B.input_channels) {
2628 add_curve_ops(srcProfile->A2B.input_curves,
2629 (int)srcProfile->A2B.input_channels);
2630 add_op(Op::clamp);
2631 add_op_ctx(Op::clut_A2B, &srcProfile->A2B);
2632 }
2633
2634 if (srcProfile->A2B.matrix_channels == 3) {
2635 add_curve_ops(srcProfile->A2B.matrix_curves, /*numChannels=*/3);
2636
2637 static const skcms_Matrix3x4 I = {{
2638 {1,0,0,0},
2639 {0,1,0,0},
2640 {0,0,1,0},
2641 }};
2642 if (0 != memcmp(&I, &srcProfile->A2B.matrix, sizeof(I))) {
2643 add_op_ctx(Op::matrix_3x4, &srcProfile->A2B.matrix);
2644 }
2645 }
2646
2647 if (srcProfile->A2B.output_channels == 3) {
2648 add_curve_ops(srcProfile->A2B.output_curves, /*numChannels=*/3);
2649 }
2650
2651 if (srcProfile->pcs == skcms_Signature_Lab) {
2652 add_op(Op::lab_to_xyz);
2653 }
2654
2655 } else if (srcProfile->has_trc && srcProfile->has_toXYZD50) {
2656 add_curve_ops(srcProfile->trc, /*numChannels=*/3);
2657 } else {
2658 return false;
2659 }
2660
2661 // A2B sources are in XYZD50 by now, but TRC sources are still in their original gamut.
2662 assert (srcProfile->has_A2B || srcProfile->has_toXYZD50);
2663
2664 if (dstProfile->has_B2A) {
2665 // B2A needs its input in XYZD50, so transform TRC sources now.
2666 if (!srcProfile->has_A2B) {
2667 add_op_ctx(Op::matrix_3x3, &srcProfile->toXYZD50);
2668 }
2669
2670 if (dstProfile->pcs == skcms_Signature_Lab) {
2671 add_op(Op::xyz_to_lab);
2672 }
2673
2674 if (dstProfile->B2A.input_channels == 3) {
2675 add_curve_ops(dstProfile->B2A.input_curves, /*numChannels=*/3);
2676 }
2677
2678 if (dstProfile->B2A.matrix_channels == 3) {
2679 static const skcms_Matrix3x4 I = {{
2680 {1,0,0,0},
2681 {0,1,0,0},
2682 {0,0,1,0},
2683 }};
2684 if (0 != memcmp(&I, &dstProfile->B2A.matrix, sizeof(I))) {
2685 add_op_ctx(Op::matrix_3x4, &dstProfile->B2A.matrix);
2686 }
2687
2688 add_curve_ops(dstProfile->B2A.matrix_curves, /*numChannels=*/3);
2689 }
2690
2691 if (dstProfile->B2A.output_channels) {
2692 add_op(Op::clamp);
2693 add_op_ctx(Op::clut_B2A, &dstProfile->B2A);
2694
2695 add_curve_ops(dstProfile->B2A.output_curves,
2696 (int)dstProfile->B2A.output_channels);
2697 }
2698 } else {
2699 // This is a TRC destination.
2700 // We'll concat any src->xyz matrix with our xyz->dst matrix into one src->dst matrix.
2701 // (A2B sources are already in XYZD50, making that src->xyz matrix I.)
2702 static const skcms_Matrix3x3 I = {{
2703 { 1.0f, 0.0f, 0.0f },
2704 { 0.0f, 1.0f, 0.0f },
2705 { 0.0f, 0.0f, 1.0f },
2706 }};
2707 const skcms_Matrix3x3* to_xyz = srcProfile->has_A2B ? &I : &srcProfile->toXYZD50;
2708
2709 // There's a chance the source and destination gamuts are identical,
2710 // in which case we can skip the gamut transform.
2711 if (0 != memcmp(&dstProfile->toXYZD50, to_xyz, sizeof(skcms_Matrix3x3))) {
2712 // Concat the entire gamut transform into from_xyz,
2713 // now slightly misnamed but it's a handy spot to stash the result.
2714 from_xyz = skcms_Matrix3x3_concat(&from_xyz, to_xyz);
2715 add_op_ctx(Op::matrix_3x3, &from_xyz);
2716 }
2717
2718 // Encode back to dst RGB using its parametric transfer functions.
2719 OpAndArg oa[3];
2720 int numOps = select_curve_ops(dst_curves, /*numChannels=*/3, oa);
2721 for (int index = 0; index < numOps; ++index) {
2722 assert(oa[index].op != Op::table_r &&
2723 oa[index].op != Op::table_g &&
2724 oa[index].op != Op::table_b &&
2725 oa[index].op != Op::table_a);
2726 add_op_ctx(oa[index].op, oa[index].arg);
2727 }
2728 }
2729 }
2730
2731 // Clamp here before premul to make sure we're clamping to normalized values _and_ gamut,
2732 // not just to values that fit in [0,1].
2733 //
2734 // E.g. r = 1.1, a = 0.5 would fit fine in fixed point after premul (ra=0.55,a=0.5),
2735 // but would be carrying r > 1, which is really unexpected for downstream consumers.
2736 if (dstFmt < skcms_PixelFormat_RGB_hhh) {
2737 add_op(Op::clamp);
2738 }
2739
2740 if (dstProfile->data_color_space == skcms_Signature_CMYK) {
2741 // Photoshop creates CMYK images as inverse CMYK.
2742 // These happen to be the only ones we've _ever_ seen.
2743 add_op(Op::invert);
2744
2745 // CMYK has no alpha channel, so make sure dstAlpha is a no-op.
2746 dstAlpha = skcms_AlphaFormat_Unpremul;
2747 }
2748
2749 if (dstAlpha == skcms_AlphaFormat_Opaque) {
2750 add_op(Op::force_opaque);
2751 } else if (dstAlpha == skcms_AlphaFormat_PremulAsEncoded) {
2752 add_op(Op::premul);
2753 }
2754 if (dstFmt & 1) {
2755 add_op(Op::swap_rb);
2756 }
2757 switch (dstFmt >> 1) {
2758 default: return false;
2759 case skcms_PixelFormat_A_8 >> 1: add_op(Op::store_a8); break;
2760 case skcms_PixelFormat_G_8 >> 1: add_op(Op::store_g8); break;
2761 case skcms_PixelFormat_ABGR_4444 >> 1: add_op(Op::store_4444); break;
2762 case skcms_PixelFormat_RGB_565 >> 1: add_op(Op::store_565); break;
2763 case skcms_PixelFormat_RGB_888 >> 1: add_op(Op::store_888); break;
2764 case skcms_PixelFormat_RGBA_8888 >> 1: add_op(Op::store_8888); break;
2765 case skcms_PixelFormat_RGBA_1010102 >> 1: add_op(Op::store_1010102); break;
2766 case skcms_PixelFormat_RGB_161616LE >> 1: add_op(Op::store_161616LE); break;
2767 case skcms_PixelFormat_RGBA_16161616LE >> 1: add_op(Op::store_16161616LE); break;
2768 case skcms_PixelFormat_RGB_161616BE >> 1: add_op(Op::store_161616BE); break;
2769 case skcms_PixelFormat_RGBA_16161616BE >> 1: add_op(Op::store_16161616BE); break;
2770 case skcms_PixelFormat_RGB_hhh_Norm >> 1: add_op(Op::store_hhh); break;
2771 case skcms_PixelFormat_RGBA_hhhh_Norm >> 1: add_op(Op::store_hhhh); break;
2772 case skcms_PixelFormat_RGB_101010x_XR >> 1: add_op(Op::store_101010x_XR); break;
2773 case skcms_PixelFormat_RGB_hhh >> 1: add_op(Op::store_hhh); break;
2774 case skcms_PixelFormat_RGBA_hhhh >> 1: add_op(Op::store_hhhh); break;
2775 case skcms_PixelFormat_RGB_fff >> 1: add_op(Op::store_fff); break;
2776 case skcms_PixelFormat_RGBA_ffff >> 1: add_op(Op::store_ffff); break;
2777
2779 add_op_ctx(Op::tf_rgb, skcms_sRGB_Inverse_TransferFunction());
2780 add_op(Op::store_8888);
2781 break;
2782 }
2783
2784 assert(ops <= program + ARRAY_COUNT(program));
2785 assert(contexts <= context + ARRAY_COUNT(context));
2786
2788 switch (cpu_type()) {
2789 case CpuType::SKX:
2790 #if !defined(SKCMS_DISABLE_SKX)
2792 break;
2793 #endif
2794
2795 case CpuType::HSW:
2796 #if !defined(SKCMS_DISABLE_HSW)
2798 break;
2799 #endif
2800
2801 case CpuType::Baseline:
2802 break;
2803 }
2804
2805 run(program, context, ops - program, (const char*)src, (char*)dst, n, src_bpp,dst_bpp);
2806 return true;
2807}
SkPathOp ops[]
Definition run.py:1
void run_program(const Op *program, const void **contexts, ptrdiff_t programSize, const char *src, char *dst, int n, const size_t src_bpp, const size_t dst_bpp)
void run_program(const Op *program, const void **contexts, ptrdiff_t programSize, const char *src, char *dst, int n, const size_t src_bpp, const size_t dst_bpp)
void run_program(const Op *program, const void **contexts, ptrdiff_t programSize, const char *src, char *dst, int n, const size_t src_bpp, const size_t dst_bpp)
static int select_curve_ops(const skcms_Curve *curves, int numChannels, OpAndArg *ops)
Definition skcms.cc:2405
const skcms_TransferFunction * skcms_sRGB_Inverse_TransferFunction()
Definition skcms.cc:1591
static size_t bytes_per_pixel(skcms_PixelFormat fmt)
Definition skcms.cc:2449
static CpuType cpu_type()
Definition skcms.cc:2302
static bool prep_for_destination(const skcms_ICCProfile *profile, skcms_Matrix3x3 *fromXYZD50, skcms_TransferFunction *invR, skcms_TransferFunction *invG, skcms_TransferFunction *invB)
Definition skcms.cc:2475
const skcms_TransferFunction * skcms_sRGB_TransferFunction()
Definition skcms.cc:1587
static void skcms_SetXYZD50(skcms_ICCProfile *p, const skcms_Matrix3x3 *m)
Definition SkMD5.cpp:134
uint32_t matrix_channels
skcms_Curve matrix_curves[3]
skcms_Curve output_curves[3]
uint32_t output_channels
skcms_Matrix3x4 matrix
uint32_t input_channels
skcms_Curve input_curves[4]
skcms_Matrix3x4 matrix
skcms_Curve output_curves[4]
skcms_Curve matrix_curves[3]
skcms_Curve input_curves[3]
uint32_t matrix_channels
uint32_t input_channels
uint32_t output_channels
skcms_Matrix3x3 toXYZD50
uint32_t data_color_space

◆ skcms_TRCs_AreApproximateInverse()

SKCMS_API bool skcms_TRCs_AreApproximateInverse ( const skcms_ICCProfile profile,
const skcms_TransferFunction inv_tf 
)

Definition at line 1679 of file skcms.cc.

1680 {
1681 if (!profile || !profile->has_trc) {
1682 return false;
1683 }
1684
1685 return skcms_AreApproximateInverses(&profile->trc[0], inv_tf) &&
1686 skcms_AreApproximateInverses(&profile->trc[1], inv_tf) &&
1687 skcms_AreApproximateInverses(&profile->trc[2], inv_tf);
1688}
bool skcms_AreApproximateInverses(const skcms_Curve *curve, const skcms_TransferFunction *inv_tf)
Definition skcms.cc:285

◆ skcms_XYZD50_profile()

SKCMS_API const skcms_ICCProfile * skcms_XYZD50_profile ( void  )

Definition at line 1490 of file skcms.cc.

1490 {
1491 // Just like sRGB above, but with identity transfer functions and toXYZD50 matrix.
1492 static const skcms_ICCProfile XYZD50_profile = {
1493 nullptr, // buffer, moot here
1494
1495 0, // size, moot here
1496 skcms_Signature_RGB, // data_color_space
1497 skcms_Signature_XYZ, // pcs
1498 0, // tag count, moot here
1499
1500 true, // has_trc, followed by the 3 trc curves
1501 {
1502 {{0, {1,1, 0,0,0,0,0}}},
1503 {{0, {1,1, 0,0,0,0,0}}},
1504 {{0, {1,1, 0,0,0,0,0}}},
1505 },
1506
1507 true, // has_toXYZD50, followed by 3x3 toXYZD50 matrix
1508 {{
1509 { 1,0,0 },
1510 { 0,1,0 },
1511 { 0,0,1 },
1512 }},
1513
1514 false, // has_A2B, followed by A2B itself, which we don't care about.
1515 {
1516 0,
1517 {
1518 {{0, {0,0, 0,0,0,0,0}}},
1519 {{0, {0,0, 0,0,0,0,0}}},
1520 {{0, {0,0, 0,0,0,0,0}}},
1521 {{0, {0,0, 0,0,0,0,0}}},
1522 },
1523 {0,0,0,0},
1524 nullptr,
1525 nullptr,
1526
1527 0,
1528 {
1529 {{0, {0,0, 0,0,0,0,0}}},
1530 {{0, {0,0, 0,0,0,0,0}}},
1531 {{0, {0,0, 0,0,0,0,0}}},
1532 },
1533 {{
1534 { 0,0,0,0 },
1535 { 0,0,0,0 },
1536 { 0,0,0,0 },
1537 }},
1538
1539 0,
1540 {
1541 {{0, {0,0, 0,0,0,0,0}}},
1542 {{0, {0,0, 0,0,0,0,0}}},
1543 {{0, {0,0, 0,0,0,0,0}}},
1544 },
1545 },
1546
1547 false, // has_B2A, followed by B2A itself, which we also don't care about.
1548 {
1549 0,
1550 {
1551 {{0, {0,0, 0,0,0,0,0}}},
1552 {{0, {0,0, 0,0,0,0,0}}},
1553 {{0, {0,0, 0,0,0,0,0}}},
1554 },
1555
1556 0,
1557 {{
1558 { 0,0,0,0 },
1559 { 0,0,0,0 },
1560 { 0,0,0,0 },
1561 }},
1562 {
1563 {{0, {0,0, 0,0,0,0,0}}},
1564 {{0, {0,0, 0,0,0,0,0}}},
1565 {{0, {0,0, 0,0,0,0,0}}},
1566 },
1567
1568 0,
1569 {0,0,0,0},
1570 nullptr,
1571 nullptr,
1572 {
1573 {{0, {0,0, 0,0,0,0,0}}},
1574 {{0, {0,0, 0,0,0,0,0}}},
1575 {{0, {0,0, 0,0,0,0,0}}},
1576 {{0, {0,0, 0,0,0,0,0}}},
1577 },
1578 },
1579
1580 false, // has_CICP, followed by cicp itself which we don't care about.
1581 { 0, 0, 0, 0 },
1582 };
1583
1584 return &XYZD50_profile;
1585}