Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Enumerations | Functions | Variables
skcms.cc File Reference
#include "src/skcms_public.h"
#include "src/skcms_internals.h"
#include "src/skcms_Transform.h"
#include <assert.h>
#include <float.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

Go to the source code of this file.

Classes

struct  TF_PQish
 
struct  TF_HLGish
 
struct  header_Layout
 
struct  tag_Layout
 
struct  sf32_Layout
 
struct  XYZ_Layout
 
struct  para_Layout
 
struct  curv_Layout
 
struct  mft_CommonLayout
 
struct  mft1_Layout
 
struct  mft2_Layout
 
struct  mAB_or_mBA_Layout
 
struct  CLUT_Layout
 
struct  CICP_Layout
 
struct  skcms_Vector3
 
struct  OpAndArg
 

Enumerations

enum  {
  skcms_Signature_acsp = 0x61637370 , skcms_Signature_rTRC = 0x72545243 , skcms_Signature_gTRC = 0x67545243 , skcms_Signature_bTRC = 0x62545243 ,
  skcms_Signature_kTRC = 0x6B545243 , skcms_Signature_rXYZ = 0x7258595A , skcms_Signature_gXYZ = 0x6758595A , skcms_Signature_bXYZ = 0x6258595A ,
  skcms_Signature_A2B0 = 0x41324230 , skcms_Signature_B2A0 = 0x42324130 , skcms_Signature_CHAD = 0x63686164 , skcms_Signature_WTPT = 0x77747074 ,
  skcms_Signature_CICP = 0x63696370 , skcms_Signature_curv = 0x63757276 , skcms_Signature_mft1 = 0x6D667431 , skcms_Signature_mft2 = 0x6D667432 ,
  skcms_Signature_mAB = 0x6D414220 , skcms_Signature_mBA = 0x6D424120 , skcms_Signature_para = 0x70617261 , skcms_Signature_sf32 = 0x73663332
}
 
enum class  CpuType { Baseline , HSW , SKX }
 

Functions

void skcms_DisableRuntimeCPUDetection ()
 
static float log2f_ (float x)
 
static float logf_ (float x)
 
static float exp2f_ (float x)
 
float powf_ (float x, float y)
 
static float expf_ (float x)
 
static float fmaxf_ (float x, float y)
 
static float fminf_ (float x, float y)
 
static bool isfinitef_ (float x)
 
static float minus_1_ulp (float x)
 
static float TFKind_marker (skcms_TFType kind)
 
static skcms_TFType classify (const skcms_TransferFunction &tf, TF_PQish *pq=nullptr, TF_HLGish *hlg=nullptr)
 
skcms_TFType skcms_TransferFunction_getType (const skcms_TransferFunction *tf)
 
bool skcms_TransferFunction_isSRGBish (const skcms_TransferFunction *tf)
 
bool skcms_TransferFunction_isPQish (const skcms_TransferFunction *tf)
 
bool skcms_TransferFunction_isHLGish (const skcms_TransferFunction *tf)
 
bool skcms_TransferFunction_makePQish (skcms_TransferFunction *tf, float A, float B, float C, float D, float E, float F)
 
bool skcms_TransferFunction_makeScaledHLGish (skcms_TransferFunction *tf, float K, float R, float G, float a, float b, float c)
 
float skcms_TransferFunction_eval (const skcms_TransferFunction *tf, float x)
 
static float eval_curve (const skcms_Curve *curve, float x)
 
float skcms_MaxRoundtripError (const skcms_Curve *curve, const skcms_TransferFunction *inv_tf)
 
bool skcms_AreApproximateInverses (const skcms_Curve *curve, const skcms_TransferFunction *inv_tf)
 
static uint16_t read_big_u16 (const uint8_t *ptr)
 
static uint32_t read_big_u32 (const uint8_t *ptr)
 
static int32_t read_big_i32 (const uint8_t *ptr)
 
static float read_big_fixed (const uint8_t *ptr)
 
static const tag_Layoutget_tag_table (const skcms_ICCProfile *profile)
 
bool skcms_GetCHAD (const skcms_ICCProfile *profile, skcms_Matrix3x3 *m)
 
static bool read_tag_xyz (const skcms_ICCTag *tag, float *x, float *y, float *z)
 
bool skcms_GetWTPT (const skcms_ICCProfile *profile, float xyz[3])
 
static bool read_to_XYZD50 (const skcms_ICCTag *rXYZ, const skcms_ICCTag *gXYZ, const skcms_ICCTag *bXYZ, skcms_Matrix3x3 *toXYZ)
 
static bool read_curve_para (const uint8_t *buf, uint32_t size, skcms_Curve *curve, uint32_t *curve_size)
 
static bool read_curve_curv (const uint8_t *buf, uint32_t size, skcms_Curve *curve, uint32_t *curve_size)
 
static bool read_curve (const uint8_t *buf, uint32_t size, skcms_Curve *curve, uint32_t *curve_size)
 
static bool read_mft_common (const mft_CommonLayout *mftTag, skcms_A2B *a2b)
 
static bool read_mft_common (const mft_CommonLayout *mftTag, skcms_B2A *b2a)
 
template<typename A2B_or_B2A >
static bool init_tables (const uint8_t *table_base, uint64_t max_tables_len, uint32_t byte_width, uint32_t input_table_entries, uint32_t output_table_entries, A2B_or_B2A *out)
 
template<typename A2B_or_B2A >
static bool read_tag_mft1 (const skcms_ICCTag *tag, A2B_or_B2A *out)
 
template<typename A2B_or_B2A >
static bool read_tag_mft2 (const skcms_ICCTag *tag, A2B_or_B2A *out)
 
static bool read_curves (const uint8_t *buf, uint32_t size, uint32_t curve_offset, uint32_t num_curves, skcms_Curve *curves)
 
static bool read_tag_mab (const skcms_ICCTag *tag, skcms_A2B *a2b, bool pcs_is_xyz)
 
static bool read_tag_mba (const skcms_ICCTag *tag, skcms_B2A *b2a, bool pcs_is_xyz)
 
static int fit_linear (const skcms_Curve *curve, int N, float tol, float *c, float *d, float *f=nullptr)
 
static void canonicalize_identity (skcms_Curve *curve)
 
static bool read_a2b (const skcms_ICCTag *tag, skcms_A2B *a2b, bool pcs_is_xyz)
 
static bool read_b2a (const skcms_ICCTag *tag, skcms_B2A *b2a, bool pcs_is_xyz)
 
static bool read_cicp (const skcms_ICCTag *tag, skcms_CICP *cicp)
 
void skcms_GetTagByIndex (const skcms_ICCProfile *profile, uint32_t idx, skcms_ICCTag *tag)
 
bool skcms_GetTagBySignature (const skcms_ICCProfile *profile, uint32_t sig, skcms_ICCTag *tag)
 
static bool usable_as_src (const skcms_ICCProfile *profile)
 
bool skcms_ParseWithA2BPriority (const void *buf, size_t len, const int priority[], const int priorities, skcms_ICCProfile *profile)
 
const skcms_ICCProfileskcms_sRGB_profile ()
 
const skcms_ICCProfileskcms_XYZD50_profile ()
 
const skcms_TransferFunctionskcms_sRGB_TransferFunction ()
 
const skcms_TransferFunctionskcms_sRGB_Inverse_TransferFunction ()
 
const skcms_TransferFunctionskcms_Identity_TransferFunction ()
 
bool skcms_ApproximatelyEqualProfiles (const skcms_ICCProfile *A, const skcms_ICCProfile *B)
 
bool skcms_TRCs_AreApproximateInverse (const skcms_ICCProfile *profile, const skcms_TransferFunction *inv_tf)
 
static bool is_zero_to_one (float x)
 
static skcms_Vector3 mv_mul (const skcms_Matrix3x3 *m, const skcms_Vector3 *v)
 
bool skcms_AdaptToXYZD50 (float wx, float wy, skcms_Matrix3x3 *toXYZD50)
 
bool skcms_PrimariesToXYZD50 (float rx, float ry, float gx, float gy, float bx, float by, float wx, float wy, skcms_Matrix3x3 *toXYZD50)
 
bool skcms_Matrix3x3_invert (const skcms_Matrix3x3 *src, skcms_Matrix3x3 *dst)
 
skcms_Matrix3x3 skcms_Matrix3x3_concat (const skcms_Matrix3x3 *A, const skcms_Matrix3x3 *B)
 
bool skcms_TransferFunction_invert (const skcms_TransferFunction *src, skcms_TransferFunction *dst)
 
static float rg_nonlinear (float x, const skcms_Curve *curve, const skcms_TransferFunction *tf, float dfdP[3])
 
static bool gauss_newton_step (const skcms_Curve *curve, skcms_TransferFunction *tf, float x0, float dx, int N)
 
static float max_roundtrip_error_checked (const skcms_Curve *curve, const skcms_TransferFunction *tf_inv)
 
static bool fit_nonlinear (const skcms_Curve *curve, int L, int N, skcms_TransferFunction *tf)
 
bool skcms_ApproximateCurve (const skcms_Curve *curve, skcms_TransferFunction *approx, float *max_error)
 
static CpuType cpu_type ()
 
static bool tf_is_gamma (const skcms_TransferFunction &tf)
 
static OpAndArg select_curve_op (const skcms_Curve *curve, int channel)
 
static int select_curve_ops (const skcms_Curve *curves, int numChannels, OpAndArg *ops)
 
static size_t bytes_per_pixel (skcms_PixelFormat fmt)
 
static bool prep_for_destination (const skcms_ICCProfile *profile, skcms_Matrix3x3 *fromXYZD50, skcms_TransferFunction *invR, skcms_TransferFunction *invG, skcms_TransferFunction *invB)
 
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)
 
static void assert_usable_as_destination (const skcms_ICCProfile *profile)
 
bool skcms_MakeUsableAsDestination (skcms_ICCProfile *profile)
 
bool skcms_MakeUsableAsDestinationWithSingleCurve (skcms_ICCProfile *profile)
 

Variables

static bool sAllowRuntimeCPUDetection = true
 
const uint8_t skcms_252_random_bytes []
 

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
skcms_Signature_acsp 
skcms_Signature_rTRC 
skcms_Signature_gTRC 
skcms_Signature_bTRC 
skcms_Signature_kTRC 
skcms_Signature_rXYZ 
skcms_Signature_gXYZ 
skcms_Signature_bXYZ 
skcms_Signature_A2B0 
skcms_Signature_B2A0 
skcms_Signature_CHAD 
skcms_Signature_WTPT 
skcms_Signature_CICP 
skcms_Signature_curv 
skcms_Signature_mft1 
skcms_Signature_mft2 
skcms_Signature_mAB 
skcms_Signature_mBA 
skcms_Signature_para 
skcms_Signature_sf32 

Definition at line 290 of file skcms.cc.

