blob: c60746349dc1e5594d7ac313f6913b4ccbca2ec7 [file] [log] [blame]
Mike Kleinded7a552018-04-10 10:05:31 -04001/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#pragma once
9
10// skcms.h contains the entire public API for skcms.
11
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.comabc19762018-04-25 20:01:32 +000012#ifndef SKCMS_API
13 #define SKCMS_API
14#endif
15
Mike Kleinded7a552018-04-10 10:05:31 -040016#include <stdbool.h>
17#include <stddef.h>
18#include <stdint.h>
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com30a067c2018-05-09 19:43:08 +000019#include <string.h>
Mike Kleinded7a552018-04-10 10:05:31 -040020
21#ifdef __cplusplus
22extern "C" {
23#endif
24
25// A row-major 3x3 matrix (ie vals[row][col])
Mike Klein00c9e462018-04-17 09:42:55 -040026typedef struct skcms_Matrix3x3 {
Mike Kleinded7a552018-04-10 10:05:31 -040027 float vals[3][3];
28} skcms_Matrix3x3;
29
30// A row-major 3x4 matrix (ie vals[row][col])
Mike Klein00c9e462018-04-17 09:42:55 -040031typedef struct skcms_Matrix3x4 {
Mike Kleinded7a552018-04-10 10:05:31 -040032 float vals[3][4];
33} skcms_Matrix3x4;
34
35// A transfer function mapping encoded values to linear values,
36// represented by this 7-parameter piecewise function:
37//
38// linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d
39// = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
40//
41// (A simple gamma transfer function sets g to gamma and a to 1.)
Mike Klein00c9e462018-04-17 09:42:55 -040042typedef struct skcms_TransferFunction {
Mike Kleinded7a552018-04-10 10:05:31 -040043 float g, a,b,c,d,e,f;
44} skcms_TransferFunction;
45
46// Unified representation of 'curv' or 'para' tag data, or a 1D table from 'mft1' or 'mft2'
Mike Klein00c9e462018-04-17 09:42:55 -040047typedef union skcms_Curve {
Mike Klein06a610c2018-04-11 09:15:15 -040048 struct {
49 uint32_t alias_of_table_entries;
50 skcms_TransferFunction parametric;
51 };
52 struct {
53 uint32_t table_entries;
54 const uint8_t* table_8;
55 const uint8_t* table_16;
Mike Kleinded7a552018-04-10 10:05:31 -040056 };
57} skcms_Curve;
58
Mike Klein00c9e462018-04-17 09:42:55 -040059typedef struct skcms_A2B {
Mike Kleinded7a552018-04-10 10:05:31 -040060 // Optional: N 1D curves, followed by an N-dimensional CLUT.
61 // If input_channels == 0, these curves and CLUT are skipped,
62 // Otherwise, input_channels must be in [1, 4].
63 uint32_t input_channels;
64 skcms_Curve input_curves[4];
65 uint8_t grid_points[4];
66 const uint8_t* grid_8;
67 const uint8_t* grid_16;
68
69 // Optional: 3 1D curves, followed by a color matrix.
70 // If matrix_channels == 0, these curves and matrix are skipped,
71 // Otherwise, matrix_channels must be 3.
72 uint32_t matrix_channels;
73 skcms_Curve matrix_curves[3];
74 skcms_Matrix3x4 matrix;
75
76 // Required: 3 1D curves. Always present, and output_channels must be 3.
77 uint32_t output_channels;
78 skcms_Curve output_curves[3];
79} skcms_A2B;
80
Mike Klein00c9e462018-04-17 09:42:55 -040081typedef struct skcms_ICCProfile {
Mike Kleinded7a552018-04-10 10:05:31 -040082 const uint8_t* buffer;
83
84 uint32_t size;
85 uint32_t data_color_space;
86 uint32_t pcs;
87 uint32_t tag_count;
88
89 // skcms_Parse() will set commonly-used fields for you when possible:
90
91 // If we can parse red, green and blue transfer curves from the profile,
92 // trc will be set to those three curves, and has_trc will be true.
93 bool has_trc;
94 skcms_Curve trc[3];
95
96 // If this profile's gamut can be represented by a 3x3 transform to XYZD50,
97 // skcms_Parse() sets toXYZD50 to that transform and has_toXYZD50 to true.
98 bool has_toXYZD50;
99 skcms_Matrix3x3 toXYZD50;
100
101 // If the profile has a valid A2B0 tag, skcms_Parse() sets A2B to that data,
102 // and has_A2B to true.
103 bool has_A2B;
104 skcms_A2B A2B;
105} skcms_ICCProfile;
106
Mike Klein3462eb02018-04-11 13:53:40 -0400107// The sRGB color profile is so commonly used that we offer a canonical skcms_ICCProfile for it.
Brian Osman0ab0f1c2018-05-01 13:09:53 -0400108SKCMS_API const skcms_ICCProfile* skcms_sRGB_profile(void);
Mike Klein3101f652018-04-17 11:20:08 -0400109// Ditto for XYZD50, the most common profile connection space.
Brian Osman0ab0f1c2018-05-01 13:09:53 -0400110SKCMS_API const skcms_ICCProfile* skcms_XYZD50_profile(void);
Mike Klein3462eb02018-04-11 13:53:40 -0400111
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com8c29c142018-05-17 18:42:40 +0000112SKCMS_API const skcms_TransferFunction* skcms_sRGB_TransferFunction(void);
113SKCMS_API const skcms_TransferFunction* skcms_sRGB_Inverse_TransferFunction(void);
114SKCMS_API const skcms_TransferFunction* skcms_Identity_TransferFunction(void);
115
Mike Klein3462eb02018-04-11 13:53:40 -0400116// Practical equality test for two skcms_ICCProfiles.
117// The implementation is subject to change, but it will always try to answer
118// "can I substitute A for B?" and "can I skip transforming from A to B?".
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.comabc19762018-04-25 20:01:32 +0000119SKCMS_API bool skcms_ApproximatelyEqualProfiles(const skcms_ICCProfile* A,
120 const skcms_ICCProfile* B);
Mike Klein3462eb02018-04-11 13:53:40 -0400121
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com8c29c142018-05-17 18:42:40 +0000122// Practical test that answers: Is curve roughly the inverse of inv_tf? Typically used by passing
123// the inverse of a known parametric transfer function (like sRGB), to determine if a particular
124// curve is very close to sRGB.
125SKCMS_API bool skcms_AreApproximateInverses(const skcms_Curve* curve,
126 const skcms_TransferFunction* inv_tf);
127
128// Similar to above, answering the question for all three TRC curves of the given profile. Again,
129// passing skcms_sRGB_InverseTransferFunction as inv_tf will answer the question:
130// "Does this profile have a transfer function that is very close to sRGB?"
131SKCMS_API bool skcms_TRCs_AreApproximateInverse(const skcms_ICCProfile* profile,
132 const skcms_TransferFunction* inv_tf);
133
Mike Kleinded7a552018-04-10 10:05:31 -0400134// Parse an ICC profile and return true if possible, otherwise return false.
135// The buffer is not copied, it must remain valid as long as the skcms_ICCProfile
136// will be used.
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.comabc19762018-04-25 20:01:32 +0000137SKCMS_API bool skcms_Parse(const void*, size_t, skcms_ICCProfile*);
Mike Kleinded7a552018-04-10 10:05:31 -0400138
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com86168a12018-05-17 17:58:48 +0000139// No-op, to be removed.
140static inline void skcms_OptimizeForSpeed(skcms_ICCProfile* p) { (void)p; }
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com3315f7e2018-04-23 18:57:32 +0000141
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.comabc19762018-04-25 20:01:32 +0000142SKCMS_API bool skcms_ApproximateCurve(const skcms_Curve* curve,
143 skcms_TransferFunction* approx,
144 float* max_error);
Mike Kleinded7a552018-04-10 10:05:31 -0400145
Mike Klein00c9e462018-04-17 09:42:55 -0400146typedef struct skcms_ICCTag {
Mike Kleinded7a552018-04-10 10:05:31 -0400147 uint32_t signature;
148 uint32_t type;
149 uint32_t size;
150 const uint8_t* buf;
151} skcms_ICCTag;
152
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.comabc19762018-04-25 20:01:32 +0000153SKCMS_API void skcms_GetTagByIndex (const skcms_ICCProfile*, uint32_t idx, skcms_ICCTag*);
154SKCMS_API bool skcms_GetTagBySignature(const skcms_ICCProfile*, uint32_t sig, skcms_ICCTag*);
Mike Kleinded7a552018-04-10 10:05:31 -0400155
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.coma724ea52018-05-07 16:57:26 +0000156// These are common ICC signature values
157enum {
158 // data_color_space
159 skcms_Signature_CMYK = 0x434D594B,
160 skcms_Signature_Gray = 0x47524159,
161 skcms_Signature_RGB = 0x52474220,
162
163 // pcs
164 skcms_Signature_Lab = 0x4C616220,
165 skcms_Signature_XYZ = 0x58595A20,
166};
167
Mike Klein00c9e462018-04-17 09:42:55 -0400168typedef enum skcms_PixelFormat {
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com09f92b92018-06-21 18:47:13 +0000169 skcms_PixelFormat_A_8,
170 skcms_PixelFormat_A_8_,
171 skcms_PixelFormat_G_8,
172 skcms_PixelFormat_G_8_,
173
Mike Kleinded7a552018-04-10 10:05:31 -0400174 skcms_PixelFormat_RGB_565,
175 skcms_PixelFormat_BGR_565,
176
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com09f92b92018-06-21 18:47:13 +0000177 skcms_PixelFormat_ABGR_4444,
178 skcms_PixelFormat_ARGB_4444,
179
Mike Kleinded7a552018-04-10 10:05:31 -0400180 skcms_PixelFormat_RGB_888,
181 skcms_PixelFormat_BGR_888,
182 skcms_PixelFormat_RGBA_8888,
183 skcms_PixelFormat_BGRA_8888,
184
185 skcms_PixelFormat_RGBA_1010102,
186 skcms_PixelFormat_BGRA_1010102,
187
188 skcms_PixelFormat_RGB_161616, // Big-endian. Pointers must be 16-bit aligned.
189 skcms_PixelFormat_BGR_161616,
190 skcms_PixelFormat_RGBA_16161616,
191 skcms_PixelFormat_BGRA_16161616,
192
193 skcms_PixelFormat_RGB_hhh, // 1-5-10 half-precision float.
194 skcms_PixelFormat_BGR_hhh, // Pointers must be 16-bit aligned.
195 skcms_PixelFormat_RGBA_hhhh,
196 skcms_PixelFormat_BGRA_hhhh,
197
198 skcms_PixelFormat_RGB_fff, // 1-8-23 single-precision float (the normal kind).
199 skcms_PixelFormat_BGR_fff, // Pointers must be 32-bit aligned.
200 skcms_PixelFormat_RGBA_ffff,
201 skcms_PixelFormat_BGRA_ffff,
202} skcms_PixelFormat;
203
204// We always store any alpha channel linearly. In the chart below, tf-1() is the inverse
205// transfer function for the given color profile (applying the transfer function linearizes).
206
207// We treat opaque as a strong requirement, not just a performance hint: we will ignore
208// any source alpha and treat it as 1.0, and will make sure that any destination alpha
209// channel is filled with the equivalent of 1.0.
210
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com5f0943f2018-08-30 21:16:38 +0000211// We used to offer multiple types of premultiplication, but now just one, PremulAsEncoded.
212// This is the premul you're probably used to working with.
Mike Kleinded7a552018-04-10 10:05:31 -0400213
Mike Klein00c9e462018-04-17 09:42:55 -0400214typedef enum skcms_AlphaFormat {
Mike Kleinded7a552018-04-10 10:05:31 -0400215 skcms_AlphaFormat_Opaque, // alpha is always opaque
216 // tf-1(r), tf-1(g), tf-1(b), 1.0
217 skcms_AlphaFormat_Unpremul, // alpha and color are unassociated
218 // tf-1(r), tf-1(g), tf-1(b), a
219 skcms_AlphaFormat_PremulAsEncoded, // premultiplied while encoded
220 // tf-1(r)*a, tf-1(g)*a, tf-1(b)*a, a
Mike Kleinded7a552018-04-10 10:05:31 -0400221} skcms_AlphaFormat;
222
223// Convert npixels pixels from src format and color profile to dst format and color profile
224// and return true, otherwise return false. It is safe to alias dst == src if dstFmt == srcFmt.
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.comabc19762018-04-25 20:01:32 +0000225SKCMS_API bool skcms_Transform(const void* src,
226 skcms_PixelFormat srcFmt,
227 skcms_AlphaFormat srcAlpha,
228 const skcms_ICCProfile* srcProfile,
229 void* dst,
230 skcms_PixelFormat dstFmt,
231 skcms_AlphaFormat dstAlpha,
232 const skcms_ICCProfile* dstProfile,
233 size_t npixels);
Mike Kleinded7a552018-04-10 10:05:31 -0400234
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com2af09042018-05-02 18:59:47 +0000235// If profile can be used as a destination in skcms_Transform, return true. Otherwise, attempt to
236// rewrite it with approximations where reasonable. If successful, return true. If no reasonable
237// approximation exists, leave the profile unchanged and return false.
238SKCMS_API bool skcms_MakeUsableAsDestination(skcms_ICCProfile* profile);
239
240// If profile can be used as a destination with a single parametric transfer function (ie for
241// rasterization), return true. Otherwise, attempt to rewrite it with approximations where
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com908ad2242018-05-02 20:11:46 +0000242// reasonable. If successful, return true. If no reasonable approximation exists, leave the
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com2af09042018-05-02 18:59:47 +0000243// profile unchanged and return false.
244SKCMS_API bool skcms_MakeUsableAsDestinationWithSingleCurve(skcms_ICCProfile* profile);
245
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.comb5d1f242018-05-09 18:44:08 +0000246SKCMS_API bool skcms_PrimariesToXYZD50(float rx, float ry,
247 float gx, float gy,
248 float bx, float by,
249 float wx, float wy,
250 skcms_Matrix3x3* toXYZD50);
251
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com30a067c2018-05-09 19:43:08 +0000252// Utilities for programmatically constructing profiles
253static inline void skcms_Init(skcms_ICCProfile* p) {
254 memset(p, 0, sizeof(*p));
255 p->data_color_space = skcms_Signature_RGB;
256 p->pcs = skcms_Signature_XYZ;
257}
258
259static inline void skcms_SetTransferFunction(skcms_ICCProfile* p,
260 const skcms_TransferFunction* tf) {
261 p->has_trc = true;
262 for (int i = 0; i < 3; ++i) {
263 p->trc[i].table_entries = 0;
264 p->trc[i].parametric = *tf;
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com30a067c2018-05-09 19:43:08 +0000265 }
266}
267
268static inline void skcms_SetXYZD50(skcms_ICCProfile* p, const skcms_Matrix3x3* m) {
269 p->has_toXYZD50 = true;
270 p->toXYZD50 = *m;
271}
272
Mike Kleinded7a552018-04-10 10:05:31 -0400273#ifdef __cplusplus
274}
275#endif