Flutter Engine
The Flutter Engine
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 , skcms_PixelFormat_RGBA_10101010_XR , skcms_PixelFormat_BGRA_10101010_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
Definition: skcms_public.h:269
@ skcms_Signature_RGB
Definition: skcms_public.h:266
@ skcms_Signature_Gray
Definition: skcms_public.h:265
@ skcms_Signature_XYZ
Definition: skcms_public.h:270
@ skcms_Signature_CMYK
Definition: skcms_public.h:264

◆ skcms_AlphaFormat

Enumerator
skcms_AlphaFormat_Opaque 
skcms_AlphaFormat_Unpremul 
skcms_AlphaFormat_PremulAsEncoded 

Definition at line 336 of file skcms_public.h.

336 {
337 skcms_AlphaFormat_Opaque, // alpha is always opaque
338 // tf-1(r), tf-1(g), tf-1(b), 1.0
339 skcms_AlphaFormat_Unpremul, // alpha and color are unassociated
340 // tf-1(r), tf-1(g), tf-1(b), a
341 skcms_AlphaFormat_PremulAsEncoded, // premultiplied while encoded
342 // tf-1(r)*a, tf-1(g)*a, tf-1(b)*a, a
skcms_AlphaFormat
Definition: skcms_public.h:336
@ skcms_AlphaFormat_Unpremul
Definition: skcms_public.h:339
@ skcms_AlphaFormat_PremulAsEncoded
Definition: skcms_public.h:341
@ skcms_AlphaFormat_Opaque
Definition: skcms_public.h:337

◆ 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 
skcms_PixelFormat_RGBA_10101010_XR 
skcms_PixelFormat_BGRA_10101010_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.
322 skcms_PixelFormat_RGBA_10101010_XR, // Note: This is located here to signal no clamping.
323 skcms_PixelFormat_BGRA_10101010_XR, // Compatible with MTLPixelFormatBGRA10_XR.
skcms_PixelFormat
Definition: skcms_public.h:273
@ skcms_PixelFormat_BGRA_ffff
Definition: skcms_public.h:318
@ skcms_PixelFormat_RGBA_8888_sRGB
Definition: skcms_public.h:289
@ skcms_PixelFormat_BGRA_hhhh
Definition: skcms_public.h:313
@ skcms_PixelFormat_RGBA_16161616BE
Definition: skcms_public.h:302
@ skcms_PixelFormat_A_8_
Definition: skcms_public.h:275
@ skcms_PixelFormat_RGB_161616LE
Definition: skcms_public.h:295
@ skcms_PixelFormat_BGR_hhh_Norm
Definition: skcms_public.h:306
@ skcms_PixelFormat_BGR_101010x_XR
Definition: skcms_public.h:321
@ skcms_PixelFormat_RGBA_ffff
Definition: skcms_public.h:317
@ skcms_PixelFormat_BGR_fff
Definition: skcms_public.h:316
@ skcms_PixelFormat_RGBA_1010102
Definition: skcms_public.h:292
@ skcms_PixelFormat_BGR_888
Definition: skcms_public.h:286
@ skcms_PixelFormat_BGRA_8888
Definition: skcms_public.h:288
@ skcms_PixelFormat_BGRA_10101010_XR
Definition: skcms_public.h:323
@ skcms_PixelFormat_RGBA_hhhh
Definition: skcms_public.h:312
@ skcms_PixelFormat_RGB_fff
Definition: skcms_public.h:315
@ skcms_PixelFormat_RGB_hhh_Norm
Definition: skcms_public.h:305
@ skcms_PixelFormat_BGR_161616BE
Definition: skcms_public.h:301
@ skcms_PixelFormat_RGBA_8888
Definition: skcms_public.h:287
@ skcms_PixelFormat_BGRA_hhhh_Norm
Definition: skcms_public.h:308
@ skcms_PixelFormat_RGB_hhh
Definition: skcms_public.h:310
@ skcms_PixelFormat_BGRA_16161616BE
Definition: skcms_public.h:303
@ skcms_PixelFormat_RGB_888
Definition: skcms_public.h:285
@ skcms_PixelFormat_G_8
Definition: skcms_public.h:276
@ skcms_PixelFormat_BGR_hhh
Definition: skcms_public.h:311
@ skcms_PixelFormat_G_8_
Definition: skcms_public.h:277
@ skcms_PixelFormat_BGR_565
Definition: skcms_public.h:280
@ skcms_PixelFormat_ABGR_4444
Definition: skcms_public.h:282
@ skcms_PixelFormat_BGRA_8888_sRGB
Definition: skcms_public.h:290
@ skcms_PixelFormat_A_8
Definition: skcms_public.h:274
@ skcms_PixelFormat_ARGB_4444
Definition: skcms_public.h:283
@ skcms_PixelFormat_BGR_161616LE
Definition: skcms_public.h:296
@ skcms_PixelFormat_RGB_565
Definition: skcms_public.h:279
@ skcms_PixelFormat_RGB_161616BE
Definition: skcms_public.h:300
@ skcms_PixelFormat_BGRA_1010102
Definition: skcms_public.h:293
@ skcms_PixelFormat_RGBA_hhhh_Norm
Definition: skcms_public.h:307
@ skcms_PixelFormat_RGBA_10101010_XR
Definition: skcms_public.h:322
@ skcms_PixelFormat_BGRA_16161616LE
Definition: skcms_public.h:298
@ skcms_PixelFormat_RGB_101010x_XR
Definition: skcms_public.h:320
@ skcms_PixelFormat_RGBA_16161616LE
Definition: skcms_public.h:297

◆ 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
Definition: skcms_public.h:54
@ skcms_TFType_Invalid
Definition: skcms_public.h:55
@ skcms_TFType_HLGish
Definition: skcms_public.h:58
@ skcms_TFType_sRGBish
Definition: skcms_public.h:56
@ skcms_TFType_HLGinvish
Definition: skcms_public.h:59
@ skcms_TFType_PQish
Definition: skcms_public.h:57

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}
#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
Definition: skcms_public.h:112