290 {
291 // File signature
292 skcms_Signature_acsp = 0x61637370,
293
294 // Tag signatures
295 skcms_Signature_rTRC = 0x72545243,
296 skcms_Signature_gTRC = 0x67545243,
297 skcms_Signature_bTRC = 0x62545243,
298 skcms_Signature_kTRC = 0x6B545243,
299
300 skcms_Signature_rXYZ = 0x7258595A,
301 skcms_Signature_gXYZ = 0x6758595A,
302 skcms_Signature_bXYZ = 0x6258595A,
303
304 skcms_Signature_A2B0 = 0x41324230,
305 skcms_Signature_B2A0 = 0x42324130,
306
307 skcms_Signature_CHAD = 0x63686164,
308 skcms_Signature_WTPT = 0x77747074,
309
310 skcms_Signature_CICP = 0x63696370,
311
312 // Type signatures
313 skcms_Signature_curv = 0x63757276,
314 skcms_Signature_mft1 = 0x6D667431,
315 skcms_Signature_mft2 = 0x6D667432,
316 skcms_Signature_mAB = 0x6D414220,
317 skcms_Signature_mBA = 0x6D424120,
318 skcms_Signature_para = 0x70617261,
319 skcms_Signature_sf32 = 0x73663332,
320 // XYZ is also a PCS signature, so it's defined in skcms.h
321 // skcms_Signature_XYZ = 0x58595A20,
322};
@ skcms_Signature_B2A0
Definition skcms.cc:305
@ skcms_Signature_rTRC
Definition skcms.cc:295
@ skcms_Signature_gTRC
Definition skcms.cc:296
@ skcms_Signature_mAB
Definition skcms.cc:316
@ skcms_Signature_gXYZ
Definition skcms.cc:301
@ skcms_Signature_WTPT
Definition skcms.cc:308
@ skcms_Signature_curv
Definition skcms.cc:313
@ skcms_Signature_sf32
Definition skcms.cc:319
@ skcms_Signature_A2B0
Definition skcms.cc:304
@ skcms_Signature_mBA
Definition skcms.cc:317
@ skcms_Signature_mft2
Definition skcms.cc:315
@ skcms_Signature_mft1
Definition skcms.cc:314
@ skcms_Signature_kTRC
Definition skcms.cc:298
@ skcms_Signature_acsp
Definition skcms.cc:292
@ skcms_Signature_bTRC
Definition skcms.cc:297
@ skcms_Signature_CHAD
Definition skcms.cc:307
@ skcms_Signature_para
Definition skcms.cc:318
@ skcms_Signature_bXYZ
Definition skcms.cc:302
@ skcms_Signature_CICP
Definition skcms.cc:310
@ skcms_Signature_rXYZ
Definition skcms.cc:300

◆ CpuType

enum class CpuType
strong
Enumerator
Baseline 
HSW 
SKX 

Definition at line 2300 of file skcms.cc.

2300{ Baseline, HSW, SKX };

Function Documentation

◆ assert_usable_as_destination()

static void assert_usable_as_destination ( const skcms_ICCProfile profile)
static

Definition at line 2809 of file skcms.cc.

2809 {
2810#if defined(NDEBUG)
2811 (void)profile;
2812#else
2813 skcms_Matrix3x3 fromXYZD50;
2814 skcms_TransferFunction invR, invG, invB;
2815 assert(prep_for_destination(profile, &fromXYZD50, &invR, &invG, &invB));
2816#endif
2817}
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

◆ bytes_per_pixel()

static size_t bytes_per_pixel ( skcms_PixelFormat  fmt)
static

Definition at line 2449 of file skcms.cc.

2449 {
2450 switch (fmt >> 1) { // ignore rgb/bgr
2451 case skcms_PixelFormat_A_8 >> 1: return 1;
2452 case skcms_PixelFormat_G_8 >> 1: return 1;
2453 case skcms_PixelFormat_ABGR_4444 >> 1: return 2;
2454 case skcms_PixelFormat_RGB_565 >> 1: return 2;
2455 case skcms_PixelFormat_RGB_888 >> 1: return 3;
2456 case skcms_PixelFormat_RGBA_8888 >> 1: return 4;
2457 case skcms_PixelFormat_RGBA_8888_sRGB >> 1: return 4;
2458 case skcms_PixelFormat_RGBA_1010102 >> 1: return 4;
2459 case skcms_PixelFormat_RGB_101010x_XR >> 1: return 4;
2460 case skcms_PixelFormat_RGB_161616LE >> 1: return 6;
2461 case skcms_PixelFormat_RGBA_16161616LE >> 1: return 8;
2462 case skcms_PixelFormat_RGB_161616BE >> 1: return 6;
2463 case skcms_PixelFormat_RGBA_16161616BE >> 1: return 8;
2464 case skcms_PixelFormat_RGB_hhh_Norm >> 1: return 6;
2465 case skcms_PixelFormat_RGBA_hhhh_Norm >> 1: return 8;
2466 case skcms_PixelFormat_RGB_hhh >> 1: return 6;
2467 case skcms_PixelFormat_RGBA_hhhh >> 1: return 8;
2468 case skcms_PixelFormat_RGB_fff >> 1: return 12;
2469 case skcms_PixelFormat_RGBA_ffff >> 1: return 16;
2470 }
2471 assert(false);
2472 return 0;
2473}
static SkString fmt(SkColor4f c)
Definition p3.cpp:43
@ skcms_PixelFormat_RGBA_8888_sRGB
@ skcms_PixelFormat_RGBA_16161616BE
@ skcms_PixelFormat_RGB_161616LE
@ skcms_PixelFormat_RGBA_ffff
@ skcms_PixelFormat_RGBA_1010102
@ skcms_PixelFormat_RGBA_hhhh
@ skcms_PixelFormat_RGB_fff
@ skcms_PixelFormat_RGB_hhh_Norm
@ skcms_PixelFormat_RGBA_8888
@ skcms_PixelFormat_RGB_hhh
@ skcms_PixelFormat_RGB_888
@ skcms_PixelFormat_G_8
@ skcms_PixelFormat_ABGR_4444
@ skcms_PixelFormat_A_8
@ skcms_PixelFormat_RGB_565
@ skcms_PixelFormat_RGB_161616BE
@ skcms_PixelFormat_RGBA_hhhh_Norm
@ skcms_PixelFormat_RGB_101010x_XR
@ skcms_PixelFormat_RGBA_16161616LE

◆ canonicalize_identity()

static void canonicalize_identity ( skcms_Curve curve)
static

Definition at line 1122 of file skcms.cc.

1122 {
1123 if (curve->table_entries && curve->table_entries <= (uint32_t)INT_MAX) {
1124 int N = (int)curve->table_entries;
1125
1126 float c = 0.0f, d = 0.0f, f = 0.0f;
1127 if (N == fit_linear(curve, N, 1.0f/static_cast<float>(2*N), &c,&d,&f)
1128 && c == 1.0f
1129 && f == 0.0f) {
1130 curve->table_entries = 0;
1131 curve->table_8 = nullptr;
1132 curve->table_16 = nullptr;
1133 curve->parametric = skcms_TransferFunction{1,1,0,0,0,0,0};
1134 }
1135 }
1136}
Type::kYUV Type::kRGBA() int(0.7 *637)
#define N
Definition beziers.cpp:19
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition main.cc:19
static int fit_linear(const skcms_Curve *curve, int N, float tol, float *c, float *d, float *f=nullptr)
Definition skcms.cc:1069
const uint8_t * table_16
const uint8_t * table_8
skcms_TransferFunction parametric
uint32_t table_entries

◆ classify()

static skcms_TFType classify ( const skcms_TransferFunction tf,
TF_PQish pq = nullptr,
TF_HLGish hlg = nullptr 
)
static

Definition at line 135 of file skcms.cc.

136 {
137 if (tf.g < 0) {
138 // Negative "g" is mapped to enum values; large negative are for sure invalid.
139 if (tf.g < -128) {
141 }
142 int enum_g = -static_cast<int>(tf.g);
143 // Non-whole "g" values are invalid as well.
144 if (static_cast<float>(-enum_g) != tf.g) {
146 }
147 // TODO: soundness checks for PQ/HLG like we do for sRGBish?
148 switch (enum_g) {
150 if (pq) {
151 memcpy(pq , &tf.a, sizeof(*pq ));
152 }
153 return skcms_TFType_PQish;
155 if (hlg) {
156 memcpy(hlg, &tf.a, sizeof(*hlg));
157 }
158 return skcms_TFType_HLGish;
160 if (hlg) {
161 memcpy(hlg, &tf.a, sizeof(*hlg));
162 }
164 }
166 }
167
168 // Basic soundness checks for sRGBish transfer functions.
169 if (isfinitef_(tf.a + tf.b + tf.c + tf.d + tf.e + tf.f + tf.g)
170 // a,c,d,g should be non-negative to make any sense.
171 && tf.a >= 0
172 && tf.c >= 0
173 && tf.d >= 0
174 && tf.g >= 0
175 // Raising a negative value to a fractional tf->g produces complex numbers.
176 && tf.a * tf.d + tf.b >= 0) {
178 }
179
181}
static bool isfinitef_(float x)
Definition skcms.cc:111
@ skcms_TFType_Invalid
@ skcms_TFType_HLGish
@ skcms_TFType_sRGBish
@ skcms_TFType_HLGinvish
@ skcms_TFType_PQish

◆ cpu_type()

static CpuType cpu_type ( )
static

Definition at line 2302 of file skcms.cc.

2302 {
2303 #if defined(SKCMS_PORTABLE) || !defined(__x86_64__) || defined(SKCMS_FORCE_BASELINE)
2304 return CpuType::Baseline;
2305 #elif defined(SKCMS_FORCE_HSW)
2306 return CpuType::HSW;
2307 #elif defined(SKCMS_FORCE_SKX)
2308 return CpuType::SKX;
2309 #else
2310 static const CpuType type = []{
2312 return CpuType::Baseline;
2313 }
2314 // See http://www.sandpile.org/x86/cpuid.htm
2315
2316 // First, a basic cpuid(1) lets us check prerequisites for HSW, SKX.
2317 uint32_t eax, ebx, ecx, edx;
2318 __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
2319 : "0"(1), "2"(0));
2320 if ((edx & (1u<<25)) && // SSE
2321 (edx & (1u<<26)) && // SSE2
2322 (ecx & (1u<< 0)) && // SSE3
2323 (ecx & (1u<< 9)) && // SSSE3
2324 (ecx & (1u<<12)) && // FMA (N.B. not used, avoided even)
2325 (ecx & (1u<<19)) && // SSE4.1
2326 (ecx & (1u<<20)) && // SSE4.2
2327 (ecx & (1u<<26)) && // XSAVE
2328 (ecx & (1u<<27)) && // OSXSAVE
2329 (ecx & (1u<<28)) && // AVX
2330 (ecx & (1u<<29))) { // F16C
2331
2332 // Call cpuid(7) to check for AVX2 and AVX-512 bits.
2333 __asm__ __volatile__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
2334 : "0"(7), "2"(0));
2335 // eax from xgetbv(0) will tell us whether XMM, YMM, and ZMM state is saved.
2336 uint32_t xcr0, dont_need_edx;
2337 __asm__ __volatile__("xgetbv" : "=a"(xcr0), "=d"(dont_need_edx) : "c"(0));
2338
2339 if ((xcr0 & (1u<<1)) && // XMM register state saved?
2340 (xcr0 & (1u<<2)) && // YMM register state saved?
2341 (ebx & (1u<<5))) { // AVX2
2342 // At this point we're at least HSW. Continue checking for SKX.
2343 if ((xcr0 & (1u<< 5)) && // Opmasks state saved?
2344 (xcr0 & (1u<< 6)) && // First 16 ZMM registers saved?
2345 (xcr0 & (1u<< 7)) && // High 16 ZMM registers saved?
2346 (ebx & (1u<<16)) && // AVX512F
2347 (ebx & (1u<<17)) && // AVX512DQ
2348 (ebx & (1u<<28)) && // AVX512CD
2349 (ebx & (1u<<30)) && // AVX512BW
2350 (ebx & (1u<<31))) { // AVX512VL
2351 return CpuType::SKX;
2352 }
2353 return CpuType::HSW;
2354 }
2355 }
2356 return CpuType::Baseline;
2357 }();
2358 return type;
2359 #endif
2360}
__asm__(".symver expf,expf@GLIBC_2.4")
CpuType
Definition skcms.cc:2300
static bool sAllowRuntimeCPUDetection
Definition skcms.cc:38

