blob: abc604be56eba529e28155a6da777da2a3aa9f49 [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
211// When premultiplying and/or using a non-linear transfer function, it's important
212// that we know the order the operations are applied. If you're used to working
213// with non-color-managed drawing systems, PremulAsEncoded is probably the "premul"
214// you're looking for; if you want linear blending, PremulLinear is the choice for you.
215
Mike Klein00c9e462018-04-17 09:42:55 -0400216typedef enum skcms_AlphaFormat {
Mike Kleinded7a552018-04-10 10:05:31 -0400217 skcms_AlphaFormat_Opaque, // alpha is always opaque
218 // tf-1(r), tf-1(g), tf-1(b), 1.0
219 skcms_AlphaFormat_Unpremul, // alpha and color are unassociated
220 // tf-1(r), tf-1(g), tf-1(b), a
221 skcms_AlphaFormat_PremulAsEncoded, // premultiplied while encoded
222 // tf-1(r)*a, tf-1(g)*a, tf-1(b)*a, a
223 skcms_AlphaFormat_PremulLinear, // premultiplied while linear
224 // tf-1(r*a), tf-1(g*a), tf-1(b*a), a
225} skcms_AlphaFormat;
226
227// Convert npixels pixels from src format and color profile to dst format and color profile
228// 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 +0000229SKCMS_API bool skcms_Transform(const void* src,
230 skcms_PixelFormat srcFmt,
231 skcms_AlphaFormat srcAlpha,
232 const skcms_ICCProfile* srcProfile,
233 void* dst,
234 skcms_PixelFormat dstFmt,
235 skcms_AlphaFormat dstAlpha,
236 const skcms_ICCProfile* dstProfile,
237 size_t npixels);
Mike Kleinded7a552018-04-10 10:05:31 -0400238
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com2af09042018-05-02 18:59:47 +0000239// If profile can be used as a destination in skcms_Transform, return true. Otherwise, attempt to
240// rewrite it with approximations where reasonable. If successful, return true. If no reasonable
241// approximation exists, leave the profile unchanged and return false.
242SKCMS_API bool skcms_MakeUsableAsDestination(skcms_ICCProfile* profile);
243
244// If profile can be used as a destination with a single parametric transfer function (ie for
245// rasterization), return true. Otherwise, attempt to rewrite it with approximations where
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com908ad222018-05-02 20:11:46 +0000246// 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 +0000247// profile unchanged and return false.
248SKCMS_API bool skcms_MakeUsableAsDestinationWithSingleCurve(skcms_ICCProfile* profile);
249
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.comb5d1f242018-05-09 18:44:08 +0000250SKCMS_API bool skcms_PrimariesToXYZD50(float rx, float ry,
251 float gx, float gy,
252 float bx, float by,
253 float wx, float wy,
254 skcms_Matrix3x3* toXYZD50);
255
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com30a067c2018-05-09 19:43:08 +0000256// Utilities for programmatically constructing profiles
257static inline void skcms_Init(skcms_ICCProfile* p) {
258 memset(p, 0, sizeof(*p));
259 p->data_color_space = skcms_Signature_RGB;
260 p->pcs = skcms_Signature_XYZ;
261}
262
263static inline void skcms_SetTransferFunction(skcms_ICCProfile* p,
264 const skcms_TransferFunction* tf) {
265 p->has_trc = true;
266 for (int i = 0; i < 3; ++i) {
267 p->trc[i].table_entries = 0;
268 p->trc[i].parametric = *tf;
skcms-skia-autoroll@skia-buildbots.google.com.iam.gserviceaccount.com30a067c2018-05-09 19:43:08 +0000269 }
270}
271
272static inline void skcms_SetXYZD50(skcms_ICCProfile* p, const skcms_Matrix3x3* m) {
273 p->has_toXYZD50 = true;
274 p->toXYZD50 = *m;
275}
276
Mike Kleinded7a552018-04-10 10:05:31 -0400277#ifdef __cplusplus
278}
279#endif