◆ 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:2495
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;
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}
for(const auto glyph :glyphs)
Definition: FontMgrTest.cpp:52
@ 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)
uint8_t values[36]
Definition: skcms.cc:393
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;
441 read_tag_xyz(&tag, &xyz[0], &xyz[1], &xyz[2]);
442}
@ skcms_Signature_WTPT
Definition: skcms.cc:308
static bool read_tag_xyz(const skcms_ICCTag *tag, float *x, float *y, float *z)
Definition: skcms.cc:425

◆ 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 384 of file skcms_public.h.

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

◆ skcms_MakeUsableAsDestination()

SKCMS_API bool skcms_MakeUsableAsDestination ( skcms_ICCProfile profile)

Definition at line 2821 of file skcms.cc.

2821 {
2822 if (!profile->has_B2A) {
2823 skcms_Matrix3x3 fromXYZD50;
2824 if (!profile->has_trc || !profile->has_toXYZD50
2825 || !skcms_Matrix3x3_invert(&profile->toXYZD50, &fromXYZD50)) {
2826 return false;
2827 }
2828
2830 for (int i = 0; i < 3; i++) {
2832 if (profile->trc[i].table_entries == 0
2833 && skcms_TransferFunction_invert(&profile->trc[i].parametric, &inv)) {
2834 tf[i] = profile->trc[i].parametric;
2835 continue;
2836 }
2837
2838 float max_error;
2839 // Parametric curves from skcms_ApproximateCurve() are guaranteed to be invertible.
2840 if (!skcms_ApproximateCurve(&profile->trc[i], &tf[i], &max_error)) {
2841 return false;
2842 }
2843 }
2844
2845 for (int i = 0; i < 3; ++i) {
2846 profile->trc[i].table_entries = 0;
2847 profile->trc[i].parametric = tf[i];
2848 }
2849 }
2851 return true;
2852}
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:2811
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 2854 of file skcms.cc.

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

◆ 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};
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;
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;
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;
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}
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static bool read_b2a(const skcms_ICCTag *tag, skcms_B2A *b2a, bool pcs_is_xyz)
Definition: skcms.cc:1163
@ 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 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
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
SeparatedVector2 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 390 of file skcms_public.h.

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

◆ skcms_SetXYZD50()

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

Definition at line 399 of file skcms_public.h.

399 {
400 p->has_toXYZD50 = true;
401 p->toXYZD50 = *m;
402}

◆ 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]
Definition: skcms_public.h:186
skcms_TransferFunction parametric
Definition: skcms_public.h:109

◆ 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:2205
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)
Definition: skcms_public.h:83

◆ 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}
#define C(TEST_CATEGORY)
Definition: colrv1.cpp:248
#define B
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 2495 of file skcms.cc.

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

◆ 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}