◆ eval_curve()

static float eval_curve ( const skcms_Curve curve,
float  x 
)
static

Definition at line 247 of file skcms.cc.

247 {
248 if (curve->table_entries == 0) {
250 }
251
252 float ix = fmaxf_(0, fminf_(x, 1)) * static_cast<float>(curve->table_entries - 1);
253 int lo = (int) ix ,
254 hi = (int)(float)minus_1_ulp(ix + 1.0f);
255 float t = ix - (float)lo;
256
257 float l, h;
258 if (curve->table_8) {
259 l = curve->table_8[lo] * (1/255.0f);
260 h = curve->table_8[hi] * (1/255.0f);
261 } else {
262 uint16_t be_l, be_h;
263 memcpy(&be_l, curve->table_16 + 2*lo, 2);
264 memcpy(&be_h, curve->table_16 + 2*hi, 2);
265 uint16_t le_l = ((be_l << 8) | (be_l >> 8)) & 0xffff;
266 uint16_t le_h = ((be_h << 8) | (be_h >> 8)) & 0xffff;
267 l = le_l * (1/65535.0f);
268 h = le_h * (1/65535.0f);
269 }
270 return l + (h-l)*t;
271}
double x
SkScalar h
static float fminf_(float x, float y)
Definition skcms.cc:109
static float minus_1_ulp(float x)
Definition skcms.cc:113
float skcms_TransferFunction_eval(const skcms_TransferFunction *tf, float x)
Definition skcms.cc:212
static float fmaxf_(float x, float y)
Definition skcms.cc:108

◆ exp2f_()

static float exp2f_ ( float  x)
static

Definition at line 65 of file skcms.cc.

65 {
66 if (x > 128.0f) {
67 return INFINITY_;
68 } else if (x < -127.0f) {
69 return 0.0f;
70 }
71 float fract = x - floorf_(x);
72
73 float fbits = (1.0f * (1<<23)) * (x + 121.274057500f
74 - 1.490129070f*fract
75 + 27.728023300f/(4.84252568f - fract));
76
77 // Before we cast fbits to int32_t, check for out of range values to pacify UBSAN.
78 // INT_MAX is not exactly representable as a float, so exclude it as effectively infinite.
79 // Negative values are effectively underflow - we'll end up returning a (different) negative
80 // value, which makes no sense. So clamp to zero.
81 if (fbits >= (float)INT_MAX) {
82 return INFINITY_;
83 } else if (fbits < 0) {
84 return 0;
85 }
86
87 int32_t bits = (int32_t)fbits;
88 memcpy(&x, &bits, sizeof(x));
89 return x;
90}
SIN Vec< N, float > fract(const Vec< N, float > &x)
Definition SkVx.h:744
#define INFINITY_
static float floorf_(float x)

◆ expf_()

static float expf_ ( float  x)
static

Definition at line 103 of file skcms.cc.

103 {
104 const float log2_e = 1.4426950408889634074f;
105 return exp2f_(log2_e * x);
106}
static float exp2f_(float x)
Definition skcms.cc:65

◆ fit_linear()

static int fit_linear ( const skcms_Curve curve,
int  N,
float  tol,
float *  c,
float *  d,
float *  f = nullptr 
)
static

Definition at line 1069 of file skcms.cc.

1070 {
1071 assert(N > 1);
1072 // We iteratively fit the first points to the TF's linear piece.
1073 // We want the cx + f line to pass through the first and last points we fit exactly.
1074 //
1075 // As we walk along the points we find the minimum and maximum slope of the line before the
1076 // error would exceed our tolerance. We stop when the range [slope_min, slope_max] becomes
1077 // emtpy, when we definitely can't add any more points.
1078 //
1079 // Some points' error intervals may intersect the running interval but not lie fully
1080 // within it. So we keep track of the last point we saw that is a valid end point candidate,
1081 // and once the search is done, back up to build the line through *that* point.
1082 const float dx = 1.0f / static_cast<float>(N - 1);
1083
1084 int lin_points = 1;
1085
1086 float f_zero = 0.0f;
1087 if (f) {
1088 *f = eval_curve(curve, 0);
1089 } else {
1090 f = &f_zero;
1091 }
1092
1093
1094 float slope_min = -INFINITY_;
1095 float slope_max = +INFINITY_;
1096 for (int i = 1; i < N; ++i) {
1097 float x = static_cast<float>(i) * dx;
1098 float y = eval_curve(curve, x);
1099
1100 float slope_max_i = (y + tol - *f) / x,
1101 slope_min_i = (y - tol - *f) / x;
1102 if (slope_max_i < slope_min || slope_max < slope_min_i) {
1103 // Slope intervals would no longer overlap.
1104 break;
1105 }
1106 slope_max = fminf_(slope_max, slope_max_i);
1107 slope_min = fmaxf_(slope_min, slope_min_i);
1108
1109 float cur_slope = (y - *f) / x;
1110 if (slope_min <= cur_slope && cur_slope <= slope_max) {
1111 lin_points = i + 1;
1112 *c = cur_slope;
1113 }
1114 }
1115
1116 // Set D to the last point that met our tolerance.
1117 *d = static_cast<float>(lin_points - 1) * dx;
1118 return lin_points;
1119}
double y
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
Definition SkRecords.h:208
static float eval_curve(const skcms_Curve *curve, float x)
Definition skcms.cc:247

◆ fit_nonlinear()

static bool fit_nonlinear ( const skcms_Curve curve,
int  L,
int  N,
skcms_TransferFunction tf 
)
static

Definition at line 2140 of file skcms.cc.

2140 {
2141 // This enforces a few constraints that are not modeled in gauss_newton_step()'s optimization.
2142 auto fixup_tf = [tf]() {
2143 // a must be non-negative. That ensures the function is monotonically increasing.
2144 // We don't really know how to fix up a if it goes negative.
2145 if (tf->a < 0) {
2146 return false;
2147 }
2148 // ad+b must be non-negative. That ensures we don't end up with complex numbers in powf.
2149 // We feel just barely not uneasy enough to tweak b so ad+b is zero in this case.
2150 if (tf->a * tf->d + tf->b < 0) {
2151 tf->b = -tf->a * tf->d;
2152 }
2153 assert (tf->a >= 0 &&
2154 tf->a * tf->d + tf->b >= 0);
2155
2156 // cd+f must be ~= (ad+b)^g+e. That ensures the function is continuous. We keep e as a free
2157 // parameter so we can guarantee this.
2158 tf->e = tf->c*tf->d + tf->f
2159 - powf_(tf->a*tf->d + tf->b, tf->g);
2160
2161 return isfinitef_(tf->e);
2162 };
2163
2164 if (!fixup_tf()) {
2165 return false;
2166 }
2167
2168 // No matter where we start, dx should always represent N even steps from 0 to 1.
2169 const float dx = 1.0f / static_cast<float>(N-1);
2170
2171 skcms_TransferFunction best_tf = *tf;
2172 float best_max_error = INFINITY_;
2173
2174 // Need this or several curves get worse... *sigh*
2175 float init_error = max_roundtrip_error_checked(curve, tf);
2176 if (init_error < best_max_error) {
2177 best_max_error = init_error;
2178 best_tf = *tf;
2179 }
2180
2181 // As far as we can tell, 1 Gauss-Newton step won't converge, and 3 steps is no better than 2.
2182 for (int j = 0; j < 8; j++) {
2183 if (!gauss_newton_step(curve, tf, static_cast<float>(L)*dx, dx, N-L) || !fixup_tf()) {
2184 *tf = best_tf;
2185 return isfinitef_(best_max_error);
2186 }
2187
2188 float max_error = max_roundtrip_error_checked(curve, tf);
2189 if (max_error < best_max_error) {
2190 best_max_error = max_error;
2191 best_tf = *tf;
2192 }
2193 }
2194
2195 *tf = best_tf;
2196 return isfinitef_(best_max_error);
2197}
float powf_(float x, float y)
Definition skcms.cc:93
static float max_roundtrip_error_checked(const skcms_Curve *curve, const skcms_TransferFunction *tf_inv)
Definition skcms.cc:2124
static bool gauss_newton_step(const skcms_Curve *curve, skcms_TransferFunction *tf, float x0, float dx, int N)
Definition skcms.cc:2043

◆ fmaxf_()

static float fmaxf_ ( float  x,
float  y 
)
static

Definition at line 108 of file skcms.cc.

108{ return x > y ? x : y; }

◆ fminf_()

static float fminf_ ( float  x,
float  y 
)
static

Definition at line 109 of file skcms.cc.

109{ return x < y ? x : y; }

◆ gauss_newton_step()

static bool gauss_newton_step ( const skcms_Curve curve,
skcms_TransferFunction tf,
float  x0,
float  dx,
int  N 
)
static

Definition at line 2043 of file skcms.cc.

