Roll skcms from b9593d4e39ea to 4e8691e28194 (1 revision)

https://skia.googlesource.com/skcms.git/+log/b9593d4e39ea..4e8691e28194

2021-03-31 mtklein@google.com B2A part 1, types and parsing

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skcms-skia-autoroll
Please CC mtklein@google.com on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/master/autoroll/README.md

Cq-Include-Trybots: luci.chromium.try:linux-blink-rel
Tbr: mtklein@google.com
Change-Id: I52d7f3b5ca56be4bb6c8a4955b70dc09bf63ad96
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/391159
Reviewed-by: skia-autoroll <skia-autoroll@skia-public.iam.gserviceaccount.com>
Commit-Queue: skia-autoroll <skia-autoroll@skia-public.iam.gserviceaccount.com>
This commit is contained in:
skia-autoroll 2021-03-31 15:47:13 +00:00 committed by Skia Commit-Bot
parent 759bbf7b60
commit 94dc3b709b
3 changed files with 384 additions and 95 deletions

View File

@ -104,8 +104,12 @@ typedef union skcms_Curve {
};
} skcms_Curve;
// Complex transforms between device space (A) and profile connection space (B):
// A2B: device -> [ "A" curves -> CLUT ] -> [ "M" curves -> matrix ] -> "B" curves -> PCS
// B2A: device <- [ "A" curves <- CLUT ] <- [ "M" curves <- matrix ] <- "B" curves <- PCS
typedef struct skcms_A2B {
// Optional: N 1D curves, followed by an N-dimensional CLUT.
// Optional: N 1D "A" curves, followed by an N-dimensional CLUT.
// If input_channels == 0, these curves and CLUT are skipped,
// Otherwise, input_channels must be in [1, 4].
uint32_t input_channels;
@ -114,18 +118,41 @@ typedef struct skcms_A2B {
const uint8_t* grid_8;
const uint8_t* grid_16;
// Optional: 3 1D curves, followed by a color matrix.
// Optional: 3 1D "M" curves, followed by a color matrix.
// If matrix_channels == 0, these curves and matrix are skipped,
// Otherwise, matrix_channels must be 3.
uint32_t matrix_channels;
skcms_Curve matrix_curves[3];
skcms_Matrix3x4 matrix;
// Required: 3 1D curves. Always present, and output_channels must be 3.
// Required: 3 1D "B" curves. Always present, and output_channels must be 3.
uint32_t output_channels;
skcms_Curve output_curves[3];
} skcms_A2B;
typedef struct skcms_B2A {
// Required: 3 1D "B" curves. Always present, and input_channels must be 3.
uint32_t input_channels;
skcms_Curve input_curves[3];
// Optional: a color matrix, followed by 3 1D "M" curves.
// If matrix_channels == 0, this matrix and these curves are skipped,
// Otherwise, matrix_channels must be 3.
uint32_t matrix_channels;
skcms_Matrix3x4 matrix;
skcms_Curve matrix_curves[3];
// Optional: an N-dimensional CLUT, followed by N 1D "A" curves.
// If output_channels == 0, this CLUT and these curves are skipped,
// Otherwise, output_channels must be in [1, 4].
uint32_t output_channels;
uint8_t grid_points[4];
const uint8_t* grid_8;
const uint8_t* grid_16;
skcms_Curve output_curves[4];
} skcms_B2A;
typedef struct skcms_ICCProfile {
const uint8_t* buffer;
@ -151,6 +178,13 @@ typedef struct skcms_ICCProfile {
// same following any user-provided prioritization of A2B0, A2B1, or A2B2.
bool has_A2B;
skcms_A2B A2B;
// If the profile has a valid B2A0 or B2A1 tag, skcms_Parse() sets B2A to
// that data, and has_B2A to true. skcms_ParseWithA2BPriority() does the
// same following any user-provided prioritization of B2A0, B2A1, or B2A2.
bool has_B2A;
skcms_B2A B2A;
} skcms_ICCProfile;
// The sRGB color profile is so commonly used that we offer a canonical skcms_ICCProfile for it.

View File

@ -288,8 +288,7 @@ enum {
skcms_Signature_bXYZ = 0x6258595A,
skcms_Signature_A2B0 = 0x41324230,
skcms_Signature_A2B1 = 0x41324231,
skcms_Signature_mAB = 0x6D414220,
skcms_Signature_B2A0 = 0x42324130,
skcms_Signature_CHAD = 0x63686164,
skcms_Signature_WTPT = 0x77747074,
@ -298,6 +297,8 @@ enum {
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,
// XYZ is also a PCS signature, so it's defined in skcms.h
@ -606,9 +607,17 @@ static bool read_mft_common(const mft_CommonLayout* mftTag, skcms_A2B* a2b) {
// field/flag.
a2b->matrix_channels = 0;
a2b->input_channels = mftTag->input_channels[0];
a2b-> input_channels = mftTag-> input_channels[0];
a2b->output_channels = mftTag->output_channels[0];
for (uint32_t i = 0; i < a2b->input_channels; ++i) {
a2b->grid_points[i] = mftTag->grid_points[0];
}
// The grid only makes sense with at least two points along each axis
if (a2b->grid_points[0] < 2) {
return false;
}
// We require exactly three (ie XYZ/Lab/RGB) output channels
if (a2b->output_channels != ARRAY_COUNT(a2b->output_curves)) {
return false;
@ -618,95 +627,116 @@ static bool read_mft_common(const mft_CommonLayout* mftTag, skcms_A2B* a2b) {
return false;
}
for (uint32_t i = 0; i < a2b->input_channels; ++i) {
a2b->grid_points[i] = mftTag->grid_points[0];
return true;
}
// All as the A2B version above, except where noted.
static bool read_mft_common(const mft_CommonLayout* mftTag, skcms_B2A* b2a) {
// All the same as A2B until the next comment...
b2a->matrix_channels = 0;
b2a-> input_channels = mftTag-> input_channels[0];
b2a->output_channels = mftTag->output_channels[0];
for (uint32_t i = 0; i < b2a->input_channels; ++i) {
b2a->grid_points[i] = mftTag->grid_points[0];
}
// The grid only makes sense with at least two points along each axis
if (a2b->grid_points[0] < 2) {
if (b2a->grid_points[0] < 2) {
return false;
}
// ...for B2A, exactly 3 *input* channels and 1-4 *output* channels.
if (b2a->input_channels != ARRAY_COUNT(b2a->input_curves)) {
return false;
}
if (b2a->output_channels < 1 || b2a->output_channels > ARRAY_COUNT(b2a->output_curves)) {
return false;
}
return true;
}
static bool init_a2b_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,
skcms_A2B* a2b) {
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) {
// byte_width is 1 or 2, [input|output]_table_entries are in [2, 4096], so no overflow
uint32_t byte_len_per_input_table = input_table_entries * byte_width;
uint32_t byte_len_per_output_table = output_table_entries * byte_width;
// [input|output]_channels are <= 4, so still no overflow
uint32_t byte_len_all_input_tables = a2b->input_channels * byte_len_per_input_table;
uint32_t byte_len_all_output_tables = a2b->output_channels * byte_len_per_output_table;
uint32_t byte_len_all_input_tables = out->input_channels * byte_len_per_input_table;
uint32_t byte_len_all_output_tables = out->output_channels * byte_len_per_output_table;
uint64_t grid_size = a2b->output_channels * byte_width;
for (uint32_t axis = 0; axis < a2b->input_channels; ++axis) {
grid_size *= a2b->grid_points[axis];
uint64_t grid_size = out->output_channels * byte_width;
for (uint32_t axis = 0; axis < out->input_channels; ++axis) {
grid_size *= out->grid_points[axis];
}
if (max_tables_len < byte_len_all_input_tables + grid_size + byte_len_all_output_tables) {
return false;
}
for (uint32_t i = 0; i < a2b->input_channels; ++i) {
a2b->input_curves[i].table_entries = input_table_entries;
for (uint32_t i = 0; i < out->input_channels; ++i) {
out->input_curves[i].table_entries = input_table_entries;
if (byte_width == 1) {
a2b->input_curves[i].table_8 = table_base + i * byte_len_per_input_table;
a2b->input_curves[i].table_16 = nullptr;
out->input_curves[i].table_8 = table_base + i * byte_len_per_input_table;
out->input_curves[i].table_16 = nullptr;
} else {
a2b->input_curves[i].table_8 = nullptr;
a2b->input_curves[i].table_16 = table_base + i * byte_len_per_input_table;
out->input_curves[i].table_8 = nullptr;
out->input_curves[i].table_16 = table_base + i * byte_len_per_input_table;
}
}
if (byte_width == 1) {
a2b->grid_8 = table_base + byte_len_all_input_tables;
a2b->grid_16 = nullptr;
out->grid_8 = table_base + byte_len_all_input_tables;
out->grid_16 = nullptr;
} else {
a2b->grid_8 = nullptr;
a2b->grid_16 = table_base + byte_len_all_input_tables;
out->grid_8 = nullptr;
out->grid_16 = table_base + byte_len_all_input_tables;
}
const uint8_t* output_table_base = table_base + byte_len_all_input_tables + grid_size;
for (uint32_t i = 0; i < a2b->output_channels; ++i) {
a2b->output_curves[i].table_entries = output_table_entries;
for (uint32_t i = 0; i < out->output_channels; ++i) {
out->output_curves[i].table_entries = output_table_entries;
if (byte_width == 1) {
a2b->output_curves[i].table_8 = output_table_base + i * byte_len_per_output_table;
a2b->output_curves[i].table_16 = nullptr;
out->output_curves[i].table_8 = output_table_base + i * byte_len_per_output_table;
out->output_curves[i].table_16 = nullptr;
} else {
a2b->output_curves[i].table_8 = nullptr;
a2b->output_curves[i].table_16 = output_table_base + i * byte_len_per_output_table;
out->output_curves[i].table_8 = nullptr;
out->output_curves[i].table_16 = output_table_base + i * byte_len_per_output_table;
}
}
return true;
}
static bool read_tag_mft1(const skcms_ICCTag* tag, skcms_A2B* a2b) {
template <typename A2B_or_B2A>
static bool read_tag_mft1(const skcms_ICCTag* tag, A2B_or_B2A* out) {
if (tag->size < SAFE_FIXED_SIZE(mft1_Layout)) {
return false;
}
const mft1_Layout* mftTag = (const mft1_Layout*)tag->buf;
if (!read_mft_common(mftTag->common, a2b)) {
if (!read_mft_common(mftTag->common, out)) {
return false;
}
uint32_t input_table_entries = 256;
uint32_t output_table_entries = 256;
return init_a2b_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft1_Layout), 1,
input_table_entries, output_table_entries, a2b);
return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft1_Layout), 1,
input_table_entries, output_table_entries, out);
}
static bool read_tag_mft2(const skcms_ICCTag* tag, skcms_A2B* a2b) {
template <typename A2B_or_B2A>
static bool read_tag_mft2(const skcms_ICCTag* tag, A2B_or_B2A* out) {
if (tag->size < SAFE_FIXED_SIZE(mft2_Layout)) {
return false;
}
const mft2_Layout* mftTag = (const mft2_Layout*)tag->buf;
if (!read_mft_common(mftTag->common, a2b)) {
if (!read_mft_common(mftTag->common, out)) {
return false;
}
@ -719,8 +749,8 @@ static bool read_tag_mft2(const skcms_ICCTag* tag, skcms_A2B* a2b) {
return false;
}
return init_a2b_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft2_Layout), 2,
input_table_entries, output_table_entries, a2b);
return init_tables(mftTag->variable, tag->size - SAFE_FIXED_SIZE(mft2_Layout), 2,
input_table_entries, output_table_entries, out);
}
static bool read_curves(const uint8_t* buf, uint32_t size, uint32_t curve_offset,
@ -750,6 +780,7 @@ static bool read_curves(const uint8_t* buf, uint32_t size, uint32_t curve_offset
return true;
}
// mAB and mBA tags use the same encoding, including color lookup tables.
typedef struct {
uint8_t type [ 4];
uint8_t reserved_a [ 4];
@ -761,21 +792,21 @@ typedef struct {
uint8_t m_curve_offset [ 4];
uint8_t clut_offset [ 4];
uint8_t a_curve_offset [ 4];
} mAB_Layout;
} mAB_or_mBA_Layout;
typedef struct {
uint8_t grid_points [16];
uint8_t grid_byte_width [ 1];
uint8_t reserved [ 3];
uint8_t variable [1/*variable*/];
} mABCLUT_Layout;
} CLUT_Layout;
static bool read_tag_mab(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xyz) {
if (tag->size < SAFE_SIZEOF(mAB_Layout)) {
if (tag->size < SAFE_SIZEOF(mAB_or_mBA_Layout)) {
return false;
}
const mAB_Layout* mABTag = (const mAB_Layout*)tag->buf;
const mAB_or_mBA_Layout* mABTag = (const mAB_or_mBA_Layout*)tag->buf;
a2b->input_channels = mABTag->input_channels[0];
a2b->output_channels = mABTag->output_channels[0];
@ -820,11 +851,11 @@ static bool read_tag_mab(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xy
if (tag->size < matrix_offset + 12 * SAFE_SIZEOF(uint32_t)) {
return false;
}
float encoding_factor = pcs_is_xyz ? 65535 / 32768.0f : 1.0f;
float encoding_factor = pcs_is_xyz ? (65535 / 32768.0f) : 1.0f;
const uint8_t* mtx_buf = tag->buf + matrix_offset;
a2b->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf + 0);
a2b->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf + 4);
a2b->matrix.vals[0][2] = encoding_factor * read_big_fixed(mtx_buf + 8);
a2b->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf + 0);
a2b->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf + 4);
a2b->matrix.vals[0][2] = encoding_factor * read_big_fixed(mtx_buf + 8);
a2b->matrix.vals[1][0] = encoding_factor * read_big_fixed(mtx_buf + 12);
a2b->matrix.vals[1][1] = encoding_factor * read_big_fixed(mtx_buf + 16);
a2b->matrix.vals[1][2] = encoding_factor * read_big_fixed(mtx_buf + 20);
@ -851,10 +882,10 @@ static bool read_tag_mab(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xy
return false;
}
if (tag->size < clut_offset + SAFE_FIXED_SIZE(mABCLUT_Layout)) {
if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout)) {
return false;
}
const mABCLUT_Layout* clut = (const mABCLUT_Layout*)(tag->buf + clut_offset);
const CLUT_Layout* clut = (const CLUT_Layout*)(tag->buf + clut_offset);
if (clut->grid_byte_width[0] == 1) {
a2b->grid_8 = clut->variable;
@ -866,7 +897,7 @@ static bool read_tag_mab(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xy
return false;
}
uint64_t grid_size = a2b->output_channels * clut->grid_byte_width[0];
uint64_t grid_size = a2b->output_channels * clut->grid_byte_width[0]; // the payload
for (uint32_t i = 0; i < a2b->input_channels; ++i) {
a2b->grid_points[i] = clut->grid_points[i];
// The grid only makes sense with at least two points along each axis
@ -875,7 +906,7 @@ static bool read_tag_mab(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xy
}
grid_size *= a2b->grid_points[i];
}
if (tag->size < clut_offset + SAFE_FIXED_SIZE(mABCLUT_Layout) + grid_size) {
if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout) + grid_size) {
return false;
}
} else {
@ -895,6 +926,129 @@ static bool read_tag_mab(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xy
return true;
}
// Exactly the same as read_tag_mab(), except where there are comments.
// TODO: refactor the two to eliminate common code?
static bool read_tag_mba(const skcms_ICCTag* tag, skcms_B2A* b2a, bool pcs_is_xyz) {
if (tag->size < SAFE_SIZEOF(mAB_or_mBA_Layout)) {
return false;
}
const mAB_or_mBA_Layout* mBATag = (const mAB_or_mBA_Layout*)tag->buf;
b2a->input_channels = mBATag->input_channels[0];
b2a->output_channels = mBATag->output_channels[0];
// Input and output arity requirements swapped... |inputs|==3, |outputs|<=4.
if (b2a->input_channels != ARRAY_COUNT(b2a->input_curves)) {
return false;
}
if (b2a->output_channels > ARRAY_COUNT(b2a->output_curves)) {
return false;
}
uint32_t b_curve_offset = read_big_u32(mBATag->b_curve_offset);
uint32_t matrix_offset = read_big_u32(mBATag->matrix_offset);
uint32_t m_curve_offset = read_big_u32(mBATag->m_curve_offset);
uint32_t clut_offset = read_big_u32(mBATag->clut_offset);
uint32_t a_curve_offset = read_big_u32(mBATag->a_curve_offset);
if (0 == b_curve_offset) {
return false;
}
// "B" curves are our inputs, not outputs.
if (!read_curves(tag->buf, tag->size, b_curve_offset, b2a->input_channels,
b2a->input_curves)) {
return false;
}
if (0 != m_curve_offset) {
if (0 == matrix_offset) {
return false;
}
// Matrix channels is tied to input_channels (3), not output_channels (1-4).
b2a->matrix_channels = b2a->input_channels;
if (!read_curves(tag->buf, tag->size, m_curve_offset, b2a->matrix_channels,
b2a->matrix_curves)) {
return false;
}
if (tag->size < matrix_offset + 12 * SAFE_SIZEOF(uint32_t)) {
return false;
}
float encoding_factor = pcs_is_xyz ? (65535 / 32768.0f) : 1.0f;
const uint8_t* mtx_buf = tag->buf + matrix_offset;
b2a->matrix.vals[0][0] = encoding_factor * read_big_fixed(mtx_buf + 0);
b2a->matrix.vals[0][1] = encoding_factor * read_big_fixed(mtx_buf + 4);
b2a->matrix.vals[0][2] = encoding_factor * read_big_fixed(mtx_buf + 8);
b2a->matrix.vals[1][0] = encoding_factor * read_big_fixed(mtx_buf + 12);
b2a->matrix.vals[1][1] = encoding_factor * read_big_fixed(mtx_buf + 16);
b2a->matrix.vals[1][2] = encoding_factor * read_big_fixed(mtx_buf + 20);
b2a->matrix.vals[2][0] = encoding_factor * read_big_fixed(mtx_buf + 24);
b2a->matrix.vals[2][1] = encoding_factor * read_big_fixed(mtx_buf + 28);
b2a->matrix.vals[2][2] = encoding_factor * read_big_fixed(mtx_buf + 32);
b2a->matrix.vals[0][3] = encoding_factor * read_big_fixed(mtx_buf + 36);
b2a->matrix.vals[1][3] = encoding_factor * read_big_fixed(mtx_buf + 40);
b2a->matrix.vals[2][3] = encoding_factor * read_big_fixed(mtx_buf + 44);
} else {
if (0 != matrix_offset) {
return false;
}
b2a->matrix_channels = 0;
}
if (0 != a_curve_offset) {
if (0 == clut_offset) {
return false;
}
// "A" curves are our output, not input.
if (!read_curves(tag->buf, tag->size, a_curve_offset, b2a->output_channels,
b2a->output_curves)) {
return false;
}
if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout)) {
return false;
}
const CLUT_Layout* clut = (const CLUT_Layout*)(tag->buf + clut_offset);
if (clut->grid_byte_width[0] == 1) {
b2a->grid_8 = clut->variable;
b2a->grid_16 = nullptr;
} else if (clut->grid_byte_width[0] == 2) {
b2a->grid_8 = nullptr;
b2a->grid_16 = clut->variable;
} else {
return false;
}
uint64_t grid_size = b2a->output_channels * clut->grid_byte_width[0];
for (uint32_t i = 0; i < b2a->input_channels; ++i) {
b2a->grid_points[i] = clut->grid_points[i];
if (b2a->grid_points[i] < 2) {
return false;
}
grid_size *= b2a->grid_points[i];
}
if (tag->size < clut_offset + SAFE_FIXED_SIZE(CLUT_Layout) + grid_size) {
return false;
}
} else {
if (0 != clut_offset) {
return false;
}
if (b2a->input_channels != b2a->output_channels) {
return false;
}
b2a->input_channels = 0;
}
return true;
}
// If you pass f, we'll fit a possibly-non-zero value for *f.
// If you pass nullptr, we'll assume you want *f to be treated as zero.
static int fit_linear(const skcms_Curve* curve, int N, float tol,
@ -949,51 +1103,70 @@ static int fit_linear(const skcms_Curve* curve, int N, float tol,
return lin_points;
}
// If this skcms_Curve holds an identity table, rewrite it as an identity skcms_TransferFunction.
static void canonicalize_identity(skcms_Curve* curve) {
if (curve->table_entries && curve->table_entries <= (uint32_t)INT_MAX) {
int N = (int)curve->table_entries;
float c = 0.0f, d = 0.0f, f = 0.0f;
if (N == fit_linear(curve, N, 1.0f/(2*N), &c,&d,&f)
&& c == 1.0f
&& f == 0.0f) {
curve->table_entries = 0;
curve->table_8 = nullptr;
curve->table_16 = nullptr;
curve->parametric = skcms_TransferFunction{1,1,0,0,0,0,0};
}
}
}
static bool read_a2b(const skcms_ICCTag* tag, skcms_A2B* a2b, bool pcs_is_xyz) {
bool ok = false;
if (tag->type == skcms_Signature_mft1) {
ok = read_tag_mft1(tag, a2b);
} else if (tag->type == skcms_Signature_mft2) {
ok = read_tag_mft2(tag, a2b);
} else if (tag->type == skcms_Signature_mAB) {
ok = read_tag_mab(tag, a2b, pcs_is_xyz);
}
if (tag->type == skcms_Signature_mft1) { ok = read_tag_mft1(tag, a2b); }
if (tag->type == skcms_Signature_mft2) { ok = read_tag_mft2(tag, a2b); }
if (tag->type == skcms_Signature_mAB ) { ok = read_tag_mab(tag, a2b, pcs_is_xyz); }
if (!ok) {
return false;
}
// Detect and canonicalize identity tables.
skcms_Curve* curves[] = {
a2b->input_channels > 0 ? a2b->input_curves + 0 : nullptr,
a2b->input_channels > 1 ? a2b->input_curves + 1 : nullptr,
a2b->input_channels > 2 ? a2b->input_curves + 2 : nullptr,
a2b->input_channels > 3 ? a2b->input_curves + 3 : nullptr,
a2b->matrix_channels > 0 ? a2b->matrix_curves + 0 : nullptr,
a2b->matrix_channels > 1 ? a2b->matrix_curves + 1 : nullptr,
a2b->matrix_channels > 2 ? a2b->matrix_curves + 2 : nullptr,
a2b->output_channels > 0 ? a2b->output_curves + 0 : nullptr,
a2b->output_channels > 1 ? a2b->output_curves + 1 : nullptr,
a2b->output_channels > 2 ? a2b->output_curves + 2 : nullptr,
};
if (a2b->input_channels > 0) { canonicalize_identity(a2b->input_curves + 0); }
if (a2b->input_channels > 1) { canonicalize_identity(a2b->input_curves + 1); }
if (a2b->input_channels > 2) { canonicalize_identity(a2b->input_curves + 2); }
if (a2b->input_channels > 3) { canonicalize_identity(a2b->input_curves + 3); }
for (int i = 0; i < ARRAY_COUNT(curves); i++) {
skcms_Curve* curve = curves[i];
if (a2b->matrix_channels > 0) { canonicalize_identity(a2b->matrix_curves + 0); }
if (a2b->matrix_channels > 1) { canonicalize_identity(a2b->matrix_curves + 1); }
if (a2b->matrix_channels > 2) { canonicalize_identity(a2b->matrix_curves + 2); }
if (curve && curve->table_entries && curve->table_entries <= (uint32_t)INT_MAX) {
int N = (int)curve->table_entries;
if (a2b->output_channels > 0) { canonicalize_identity(a2b->output_curves + 0); }
if (a2b->output_channels > 1) { canonicalize_identity(a2b->output_curves + 1); }
if (a2b->output_channels > 2) { canonicalize_identity(a2b->output_curves + 2); }
float c = 0.0f, d = 0.0f, f = 0.0f;
if (N == fit_linear(curve, N, 1.0f/(2*N), &c,&d,&f)
&& c == 1.0f
&& f == 0.0f) {
curve->table_entries = 0;
curve->table_8 = nullptr;
curve->table_16 = nullptr;
curve->parametric = skcms_TransferFunction{1,1,0,0,0,0,0};
}
}
return true;
}
static bool read_b2a(const skcms_ICCTag* tag, skcms_B2A* b2a, bool pcs_is_xyz) {
bool ok = false;
if (tag->type == skcms_Signature_mft1) { ok = read_tag_mft1(tag, b2a); }
if (tag->type == skcms_Signature_mft2) { ok = read_tag_mft2(tag, b2a); }
if (tag->type == skcms_Signature_mBA ) { ok = read_tag_mba(tag, b2a, pcs_is_xyz); }
if (!ok) {
return false;
}
if (b2a->input_channels > 0) { canonicalize_identity(b2a->input_curves + 0); }
if (b2a->input_channels > 1) { canonicalize_identity(b2a->input_curves + 1); }
if (b2a->input_channels > 2) { canonicalize_identity(b2a->input_curves + 2); }
if (b2a->matrix_channels > 0) { canonicalize_identity(b2a->matrix_curves + 0); }
if (b2a->matrix_channels > 1) { canonicalize_identity(b2a->matrix_curves + 1); }
if (b2a->matrix_channels > 2) { canonicalize_identity(b2a->matrix_curves + 2); }
if (b2a->output_channels > 0) { canonicalize_identity(b2a->output_curves + 0); }
if (b2a->output_channels > 1) { canonicalize_identity(b2a->output_curves + 1); }
if (b2a->output_channels > 2) { canonicalize_identity(b2a->output_curves + 2); }
if (b2a->output_channels > 3) { canonicalize_identity(b2a->output_curves + 3); }
return true;
}
@ -1132,16 +1305,15 @@ bool skcms_ParseWithA2BPriority(const void* buf, size_t len,
}
}
skcms_ICCTag a2b_tag;
for (int i = 0; i < priorities; i++) {
// enum { perceptual, relative_colormetric, saturation }
if (priority[i] < 0 || priority[i] > 2) {
return false;
}
uint32_t sig = skcms_Signature_A2B0 + static_cast<uint32_t>(priority[i]);
if (skcms_GetTagBySignature(profile, sig, &a2b_tag)) {
if (!read_a2b(&a2b_tag, &profile->A2B, pcs_is_xyz)) {
skcms_ICCTag tag;
if (skcms_GetTagBySignature(profile, sig, &tag)) {
if (!read_a2b(&tag, &profile->A2B, pcs_is_xyz)) {
// Malformed A2B tag
return false;
}
@ -1150,6 +1322,23 @@ bool skcms_ParseWithA2BPriority(const void* buf, size_t len,
}
}
for (int i = 0; i < priorities; i++) {
// enum { perceptual, relative_colormetric, saturation }
if (priority[i] < 0 || priority[i] > 2) {
return false;
}
uint32_t sig = skcms_Signature_B2A0 + static_cast<uint32_t>(priority[i]);
skcms_ICCTag tag;
if (skcms_GetTagBySignature(profile, sig, &tag)) {
if (!read_b2a(&tag, &profile->B2A, pcs_is_xyz)) {
// Malformed B2A tag
return false;
}
profile->has_B2A = true;
break;
}
}
return usable_as_src(profile);
}
@ -1179,7 +1368,7 @@ const skcms_ICCProfile* skcms_sRGB_profile() {
{ 0.013916016f, 0.097076416f, 0.714096069f },
}},
false, // has_A2B, followed by a2b itself which we don't care about.
false, // has_A2B, followed by A2B itself, which we don't care about.
{
0,
{
@ -1211,6 +1400,39 @@ const skcms_ICCProfile* skcms_sRGB_profile() {
{{0, {0,0, 0,0,0,0,0}}},
},
},
false, // has_B2A, followed by B2A itself, which we also don't care about.
{
0,
{
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
},
0,
{{
{ 0,0,0,0 },
{ 0,0,0,0 },
{ 0,0,0,0 },
}},
{
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
},
0,
{0,0,0,0},
nullptr,
nullptr,
{
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
},
},
};
return &sRGB_profile;
}
@ -1239,7 +1461,7 @@ const skcms_ICCProfile* skcms_XYZD50_profile() {
{ 0,0,1 },
}},
false, // has_A2B, followed by a2b itself which we don't care about.
false, // has_A2B, followed by A2B itself, which we don't care about.
{
0,
{
@ -1271,6 +1493,39 @@ const skcms_ICCProfile* skcms_XYZD50_profile() {
{{0, {0,0, 0,0,0,0,0}}},
},
},
false, // has_B2A, followed by B2A itself, which we also don't care about.
{
0,
{
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
},
0,
{{
{ 0,0,0,0 },
{ 0,0,0,0 },
{ 0,0,0,0 },
}},
{
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
},
0,
{0,0,0,0},
nullptr,
nullptr,
{
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
{{0, {0,0, 0,0,0,0,0}}},
},
},
};
return &XYZD50_profile;

View File

@ -1 +1 @@
b9593d4e39ea3dbf1ba5e894f0cefc1cb1568437
4e8691e281941320a5644d50c566c2d4394cf1c8