2045 {
2046 // We'll sample x from the range [x0,x1] (both inclusive) N times with even spacing.
2047 //
2048 // Let P = [ tf->g, tf->a, tf->b ] (the three terms that we're adjusting).
2049 //
2050 // We want to do P' = P + (Jf^T Jf)^-1 Jf^T r(P),
2051 // where r(P) is the residual vector
2052 // and Jf is the Jacobian matrix of f(), ∂r/∂P.
2053 //
2054 // Let's review the shape of each of these expressions:
2055 // r(P) is [N x 1], a column vector with one entry per value of x tested
2056 // Jf is [N x 3], a matrix with an entry for each (x,P) pair
2057 // Jf^T is [3 x N], the transpose of Jf
2058 //
2059 // Jf^T Jf is [3 x N] * [N x 3] == [3 x 3], a 3x3 matrix,
2060 // and so is its inverse (Jf^T Jf)^-1
2061 // Jf^T r(P) is [3 x N] * [N x 1] == [3 x 1], a column vector with the same shape as P
2062 //
2063 // Our implementation strategy to get to the final ∆P is
2064 // 1) evaluate Jf^T Jf, call that lhs
2065 // 2) evaluate Jf^T r(P), call that rhs
2066 // 3) invert lhs
2067 // 4) multiply inverse lhs by rhs
2068 //
2069 // This is a friendly implementation strategy because we don't have to have any
2070 // buffers that scale with N, and equally nice don't have to perform any matrix
2071 // operations that are variable size.
2072 //
2073 // Other implementation strategies could trade this off, e.g. evaluating the
2074 // pseudoinverse of Jf ( (Jf^T Jf)^-1 Jf^T ) directly, then multiplying that by
2075 // the residuals. That would probably require implementing singular value
2076 // decomposition, and would create a [3 x N] matrix to be multiplied by the
2077 // [N x 1] residual vector, but on the upside I think that'd eliminate the
2078 // possibility of this gauss_newton_step() function ever failing.
2079
2080 // 0) start off with lhs and rhs safely zeroed.
2081 skcms_Matrix3x3 lhs = {{ {0,0,0}, {0,0,0}, {0,0,0} }};
2082 skcms_Vector3 rhs = { {0,0,0} };
2083
2084 // 1,2) evaluate lhs and evaluate rhs
2085 // We want to evaluate Jf only once, but both lhs and rhs involve Jf^T,
2086 // so we'll have to update lhs and rhs at the same time.
2087 for (int i = 0; i < N; i++) {
2088 float x = x0 + static_cast<float>(i)*dx;
2089
2090 float dfdP[3] = {0,0,0};
2091 float resid = rg_nonlinear(x,curve,tf, dfdP);
2092
2093 for (int r = 0; r < 3; r++) {
2094 for (int c = 0; c < 3; c++) {
2095 lhs.vals[r][c] += dfdP[r] * dfdP[c];
2096 }
2097 rhs.vals[r] += dfdP[r] * resid;
2098 }
2099 }
2100
2101 // If any of the 3 P parameters are unused, this matrix will be singular.
2102 // Detect those cases and fix them up to indentity instead, so we can invert.
2103 for (int k = 0; k < 3; k++) {
2104 if (lhs.vals[0][k]==0 && lhs.vals[1][k]==0 && lhs.vals[2][k]==0 &&
2105 lhs.vals[k][0]==0 && lhs.vals[k][1]==0 && lhs.vals[k][2]==0) {
2106 lhs.vals[k][k] = 1;
2107 }
2108 }
2109
2110 // 3) invert lhs
2111 skcms_Matrix3x3 lhs_inv;
2112 if (!skcms_Matrix3x3_invert(&lhs, &lhs_inv)) {
2113 return false;
2114 }
2115
2116 // 4) multiply inverse lhs by rhs
2117 skcms_Vector3 dP = mv_mul(&lhs_inv, &rhs);
2118 tf->g += dP.vals[0];
2119 tf->a += dP.vals[1];
2120 tf->b += dP.vals[2];
2121 return isfinitef_(tf->g) && isfinitef_(tf->a) && isfinitef_(tf->b);
2122}
bool skcms_Matrix3x3_invert(const skcms_Matrix3x3 *src, skcms_Matrix3x3 *dst)
Definition skcms.cc:1792
static float rg_nonlinear(float x, const skcms_Curve *curve, const skcms_TransferFunction *tf, float dfdP[3])
Definition skcms.cc:2015
static skcms_Vector3 mv_mul(const skcms_Matrix3x3 *m, const skcms_Vector3 *v)
Definition skcms.cc:1696
float vals[3][3]
float vals[3]
Definition skcms.cc:1694

◆ get_tag_table()

static const tag_Layout * get_tag_table ( const skcms_ICCProfile profile)
static

Definition at line 384 of file skcms.cc.

384 {
385 return (const tag_Layout*)(profile->buffer + SAFE_SIZEOF(header_Layout));
386}
#define SAFE_SIZEOF(x)

◆ init_tables()

template<typename A2B_or_B2A >
static bool init_tables ( const uint8_t *  table_base,
uint64_t  max_tables_len,
uint32_t  byte_width,
uint32_t  input_table_entries,
uint32_t  output_table_entries,
A2B_or_B2A *  out 
)
static

Definition at line 674 of file skcms.cc.

676 {
677 // byte_width is 1 or 2, [input|output]_table_entries are in [2, 4096], so no overflow
678 uint32_t byte_len_per_input_table = input_table_entries * byte_width;
679 uint32_t byte_len_per_output_table = output_table_entries * byte_width;
680
681 // [input|output]_channels are <= 4, so still no overflow
682 uint32_t byte_len_all_input_tables = out->input_channels * byte_len_per_input_table;
683 uint32_t byte_len_all_output_tables = out->output_channels * byte_len_per_output_table;
684
685 uint64_t grid_size = out->output_channels * byte_width;
686 for (uint32_t axis = 0; axis < out->input_channels; ++axis) {
687 grid_size *= out->grid_points[axis];
688 }
689
690 if (max_tables_len < byte_len_all_input_tables + grid_size + byte_len_all_output_tables) {
691 return false;
692 }
693
694 for (uint32_t i = 0; i < out->input_channels; ++i) {
695 out->input_curves[i].table_entries = input_table_entries;
696 if (byte_width == 1) {
697 out->input_curves[i].table_8 = table_base + i * byte_len_per_input_table;
698 out->input_curves[i].table_16 = nullptr;
699 } else {
700 out->input_curves[i].table_8 = nullptr;
701 out->input_curves[i].table_16 = table_base + i * byte_len_per_input_table;
702 }
703 }
704
705 if (byte_width == 1) {
706 out->grid_8 = table_base + byte_len_all_input_tables;
707 out->grid_16 = nullptr;
708 } else {
709 out->grid_8 = nullptr;
710 out->grid_16 = table_base + byte_len_all_input_tables;
711 }
712
713 const uint8_t* output_table_base = table_base + byte_len_all_input_tables + grid_size;
714 for (uint32_t i = 0; i < out->output_channels; ++i) {
715 out->output_curves[i].table_entries = output_table_entries;
716 if (byte_width == 1) {
717 out->output_curves[i].table_8 = output_table_base + i * byte_len_per_output_table;
718 out->output_curves[i].table_16 = nullptr;
719 } else {
720 out->output_curves[i].table_8 = nullptr;
721 out->output_curves[i].table_16 = output_table_base + i * byte_len_per_output_table;
722 }
723 }
724
725 return true;
726}

◆ is_zero_to_one()

static bool is_zero_to_one ( float  x)
static

Definition at line 1690 of file skcms.cc.

1690 {
1691 return 0 <= x && x <= 1;
1692}

◆ isfinitef_()

static bool isfinitef_ ( float  x)
static

Definition at line 111 of file skcms.cc.

111{ return 0 == x*0; }

◆ log2f_()

static float log2f_ ( float  x)
static

Definition at line 44 of file skcms.cc.

44 {
45 // The first approximation of log2(x) is its exponent 'e', minus 127.
46 int32_t bits;
47 memcpy(&bits, &x, sizeof(bits));
48
49 float e = (float)bits * (1.0f / (1<<23));
50
51 // If we use the mantissa too we can refine the error signficantly.
52 int32_t m_bits = (bits & 0x007fffff) | 0x3f000000;
53 float m;
54 memcpy(&m, &m_bits, sizeof(m));
55
56 return (e - 124.225514990f
57 - 1.498030302f*m
58 - 1.725879990f/(0.3520887068f + m));
59}

◆ logf_()

static float logf_ ( float  x)
static

Definition at line 60 of file skcms.cc.

60 {
61 const float ln2 = 0.69314718f;
62 return ln2*log2f_(x);
63}
static float log2f_(float x)
Definition skcms.cc:44

◆ max_roundtrip_error_checked()

static float max_roundtrip_error_checked ( const skcms_Curve curve,
const skcms_TransferFunction tf_inv 
)
static

Definition at line 2124 of file skcms.cc.

2125 {
2127 if (!skcms_TransferFunction_invert(tf_inv, &tf) || skcms_TFType_sRGBish != classify(tf)) {
2128 return INFINITY_;
2129 }
2130
2131 skcms_TransferFunction tf_inv_again;
2132 if (!skcms_TransferFunction_invert(&tf, &tf_inv_again)) {
2133 return INFINITY_;
2134 }
2135
2136 return skcms_MaxRoundtripError(curve, &tf_inv_again);
2137}
static skcms_TFType classify(const skcms_TransferFunction &tf, TF_PQish *pq=nullptr, TF_HLGish *hlg=nullptr)
Definition skcms.cc:135
bool skcms_TransferFunction_invert(const skcms_TransferFunction *src, skcms_TransferFunction *dst)
Definition skcms.cc:1863
float skcms_MaxRoundtripError(const skcms_Curve *curve, const skcms_TransferFunction *inv_tf)
Definition skcms.cc:273

◆ minus_1_ulp()

static float minus_1_ulp ( float  x)
static

Definition at line 113 of file skcms.cc.

113 {
114 int32_t bits;
115 memcpy(&bits, &x, sizeof(bits));
116 bits = bits - 1;
117 memcpy(&x, &bits, sizeof(bits));
118 return x;
119}

◆ mv_mul()

static skcms_Vector3 mv_mul ( const skcms_Matrix3x3 m,
const skcms_Vector3 v 
)
static

Definition at line 1696 of file skcms.cc.

1696 {
1697 skcms_Vector3 dst = {{0,0,0}};
1698 for (int row = 0; row < 3; ++row) {
1699 dst.vals[row] = m->vals[row][0] * v->vals[0]
1700 + m->vals[row][1] * v->vals[1]
1701 + m->vals[row][2] * v->vals[2];
1702 }
1703 return dst;
1704}
dst
Definition cp.py:12

◆ powf_()

float powf_ ( float  x,
float  y 
)

Definition at line 93 of file skcms.cc.

93 {
94 if (x <= 0.f) {
95 return 0.f;
96 }
97 if (x == 1.f) {
98 return 1.f;
99 }
100 return exp2f_(log2f_(x) * y);
101}

◆ prep_for_destination()

static bool prep_for_destination ( const skcms_ICCProfile profile,
skcms_Matrix3x3 fromXYZD50,
skcms_TransferFunction invR,
skcms_TransferFunction invG,
skcms_TransferFunction invB 
)
static

Definition at line 2475 of file skcms.cc.

2479 {
2480 // skcms_Transform() supports B2A destinations...
2481 if (profile->has_B2A) { return true; }
2482 // ...and destinations with parametric transfer functions and an XYZD50 gamut matrix.
2483 return profile->has_trc
2484 && profile->has_toXYZD50
2485 && profile->trc[0].table_entries == 0
2486 && profile->trc[1].table_entries == 0
2487 && profile->trc[2].table_entries == 0
2488 && skcms_TransferFunction_invert(&profile->trc[0].parametric, invR)
2489 && skcms_TransferFunction_invert(&profile->trc[1].parametric, invG)
2490 && skcms_TransferFunction_invert(&profile->trc[2].parametric, invB)
2491 && skcms_Matrix3x3_invert(&profile->toXYZD50, fromXYZD50);
2492}

◆ read_a2b()

static bool read_a2b ( const skcms_ICCTag tag,
skcms_A2B a2b,
bool  pcs_is_xyz 
)
static

Definition at line 1138 of file skcms.cc.

1138 {
1139 bool ok = false;
1140 if (tag->type == skcms_Signature_mft1) { ok = read_tag_mft1(tag, a2b); }
1141 if (tag->type == skcms_Signature_mft2) { ok = read_tag_mft2(tag, a2b); }
1142 if (tag->type == skcms_Signature_mAB ) { ok = read_tag_mab(tag, a2b, pcs_is_xyz); }
1143 if (!ok) {
1144 return false;
1145 }
1146
1147 if (a2b->input_channels > 0) { canonicalize_identity(a2b->input_curves + 0); }
1148 if (a2b->input_channels > 1) { canonicalize_identity(a2b->input_curves + 1); }
1149 if (a2b->input_channels > 2) { canonicalize_identity(a2b->input_curves + 2); }
1150 if (a2b->input_channels > 3) { canonicalize_identity(a2b->input_curves + 3); }
1151
1152 if (a2b->matrix_channels > 0) { canonicalize_identity(a2b->matrix_curves + 0); }
1153 if (a2b->matrix_channels > 1) { canonicalize_identity(a2b->matrix_curves + 1); }
1154 if (a2b->matrix_channels > 2) { canonicalize_identity(a2b->matrix_curves + 2); }
1155
1156 if (a2b->output_channels > 0) { canonicalize_identity(a2b->output_curves + 0); }
1157 if (a2b->output_channels > 1) { canonicalize_identity(a2b->output_curves + 1); }
1158 if (a2b->output_channels > 2) { canonicalize_identity(a2b->output_curves + 2); }
1159
1160 return true;
1161}
static bool ok(int result)
static bool read_tag_mft1(const skcms_ICCTag *tag, A2B_or_B2A *out)
Definition skcms.cc:729
static bool read_tag_mft2(const skcms_ICCTag *tag, A2B_or_B2A *out)
Definition skcms.cc:747
static bool read_tag_mab(const skcms_ICCTag *tag, skcms_A2B *a2b, bool pcs_is_xyz)
Definition skcms.cc:818
static void canonicalize_identity(skcms_Curve *curve)
Definition skcms.cc:1122
uint32_t matrix_channels
skcms_Curve matrix_curves[3]
skcms_Curve output_curves[3]
uint32_t output_channels
uint32_t input_channels
skcms_Curve input_curves[4]

◆ read_b2a()

static bool read_b2a ( const skcms_ICCTag tag,
skcms_B2A b2a,
bool  pcs_is_xyz 
)
static

Definition at line 1163 of file skcms.cc.

1163 {
1164 bool ok = false;
1165 if (tag->type == skcms_Signature_mft1) { ok = read_tag_mft1(tag, b2a); }
1166 if (tag->type == skcms_Signature_mft2) { ok = read_tag_mft2(tag, b2a); }
1167 if (tag->type == skcms_Signature_mBA ) { ok = read_tag_mba(tag, b2a, pcs_is_xyz); }
1168 if (!ok) {
1169 return false;
1170 }
1171
1172 if (b2a->input_channels > 0) { canonicalize_identity(b2a->input_curves + 0); }
1173 if (b2a->input_channels > 1) { canonicalize_identity(b2a->input_curves + 1); }
1174 if (b2a->input_channels > 2) { canonicalize_identity(b2a->input_curves + 2); }
1175
1176 if (b2a->matrix_channels > 0) { canonicalize_identity(b2a->matrix_curves + 0); }
1177 if (b2a->matrix_channels > 1) { canonicalize_identity(b2a->matrix_curves + 1); }
1178 if (b2a->matrix_channels > 2) { canonicalize_identity(b2a->matrix_curves + 2); }
1179
1180 if (b2a->output_channels > 0) { canonicalize_identity(b2a->output_curves + 0); }
1181 if (b2a->output_channels > 1) { canonicalize_identity(b2a->output_curves + 1); }
1182 if (b2a->output_channels > 2) { canonicalize_identity(b2a->output_curves + 2); }
1183 if (b2a->output_channels > 3) { canonicalize_identity(b2a->output_curves + 3); }
1184
1185 return true;
1186}
static bool read_tag_mba(const skcms_ICCTag *tag, skcms_B2A *b2a, bool pcs_is_xyz)
Definition skcms.cc:945
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

◆ read_big_fixed()

static float read_big_fixed ( const uint8_t *  ptr)
static

Definition at line 348 of file skcms.cc.

348 {
349 return static_cast<float>(read_big_i32(ptr)) * (1.0f / 65536.0f);
350}
static int32_t read_big_i32(const uint8_t *ptr)
Definition skcms.cc:344

◆ read_big_i32()

static int32_t read_big_i32 ( const uint8_t *  ptr)
static

Definition at line 344 of file skcms.cc.

344 {
345 return (int32_t)read_big_u32(ptr);
346}
static uint32_t read_big_u32(const uint8_t *ptr)
Definition skcms.cc:334

◆ read_big_u16()

static uint16_t read_big_u16 ( const uint8_t *  ptr)
static

Definition at line 324 of file skcms.cc.

324 {
325 uint16_t be;
326 memcpy(&be, ptr, sizeof(be));
327#if defined(_MSC_VER)
328 return _byteswap_ushort(be);
329#else
330 return __builtin_bswap16(be);
331#endif
332}

◆ read_big_u32()

static uint32_t read_big_u32 ( const uint8_t *  ptr)
static

Definition at line 334 of file skcms.cc.

334 {
335 uint32_t be;
336 memcpy(&be, ptr, sizeof(be));
337#if defined(_MSC_VER)
338 return _byteswap_ulong(be);
339#else
340 return __builtin_bswap32(be);
341#endif
342}

◆ read_cicp()

static bool read_cicp ( const skcms_ICCTag tag,
skcms_CICP cicp 
)
static

Definition at line 1197 of file skcms.cc.

1197 {
1198 if (tag->type != skcms_Signature_CICP || tag->size < SAFE_SIZEOF(CICP_Layout)) {
1199 return false;
1200 }
1201
1202 const CICP_Layout* cicpTag = (const CICP_Layout*)tag->buf;
1203
1204 cicp->color_primaries = cicpTag->color_primaries[0];
1205 cicp->transfer_characteristics = cicpTag->transfer_characteristics[0];
1206 cicp->matrix_coefficients = cicpTag->matrix_coefficients[0];
1207 cicp->video_full_range_flag = cicpTag->video_full_range_flag[0];
1208 return true;
1209}
uint8_t color_primaries
uint8_t matrix_coefficients
uint8_t video_full_range_flag
uint8_t transfer_characteristics
const uint8_t * buf

◆ read_curve()

static bool read_curve ( const uint8_t *  buf,
uint32_t  size,
skcms_Curve curve,
uint32_t *  curve_size 
)
static

Definition at line 578 of file skcms.cc.

579 {
580 if (!buf || size < 4 || !curve) {
581 return false;
582 }
583
584 uint32_t type = read_big_u32(buf);
585 if (type == skcms_Signature_para) {
586 return read_curve_para(buf, size, curve, curve_size);
587 } else if (type == skcms_Signature_curv) {
588 return read_curve_curv(buf, size, curve, curve_size);
589 }
590
591 return false;
592}
static bool read_curve_para(const uint8_t *buf, uint32_t size, skcms_Curve *curve, uint32_t *curve_size)
Definition skcms.cc:459
static bool read_curve_curv(const uint8_t *buf, uint32_t size, skcms_Curve *curve, uint32_t *curve_size)
Definition skcms.cc:535

◆ read_curve_curv()

static bool read_curve_curv ( const uint8_t *  buf,
uint32_t  size,
skcms_Curve curve,
uint32_t *  curve_size 
)
static

Definition at line 535 of file skcms.cc.

536 {
537 if (size < SAFE_FIXED_SIZE(curv_Layout)) {
538 return false;
539 }
540
541 const curv_Layout* curvTag = (const curv_Layout*)buf;
542
543 uint32_t value_count = read_big_u32(curvTag->value_count);
544 if (size < SAFE_FIXED_SIZE(curv_Layout) + value_count * SAFE_SIZEOF(uint16_t)) {
545 return false;
546 }
547
548 if (curve_size) {
549 *curve_size = SAFE_FIXED_SIZE(curv_Layout) + value_count * SAFE_SIZEOF(uint16_t);
550 }
551
552 if (value_count < 2) {
553 curve->table_entries = 0;
554 curve->parametric.a = 1.0f;
555 curve->parametric.b = 0.0f;
556 curve->parametric.c = 0.0f;
557 curve->parametric.d = 0.0f;
558 curve->parametric.e = 0.0f;
559 curve->parametric.f = 0.0f;
560 if (value_count == 0) {
561 // Empty tables are a shorthand for an identity curve
562 curve->parametric.g = 1.0f;
563 } else {
564 // Single entry tables are a shorthand for simple gamma
565 curve->parametric.g = read_big_u16(curvTag->variable) * (1.0f / 256.0f);
566 }
567 } else {
568 curve->table_8 = nullptr;
569 curve->table_16 = curvTag->variable;
570 curve->table_entries = value_count;
571 }
572
573 return true;
574}
static uint16_t read_big_u16(const uint8_t *ptr)
Definition skcms.cc:324
#define SAFE_FIXED_SIZE(type)
uint8_t variable[1]
Definition skcms.cc:532
uint8_t value_count[4]
Definition skcms.cc:531

◆ read_curve_para()

static bool read_curve_para ( const uint8_t *  buf,
uint32_t  size,
skcms_Curve curve,
uint32_t *  curve_size 
)
static

Definition at line 459 of file skcms.cc.

460 {
461 if (size < SAFE_FIXED_SIZE(para_Layout)) {
462 return false;
463 }
464
465 const para_Layout* paraTag = (const para_Layout*)buf;
466
467 enum { kG = 0, kGAB = 1, kGABC = 2, kGABCD = 3, kGABCDEF = 4 };
468 uint16_t function_type = read_big_u16(paraTag->function_type);
469 if (function_type > kGABCDEF) {
470 return false;
471 }
472
473 static const uint32_t curve_bytes[] = { 4, 12, 16, 20, 28 };
474 if (size < SAFE_FIXED_SIZE(para_Layout) + curve_bytes[function_type]) {
475 return false;
476 }
477
478 if (curve_size) {
479 *curve_size = SAFE_FIXED_SIZE(para_Layout) + curve_bytes[function_type];
480 }
481
482 curve->table_entries = 0;
483 curve->parametric.a = 1.0f;
484 curve->parametric.b = 0.0f;
485 curve->parametric.c = 0.0f;
486 curve->parametric.d = 0.0f;
487 curve->parametric.e = 0.0f;
488 curve->parametric.f = 0.0f;
489 curve->parametric.g = read_big_fixed(paraTag->variable);
490
491 switch (function_type) {
492 case kGAB:
493 curve->parametric.a = read_big_fixed(paraTag->variable + 4);
494 curve->parametric.b = read_big_fixed(paraTag->variable + 8);
495 if (curve->parametric.a == 0) {
496 return false;
497 }
498 curve->parametric.d = -curve->parametric.b / curve->parametric.a;
499 break;
500 case kGABC:
501 curve->parametric.a = read_big_fixed(paraTag->variable + 4);
502 curve->parametric.b = read_big_fixed(paraTag->variable + 8);
503 curve->parametric.e = read_big_fixed(paraTag->variable + 12);
504 if (curve->parametric.a == 0) {
505 return false;
506 }
507 curve->parametric.d = -curve->parametric.b / curve->parametric.a;
508 curve->parametric.f = curve->parametric.e;
509 break;
510 case kGABCD:
511 curve->parametric.a = read_big_fixed(paraTag->variable + 4);
512 curve->parametric.b = read_big_fixed(paraTag->variable + 8);
513 curve->parametric.c = read_big_fixed(paraTag->variable + 12);
514 curve->parametric.d = read_big_fixed(paraTag->variable + 16);
515 break;
516 case kGABCDEF:
517 curve->parametric.a = read_big_fixed(paraTag->variable + 4);
518 curve->parametric.b = read_big_fixed(paraTag->variable + 8);
519 curve->parametric.c = read_big_fixed(paraTag->variable + 12);
520 curve->parametric.d = read_big_fixed(paraTag->variable + 16);
521 curve->parametric.e = read_big_fixed(paraTag->variable + 20);
522 curve->parametric.f = read_big_fixed(paraTag->variable + 24);
523 break;
524 }
526}
bool skcms_TransferFunction_isSRGBish(const skcms_TransferFunction *tf)
Definition skcms.cc:186
static float read_big_fixed(const uint8_t *ptr)
Definition skcms.cc:348
uint8_t variable[1]
Definition skcms.cc:456
uint8_t function_type[2]
Definition skcms.cc:454

◆ read_curves()

static bool read_curves ( const uint8_t *  buf,
uint32_t  size,
uint32_t  curve_offset,
uint32_t  num_curves,
skcms_Curve curves 
)
static

Definition at line 770 of file skcms.cc.

771 {
772 for (uint32_t i = 0; i < num_curves; ++i) {
773 if (curve_offset > size) {
774 return false;
775 }
776
777 uint32_t curve_bytes;
778 if (!read_curve(buf + curve_offset, size - curve_offset, &curves[i], &curve_bytes)) {
779 return false;
780 }
781
782 if (curve_bytes > UINT32_MAX - 3) {
783 return false;
784 }
785 curve_bytes = (curve_bytes + 3) & ~3U;
786
787 uint64_t new_offset_64 = (uint64_t)curve_offset + curve_bytes;
788 curve_offset = (uint32_t)new_offset_64;
789 if (new_offset_64 != curve_offset) {
790 return false;
791 }
792 }
793
794 return true;
795}
static bool read_curve(const uint8_t *buf, uint32_t size, skcms_Curve *curve, uint32_t *curve_size)
Definition skcms.cc:578

◆ read_mft_common() [1/2]

static bool read_mft_common ( const mft_CommonLayout mftTag,
skcms_A2B a2b 
)
static

Definition at line 619 of file skcms.cc.

619 {
620 // MFT matrices are applied before the first set of curves, but must be identity unless the
621 // input is PCSXYZ. We don't support PCSXYZ profiles, so we ignore this matrix. Note that the
622 // matrix in skcms_A2B is applied later in the pipe, so supporting this would require another
623 // field/flag.
624 a2b->matrix_channels = 0;
625 a2b-> input_channels = mftTag-> input_channels[0];
626 a2b->output_channels = mftTag->output_channels[0];
627
628 // We require exactly three (ie XYZ/Lab/RGB) output channels
629 if (a2b->output_channels != ARRAY_COUNT(a2b->output_curves)) {
630 return false;
631 }
632 // We require at least one, and no more than four (ie CMYK) input channels
633 if (a2b->input_channels < 1 || a2b->input_channels > ARRAY_COUNT(a2b->input_curves)) {
634 return false;
635 }
636
637 for (uint32_t i = 0; i < a2b->input_channels; ++i) {
638 a2b->grid_points[i] = mftTag->grid_points[0];
639 }
640 // The grid only makes sense with at least two points along each axis
641 if (a2b->grid_points[0] < 2) {
642 return false;
643 }
644 return true;
645}
#define ARRAY_COUNT(arr)
uint8_t output_channels[1]
Definition skcms.cc:599
uint8_t grid_points[1]
Definition skcms.cc:600
uint8_t grid_points[4]

◆ read_mft_common() [2/2]

static bool read_mft_common ( const mft_CommonLayout mftTag,
skcms_B2A b2a 
)
static

Definition at line 648 of file skcms.cc.

648 {
649 // Same as A2B.
650 b2a->matrix_channels = 0;
651 b2a-> input_channels = mftTag-> input_channels[0];
652 b2a->output_channels = mftTag->output_channels[0];
653
654
655 // For B2A, exactly 3 input channels (XYZ) and 3 (RGB) or 4 (CMYK) output channels.
656 if (b2a->input_channels != ARRAY_COUNT(b2a->input_curves)) {
657 return false;
658 }
659 if (b2a->output_channels < 3 || b2a->output_channels > ARRAY_COUNT(b2a->output_curves)) {
660 return false;
661 }
662
663 // Same as A2B.
664 for (uint32_t i = 0; i < b2a->input_channels; ++i) {
665 b2a->grid_points[i] = mftTag->grid_points[0];
666 }
667 if (b2a->grid_points[0] < 2) {
668 return false;
669 }
670 return true;
671}
uint8_t grid_points[4]

◆ read_tag_mab()

static bool read_tag_mab ( const skcms_ICCTag tag,
skcms_A2B a2b,
bool  pcs_is_xyz 
)
static

Definition at line 818 of file skcms.cc.

818 {
819 if (tag->size < SAFE_SIZEOF(mAB_or_mBA_Layout)) {
820 return false;
821 }
822
823 const mAB_or_mBA_Layout* mABTag = (const mAB_or_mBA_Layout*)tag->buf;
824
825 a2b->input_channels = mABTag->input_channels[0];
826 a2b->output_channels = mABTag->output_channels[0];
827
828 // We require exactly three (ie XYZ/Lab/RGB) output channels
829 if (a2b->output_channels != ARRAY_COUNT(a2b->output_curves)) {
830 return false;
831 }
832 // We require no more than four (ie CMYK) input channels
833 if (a2b->input_channels > ARRAY_COUNT(a2b->input_curves)) {
834 return false;
835 }
836
837 uint32_t b_curve_offset = read_big_u32(mABTag->b_curve_offset);
838 uint32_t matrix_offset = read_big_u32(mABTag->matrix_offset);
839 uint32_t m_curve_offset = read_big_u32(mABTag->m_curve_offset);
840 uint32_t clut_offset = read_big_u32(mABTag->clut_offset);
841 uint32_t a_curve_offset = read_big_u32(mABTag->a_curve_offset);
842
843 // "B" curves must be present
844 if (0 == b_curve_offset) {
845 return false;
846 }
847
848 if (!read_curves(tag->buf, tag->size, b_curve_offset, a2b->output_channels,
849 a2b->output_curves)) {
850 return false;
851 }
852
853 // "M" curves and Matrix must be used together
854 if (0 != m_curve_offset) {
855 if (0 == matrix_offset) {
856 return false;
857 }
859 if (!read_curves(tag->buf, tag->size, m_curve_offset, a2b->matrix_channels,
860 a2b->matrix_curves)) {
861 return false;
862 }
863
864 // Read matrix, which is stored as a row-major 3x3, followed by the fourth column
865 if (tag->size < matrix_offset + 12 * SAFE_SIZEOF(uint32_t)) {
866 return false;
867 }
868 float encoding_factor = pcs_is_xyz ? (65535 / 32768.0f) : 1.0f;
869 const uint8_t* mtx_buf = tag->buf + matrix_offset;
870 a2b->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf + 0);
871 a2b->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf + 4);
872 a2b->matrix.vals[0][2] = encoding_factor * read_big_fixed(mtx_buf + 8);
873 a2b->matrix.vals[1][0] = encoding_factor * read_big_fixed(mtx_buf + 12);
874 a2b->matrix.vals[1][1] = encoding_factor * read_big_fixed(mtx_buf + 16);
875 a2b->matrix.vals[1][2] = encoding_factor * read_big_fixed(mtx_buf + 20);
876 a2b->matrix.vals[2][0] = encoding_factor * read_big_fixed(mtx_buf + 24);
877 a2b->matrix.vals[2][1] = encoding_factor * read_big_fixed(mtx_buf + 28);
878 a2b->matrix.vals[2][2] = encoding_factor * read_big_fixed(mtx_buf + 32);
879 a2b->matrix.vals[0][3] = encoding_factor * read_big_fixed(mtx_buf + 36);
880 a2b->matrix.vals[1][3] = encoding_factor * read_big_fixed(mtx_buf + 40);
881 a2b->matrix.vals[2][3] = encoding_factor * read_big_fixed(mtx_buf + 44);
882 } else {
883 if (0 != matrix_offset) {
884 return false;
885 }
886 a2b->matrix_channels = 0;
887 }
888
889 // "A" curves and CLUT must be used together
890 if (0 != a_curve_offset) {
891 if (0 == clut_offset) {
892 return false;
893 }
894 if (!read_curves(tag->buf, tag->size, a_curve_offset, a2b->input_channels,
895 a2b->input_curves)) {
896 return false;
897 }
898
899 if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout)) {
900 return false;
901 }
902 const CLUT_Layout* clut = (const CLUT_Layout*)(tag->buf + clut_offset);
903
904 if (clut->grid_byte_width[0] == 1) {
905 a2b->grid_8 = clut->variable;
906 a2b->grid_16 = nullptr;
907 } else if (clut->grid_byte_width[0] == 2) {
908 a2b->grid_8 = nullptr;
909 a2b->grid_16 = clut->variable;
910 } else {
911 return false;
912 }
913
914 uint64_t grid_size = a2b->output_channels * clut->grid_byte_width[0]; // the payload
915 for (uint32_t i = 0; i < a2b->input_channels; ++i) {
916 a2b->grid_points[i] = clut->grid_points[i];
917 // The grid only makes sense with at least two points along each axis
918 if (a2b->grid_points[i] < 2) {
919 return false;
920 }
921 grid_size *= a2b->grid_points[i];
922 }
923 if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout) + grid_size) {
924 return false;
925 }
926 } else {
927 if (0 != clut_offset) {
928 return false;
929 }
930
931 // If there is no CLUT, the number of input and output channels must match
932 if (a2b->input_channels != a2b->output_channels) {
933 return false;
934 }
935
936 // Zero out the number of input channels to signal that we're skipping this stage
937 a2b->input_channels = 0;
938 }
939
940 return true;
941}
static void clut(uint32_t input_channels, uint32_t output_channels, const uint8_t grid_points[4], const uint8_t *grid_8, const uint8_t *grid_16, F *r, F *g, F *b, F *a)
static bool read_curves(const uint8_t *buf, uint32_t size, uint32_t curve_offset, uint32_t num_curves, skcms_Curve *curves)
Definition skcms.cc:770
const uint8_t * grid_8
skcms_Matrix3x4 matrix
const uint8_t * grid_16
float vals[3][4]

◆ read_tag_mba()

static bool read_tag_mba ( const skcms_ICCTag tag,
skcms_B2A b2a,
bool  pcs_is_xyz 
)
static

Definition at line 945 of file skcms.cc.

945 {
946 if (tag->size < SAFE_SIZEOF(mAB_or_mBA_Layout)) {
947 return false;
948 }
949
950 const mAB_or_mBA_Layout* mBATag = (const mAB_or_mBA_Layout*)tag->buf;
951
952 b2a->input_channels = mBATag->input_channels[0];
953 b2a->output_channels = mBATag->output_channels[0];
954
955 // Require exactly 3 inputs (XYZ) and 3 (RGB) or 4 (CMYK) outputs.
956 if (b2a->input_channels != ARRAY_COUNT(b2a->input_curves)) {
957 return false;
958 }
959 if (b2a->output_channels < 3 || b2a->output_channels > ARRAY_COUNT(b2a->output_curves)) {
960 return false;
961 }
962
963 uint32_t b_curve_offset = read_big_u32(mBATag->b_curve_offset);
964 uint32_t matrix_offset = read_big_u32(mBATag->matrix_offset);
965 uint32_t m_curve_offset = read_big_u32(mBATag->m_curve_offset);
966 uint32_t clut_offset = read_big_u32(mBATag->clut_offset);
967 uint32_t a_curve_offset = read_big_u32(mBATag->a_curve_offset);
968
969 if (0 == b_curve_offset) {
970 return false;
971 }
972
973 // "B" curves are our inputs, not outputs.
974 if (!read_curves(tag->buf, tag->size, b_curve_offset, b2a->input_channels,
975 b2a->input_curves)) {
976 return false;
977 }
978
979 if (0 != m_curve_offset) {
980 if (0 == matrix_offset) {
981 return false;
982 }
983 // Matrix channels is tied to input_channels (3), not output_channels.
985
986 if (!read_curves(tag->buf, tag->size, m_curve_offset, b2a->matrix_channels,
987 b2a->matrix_curves)) {
988 return false;
989 }
990
991 if (tag->size < matrix_offset + 12 * SAFE_SIZEOF(uint32_t)) {
992 return false;
993 }
994 float encoding_factor = pcs_is_xyz ? (32768 / 65535.0f) : 1.0f; // TODO: understand
995 const uint8_t* mtx_buf = tag->buf + matrix_offset;
996 b2a->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf + 0);
997 b2a->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf + 4);
998 b2a->matrix.vals[0][2] = encoding_factor * read_big_fixed(mtx_buf + 8);
999 b2a->matrix.vals[1][0] = encoding_factor * read_big_fixed(mtx_buf + 12);
1000 b2a->matrix.vals[1][1] = encoding_factor * read_big_fixed(mtx_buf + 16);
1001 b2a->matrix.vals[1][2] = encoding_factor * read_big_fixed(mtx_buf + 20);
1002 b2a->matrix.vals[2][0] = encoding_factor * read_big_fixed(mtx_buf + 24);
1003 b2a->matrix.vals[2][1] = encoding_factor * read_big_fixed(mtx_buf + 28);
1004 b2a->matrix.vals[2][2] = encoding_factor * read_big_fixed(mtx_buf + 32);
1005 b2a->matrix.vals[0][3] = encoding_factor * read_big_fixed(mtx_buf + 36);
1006 b2a->matrix.vals[1][3] = encoding_factor * read_big_fixed(mtx_buf + 40);
1007 b2a->matrix.vals[2][3] = encoding_factor * read_big_fixed(mtx_buf + 44);
1008 } else {
1009 if (0 != matrix_offset) {
1010 return false;
1011 }
1012 b2a->matrix_channels = 0;
1013 }
1014
1015 if (0 != a_curve_offset) {
1016 if (0 == clut_offset) {
1017 return false;
1018 }
1019
1020 // "A" curves are our output, not input.
1021 if (!read_curves(tag->buf, tag->size, a_curve_offset, b2a->output_channels,
1022 b2a->output_curves)) {
1023 return false;
1024 }
1025
1026 if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout)) {
1027 return false;
1028 }
1029 const CLUT_Layout* clut = (const CLUT_Layout*)(tag->buf + clut_offset);
1030
1031 if (clut->grid_byte_width[0] == 1) {
1032 b2a->grid_8 = clut->variable;
1033 b2a->grid_16 = nullptr;
1034 } else if (clut->grid_byte_width[0] == 2) {
1035 b2a->grid_8 = nullptr;
1036 b2a->grid_16 = clut->variable;
1037 } else {
1038 return false;
1039 }
1040
1041 uint64_t grid_size = b2a->output_channels * clut->grid_byte_width[0];
1042 for (uint32_t i = 0; i < b2a->input_channels; ++i) {
1043 b2a->grid_points[i] = clut->grid_points[i];
1044 if (b2a->grid_points[i] < 2) {
1045 return false;
1046 }
1047 grid_size *= b2a->grid_points[i];
1048 }
1049 if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout) + grid_size) {
1050 return false;
1051 }
1052 } else {
1053 if (0 != clut_offset) {
1054 return false;
1055 }
1056
1057 if (b2a->input_channels != b2a->output_channels) {
1058 return false;
1059 }
1060
1061 // Zero out *output* channels to skip this stage.
1062 b2a->output_channels = 0;
1063 }
1064 return true;
1065}
skcms_Matrix3x4 matrix
const uint8_t * grid_16
const uint8_t * grid_8

◆ read_tag_mft1()

template<typename A2B_or_B2A >
static bool read_tag_mft1 ( const skcms_ICCTag tag,
A2B_or_B2A *  out 
)
static

Definition at line 729 of file skcms.cc.

729 {
730 if (tag->size < SAFE_FIXED_SIZE(mft1_Layout)) {
731 return false;
732 }
733
734 const mft1_Layout* mftTag = (const mft1_Layout*)tag->buf;
735 if (!read_mft_common(mftTag->common, out)) {
736 return false;
737 }
738
739 uint32_t input_table_entries = 256;
740 uint32_t output_table_entries = 256;
741
742 return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft1_Layout), 1,
743 input_table_entries, output_table_entries, out);
744}
static bool read_mft_common(const mft_CommonLayout *mftTag, skcms_A2B *a2b)
Definition skcms.cc:619
static bool init_tables(const uint8_t *table_base, uint64_t max_tables_len, uint32_t byte_width, uint32_t input_table_entries, uint32_t output_table_entries, A2B_or_B2A *out)
Definition skcms.cc:674

◆ read_tag_mft2()

template<typename A2B_or_B2A >
static bool read_tag_mft2 ( const skcms_ICCTag tag,
A2B_or_B2A *  out 
)
static

Definition at line 747 of file skcms.cc.

747 {
748 if (tag->size < SAFE_FIXED_SIZE(mft2_Layout)) {
749 return false;
750 }
751
752 const mft2_Layout* mftTag = (const mft2_Layout*)tag->buf;
753 if (!read_mft_common(mftTag->common, out)) {
754 return false;
755 }
756
757 uint32_t input_table_entries = read_big_u16(mftTag->input_table_entries);
758 uint32_t output_table_entries = read_big_u16(mftTag->output_table_entries);
759
760 // ICC spec mandates that 2 <= table_entries <= 4096
761 if (input_table_entries < 2 || input_table_entries > 4096 ||
762 output_table_entries < 2 || output_table_entries > 4096) {
763 return false;
764 }
765
766 return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft2_Layout), 2,
767 input_table_entries, output_table_entries, out);
768}

◆ read_tag_xyz()

static bool read_tag_xyz ( const skcms_ICCTag tag,
float *  x,
float *  y,
float *  z 
)
static

Definition at line 425 of file skcms.cc.

425 {
426 if (tag->type != skcms_Signature_XYZ || tag->size < SAFE_SIZEOF(XYZ_Layout)) {
427 return false;
428 }
429
430 const XYZ_Layout* xyzTag = (const XYZ_Layout*)tag->buf;
431
432 *x = read_big_fixed(xyzTag->X);
433 *y = read_big_fixed(xyzTag->Y);
434 *z = read_big_fixed(xyzTag->Z);
435 return true;
436}
@ skcms_Signature_XYZ

◆ read_to_XYZD50()

static bool read_to_XYZD50 ( const skcms_ICCTag rXYZ,
const skcms_ICCTag gXYZ,
const skcms_ICCTag bXYZ,
skcms_Matrix3x3 toXYZ 
)
static

Definition at line 444 of file skcms.cc.

445 {
446 return read_tag_xyz(rXYZ, &toXYZ->vals[0][0], &toXYZ->vals[1][0], &toXYZ->vals[2][0]) &&
447 read_tag_xyz(gXYZ, &toXYZ->vals[0][1], &toXYZ->vals[1][1], &toXYZ->vals[2][1]) &&
448 read_tag_xyz(bXYZ, &toXYZ->vals[0][2], &toXYZ->vals[1][2], &toXYZ->vals[2][2]);
449}
static bool read_tag_xyz(const skcms_ICCTag *tag, float *x, float *y, float *z)
Definition skcms.cc:425

◆ rg_nonlinear()

static float rg_nonlinear ( float  x,
const skcms_Curve curve,
const skcms_TransferFunction tf,
float  dfdP[3] 
)
static

Definition at line 2015 of file skcms.cc.

2018 {
2019 const float y = eval_curve(curve, x);
2020
2021 const float g = tf->g, a = tf->a, b = tf->b,
2022 c = tf->c, d = tf->d, f = tf->f;
2023
2024 const float Y = fmaxf_(a*y + b, 0.0f),
2025 D = a*d + b;
2026 assert (D >= 0);
2027
2028 // The gradient.
2029 dfdP[0] = logf_(Y)*powf_(Y, g)
2030 - logf_(D)*powf_(D, g);
2031 dfdP[1] = y*g*powf_(Y, g-1)
2032 - d*g*powf_(D, g-1);
2033 dfdP[2] = g*powf_(Y, g-1)
2034 - g*powf_(D, g-1);
2035
2036 // The residual.
2037 const float f_inv = powf_(Y, g)
2038 - powf_(D, g)
2039 + c*d + f;
2040 return x - f_inv;
2041}
static const SkScalar Y
static bool b
struct MyStruct a[10]
static float logf_(float x)
Definition skcms.cc:60

◆ select_curve_op()

static OpAndArg select_curve_op ( const skcms_Curve curve,
int  channel 
)
static

Definition at line 2372 of file skcms.cc.

2372 {
2373 struct OpType {
2374 Op sGamma, sRGBish, PQish, HLGish, HLGinvish, table;
2375 };
2376 static constexpr OpType kOps[] = {
2377 { Op::gamma_r, Op::tf_r, Op::pq_r, Op::hlg_r, Op::hlginv_r, Op::table_r },
2378 { Op::gamma_g, Op::tf_g, Op::pq_g, Op::hlg_g, Op::hlginv_g, Op::table_g },
2379 { Op::gamma_b, Op::tf_b, Op::pq_b, Op::hlg_b, Op::hlginv_b, Op::table_b },
2380 { Op::gamma_a, Op::tf_a, Op::pq_a, Op::hlg_a, Op::hlginv_a, Op::table_a },
2381 };
2382 const auto& op = kOps[channel];
2383
2384 if (curve->table_entries == 0) {
2385 const OpAndArg noop = { Op::load_a8/*doesn't matter*/, nullptr };
2386
2387 const skcms_TransferFunction& tf = curve->parametric;
2388
2389 if (tf_is_gamma(tf)) {
2390 return tf.g != 1 ? OpAndArg{op.sGamma, &tf}
2391 : noop;
2392 }
2393
2394 switch (classify(tf)) {
2395 case skcms_TFType_Invalid: return noop;
2396 case skcms_TFType_sRGBish: return OpAndArg{op.sRGBish, &tf};
2397 case skcms_TFType_PQish: return OpAndArg{op.PQish, &tf};
2398 case skcms_TFType_HLGish: return OpAndArg{op.HLGish, &tf};
2399 case skcms_TFType_HLGinvish: return OpAndArg{op.HLGinvish, &tf};
2400 }
2401 }
2402 return OpAndArg{op.table, curve};
2403}
SI F table(const skcms_Curve *curve, F v)
noop(*args, **kwargs)
static bool tf_is_gamma(const skcms_TransferFunction &tf)
Definition skcms.cc:2362

◆ select_curve_ops()

static int select_curve_ops ( const skcms_Curve curves,
int  numChannels,
OpAndArg ops 
)
static

Definition at line 2405 of file skcms.cc.

2405 {
2406 // We process the channels in reverse order, yielding ops in ABGR order.
2407 // (Working backwards allows us to fuse trailing B+G+R ops into a single RGB op.)
2408 int cursor = 0;
2409 for (int index = numChannels; index-- > 0; ) {
2410 ops[cursor] = select_curve_op(&curves[index], index);
2411 if (ops[cursor].arg) {
2412 ++cursor;
2413 }
2414 }
2415
2416 // Identify separate B+G+R ops and fuse them into a single RGB op.
2417 if (cursor >= 3) {
2418 struct FusableOps {
2419 Op r, g, b, rgb;
2420 };
2421 static constexpr FusableOps kFusableOps[] = {
2422 {Op::gamma_r, Op::gamma_g, Op::gamma_b, Op::gamma_rgb},
2423 {Op::tf_r, Op::tf_g, Op::tf_b, Op::tf_rgb},
2424 {Op::pq_r, Op::pq_g, Op::pq_b, Op::pq_rgb},
2425 {Op::hlg_r, Op::hlg_g, Op::hlg_b, Op::hlg_rgb},
2426 {Op::hlginv_r, Op::hlginv_g, Op::hlginv_b, Op::hlginv_rgb},
2427 };
2428
2429 int posR = cursor - 1;
2430 int posG = cursor - 2;
2431 int posB = cursor - 3;
2432 for (const FusableOps& fusableOp : kFusableOps) {
2433 if (ops[posR].op == fusableOp.r &&
2434 ops[posG].op == fusableOp.g &&
2435 ops[posB].op == fusableOp.b &&
2436 (0 == memcmp(ops[posR].arg, ops[posG].arg, sizeof(skcms_TransferFunction))) &&
2437 (0 == memcmp(ops[posR].arg, ops[posB].arg, sizeof(skcms_TransferFunction)))) {
2438 // Fuse the three matching ops into one.
2439 ops[posB].op = fusableOp.rgb;
2440 cursor -= 2;
2441 break;
2442 }
2443 }
2444 }
2445
2446 return cursor;
2447}
SkPathOp ops[]
static OpAndArg select_curve_op(const skcms_Curve *curve, int channel)
Definition skcms.cc:2372

◆ skcms_AdaptToXYZD50()

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

◆ skcms_ApproximateCurve()

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}
static bool fit_nonlinear(const skcms_Curve *curve, int L, int N, skcms_TransferFunction *tf)
Definition skcms.cc:2140

◆ skcms_ApproximatelyEqualProfiles()

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
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_PixelFormat
@ skcms_Signature_CMYK
@ skcms_AlphaFormat_Unpremul

◆ skcms_AreApproximateInverses()

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()

void skcms_DisableRuntimeCPUDetection ( void  )

Definition at line 40 of file skcms.cc.

40 {
42}

◆ skcms_GetCHAD()

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}
bool skcms_GetTagBySignature(const skcms_ICCProfile *profile, uint32_t sig, skcms_ICCTag *tag)
Definition skcms.cc:1221

◆ skcms_GetTagByIndex()

void skcms_GetTagByIndex ( const skcms_ICCProfile profile,
uint32_t  idx,
skcms_ICCTag tag 
)

Definition at line 1211 of file skcms.cc.

1211 {
1212 if (!profile || !profile->buffer || !tag) { return; }
1213 if (idx > profile->tag_count) { return; }
1214 const tag_Layout* tags = get_tag_table(profile);
1215 tag->signature = read_big_u32(tags[idx].signature);
1216 tag->size = read_big_u32(tags[idx].size);
1217 tag->buf = read_big_u32(tags[idx].offset) + profile->buffer;
1218 tag->type = read_big_u32(tag->buf);
1219}
static const tag_Layout * get_tag_table(const skcms_ICCProfile *profile)
Definition skcms.cc:384
Point offset

◆ skcms_GetTagBySignature()

bool skcms_GetTagBySignature ( const skcms_ICCProfile profile,
uint32_t  sig,
skcms_ICCTag tag 
)

Definition at line 1221 of file skcms.cc.

1221 {
1222 if (!profile || !profile->buffer || !tag) { return false; }
1223 const tag_Layout* tags = get_tag_table(profile);
1224 for (uint32_t i = 0; i < profile->tag_count; ++i) {
1225 if (read_big_u32(tags[i].signature) == sig) {
1226 tag->signature = sig;
1227 tag->size = read_big_u32(tags[i].size);
1228 tag->buf = read_big_u32(tags[i].offset) + profile->buffer;
1229 tag->type = read_big_u32(tag->buf);
1230 return true;
1231 }
1232 }
1233 return false;
1234}

◆ skcms_GetWTPT()

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}

◆ skcms_Identity_TransferFunction()

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_MakeUsableAsDestination()

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
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()

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
bool skcms_MakeUsableAsDestination(skcms_ICCProfile *profile)
Definition skcms.cc:2819

◆ skcms_Matrix3x3_concat()

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()

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}

◆ skcms_MaxRoundtripError()

float skcms_MaxRoundtripError ( const skcms_Curve curve,
const skcms_TransferFunction inv_tf 
)

Definition at line 273 of file skcms.cc.

273 {
274 uint32_t N = curve->table_entries > 256 ? curve->table_entries : 256;
275 const float dx = 1.0f / static_cast<float>(N - 1);
276 float err = 0;
277 for (uint32_t i = 0; i < N; i++) {
278 float x = static_cast<float>(i) * dx,
279 y = eval_curve(curve, x);
280 err = fmaxf_(err, fabsf_(x - skcms_TransferFunction_eval(inv_tf, y)));
281 }
282 return err;
283}
static float fabsf_(float x)

◆ skcms_ParseWithA2BPriority()

bool skcms_ParseWithA2BPriority ( const void *  buf,
size_t  len,
const int  priority[],
const 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 bool usable_as_src(const skcms_ICCProfile *profile)
Definition skcms.cc:1236
@ skcms_Signature_Lab
@ skcms_Signature_Gray
static const char header[]
Definition skpbench.cpp:88

◆ skcms_PrimariesToXYZD50()

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_sRGB_Inverse_TransferFunction()

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()

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_Signature_RGB

◆ skcms_sRGB_TransferFunction()

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_eval()

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 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_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()

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
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()

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()

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()

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_makePQish()

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()

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}
#define R(r)
bool skcms_TransferFunction_isHLGish(const skcms_TransferFunction *tf)
Definition skcms.cc:192
Definition SkMD5.cpp:125

◆ skcms_Transform()

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 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}
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
const skcms_TransferFunction * skcms_sRGB_TransferFunction()
Definition skcms.cc:1587
static void skcms_SetXYZD50(skcms_ICCProfile *p, const skcms_Matrix3x3 *m)
@ skcms_AlphaFormat_PremulAsEncoded
@ skcms_AlphaFormat_Opaque
Definition SkMD5.cpp:134
skcms_Matrix3x3 toXYZD50
uint32_t data_color_space

◆ skcms_TRCs_AreApproximateInverse()

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()

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}

◆ tf_is_gamma()

static bool tf_is_gamma ( const skcms_TransferFunction tf)
static

Definition at line 2362 of file skcms.cc.

2362 {
2363 return tf.g > 0 && tf.a == 1 &&
2364 tf.b == 0 && tf.c == 0 && tf.d == 0 && tf.e == 0 && tf.f == 0;
2365}

◆ TFKind_marker()

static float TFKind_marker ( skcms_TFType  kind)
static

Definition at line 130 of file skcms.cc.

130 {
131 // We'd use different NaNs, but those aren't guaranteed to be preserved by WASM.
132 return -(float)kind;
133}

◆ usable_as_src()

static bool usable_as_src ( const skcms_ICCProfile profile)
static

Definition at line 1236 of file skcms.cc.

1236 {
1237 return profile->has_A2B
1238 || (profile->has_trc && profile->has_toXYZD50);
1239}

Variable Documentation

◆ sAllowRuntimeCPUDetection

bool sAllowRuntimeCPUDetection = true
static

Definition at line 38 of file skcms.cc.

◆ skcms_252_random_bytes

const uint8_t skcms_252_random_bytes[]
Initial value:
= {
8, 179, 128, 204, 253, 38, 134, 184, 68, 102, 32, 138, 99, 39, 169, 215,
119, 26, 3, 223, 95, 239, 52, 132, 114, 74, 81, 234, 97, 116, 244, 205, 30,
154, 173, 12, 51, 159, 122, 153, 61, 226, 236, 178, 229, 55, 181, 220, 191,
194, 160, 126, 168, 82, 131, 18, 180, 245, 163, 22, 246, 69, 235, 252, 57,
108, 14, 6, 152, 240, 255, 171, 242, 20, 227, 177, 238, 96, 85, 16, 211,
70, 200, 149, 155, 146, 127, 145, 100, 151, 109, 19, 165, 208, 195, 164,
137, 254, 182, 248, 64, 201, 45, 209, 5, 147, 207, 210, 113, 162, 83, 225,
9, 31, 15, 231, 115, 37, 58, 53, 24, 49, 197, 56, 120, 172, 48, 21, 214,
129, 111, 11, 50, 187, 196, 34, 60, 103, 71, 144, 47, 203, 77, 80, 232,
140, 222, 250, 206, 166, 247, 139, 249, 221, 72, 106, 27, 199, 117, 54,
219, 135, 118, 40, 79, 41, 251, 46, 93, 212, 92, 233, 148, 28, 121, 63,
123, 158, 105, 59, 29, 42, 143, 23, 0, 107, 176, 87, 104, 183, 156, 193,
189, 90, 188, 65, 190, 17, 198, 7, 186, 161, 1, 124, 78, 125, 170, 133,
174, 218, 67, 157, 75, 101, 89, 217, 62, 33, 141, 228, 25, 35, 91, 230, 4,
2, 13, 73, 86, 167, 237, 84, 243, 44, 185, 66, 130, 110, 150, 142, 216, 88,
112, 36, 224, 136, 202, 76, 94, 98, 175, 213
}

Definition at line 1602 of file skcms.cc.

1602 {
1603 8, 179, 128, 204, 253, 38, 134, 184, 68, 102, 32, 138, 99, 39, 169, 215,
1604 119, 26, 3, 223, 95, 239, 52, 132, 114, 74, 81, 234, 97, 116, 244, 205, 30,
1605 154, 173, 12, 51, 159, 122, 153, 61, 226, 236, 178, 229, 55, 181, 220, 191,
1606 194, 160, 126, 168, 82, 131, 18, 180, 245, 163, 22, 246, 69, 235, 252, 57,
1607 108, 14, 6, 152, 240, 255, 171, 242, 20, 227, 177, 238, 96, 85, 16, 211,
1608 70, 200, 149, 155, 146, 127, 145, 100, 151, 109, 19, 165, 208, 195, 164,
1609 137, 254, 182, 248, 64, 201, 45, 209, 5, 147, 207, 210, 113, 162, 83, 225,
1610 9, 31, 15, 231, 115, 37, 58, 53, 24, 49, 197, 56, 120, 172, 48, 21, 214,
1611 129, 111, 11, 50, 187, 196, 34, 60, 103, 71, 144, 47, 203, 77, 80, 232,
1612 140, 222, 250, 206, 166, 247, 139, 249, 221, 72, 106, 27, 199, 117, 54,
1613 219, 135, 118, 40, 79, 41, 251, 46, 93, 212, 92, 233, 148, 28, 121, 63,
1614 123, 158, 105, 59, 29, 42, 143, 23, 0, 107, 176, 87, 104, 183, 156, 193,
1615 189, 90, 188, 65, 190, 17, 198, 7, 186, 161, 1, 124, 78, 125, 170, 133,
1616 174, 218, 67, 157, 75, 101, 89, 217, 62, 33, 141, 228, 25, 35, 91, 230, 4,
1617 2, 13, 73, 86, 167, 237, 84, 243, 44, 185, 66, 130, 110, 150, 142, 216, 88,
1618 112, 36, 224, 136, 202, 76, 94, 98, 175, 213
1619};