blob: a2a66c607ae4c730a023728b3243356f89369564 [file] [log] [blame]
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <assert.h>
#include <math.h>
#include <string.h>
static uint32_t skcms_make_signature(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
return (uint32_t)(a << 24)
| (uint32_t)(b << 16)
| (uint32_t)(c << 8)
| (uint32_t)(d << 0);
}
static uint16_t read_big_u16(const uint8_t* ptr) {
return (uint16_t)(ptr[0] << 8)
| (uint16_t)(ptr[1] << 0);
}
static uint32_t read_big_u32(const uint8_t* ptr) {
return (uint32_t)(ptr[0] << 24)
| (uint32_t)(ptr[1] << 16)
| (uint32_t)(ptr[2] << 8)
| (uint32_t)(ptr[3] << 0);
}
static int32_t read_big_i32(const uint8_t* ptr) {
return (int32_t)read_big_u32(ptr);
}
static uint64_t read_big_u64(const uint8_t* ptr) {
uint64_t hi = read_big_u32(ptr);
uint64_t lo = read_big_u32(ptr + 4);
return hi << 32 | lo;
}
static float read_big_fixed(const uint8_t* ptr) {
return read_big_i32(ptr) * (1.0f / 65536.0f);
}
static skcms_ICCDateTime read_big_date_time(const uint8_t* ptr) {
skcms_ICCDateTime date_time;
date_time.year = read_big_u16(ptr + 0);
date_time.month = read_big_u16(ptr + 2);
date_time.day = read_big_u16(ptr + 4);
date_time.hour = read_big_u16(ptr + 6);
date_time.minute = read_big_u16(ptr + 8);
date_time.second = read_big_u16(ptr + 10);
return date_time;
}
// Maps to an in-memory profile so that fields line up to the locations specified
// in ICC.1:2010, section 7.2
typedef struct {
uint8_t size [ 4];
uint8_t cmm_type [ 4];
uint8_t version [ 4];
uint8_t profile_class [ 4];
uint8_t data_color_space [ 4];
uint8_t pcs [ 4];
uint8_t creation_date_time [12];
uint8_t signature [ 4];
uint8_t platform [ 4];
uint8_t flags [ 4];
uint8_t device_manufacturer [ 4];
uint8_t device_model [ 4];
uint8_t device_attributes [ 8];
uint8_t rendering_intent [ 4];
uint8_t illuminant_X [ 4];
uint8_t illuminant_Y [ 4];
uint8_t illuminant_Z [ 4];
uint8_t creator [ 4];
uint8_t profile_id [16];
uint8_t reserved [28];
uint8_t tag_count [ 4]; // Technically not part of header, but required
} skcms_ICCHeader;
typedef struct {
uint8_t signature [4];
uint8_t offset [4];
uint8_t size [4];
} skcms_ICCTag_Layout;
static const skcms_ICCTag_Layout* skcms_ICCProfile_getTagTable(const skcms_ICCProfile* profile) {
return (const skcms_ICCTag_Layout*)(profile->buffer + sizeof(skcms_ICCHeader));
}
bool skcms_ICCProfile_parse(skcms_ICCProfile* profile,
const void* buf,
size_t len) {
static_assert(sizeof(skcms_ICCHeader) == 132, "ICC header size");
if (!profile) {
return false;
}
memset(profile, 0, sizeof(*profile));
if (len < sizeof(skcms_ICCHeader)) {
return false;
}
// Byte-swap all header fields
const skcms_ICCHeader* header = buf;
profile->buffer = buf;
profile->size = read_big_u32(header->size);
profile->cmm_type = read_big_u32(header->cmm_type);
profile->version = read_big_u32(header->version);
profile->profile_class = read_big_u32(header->profile_class);
profile->data_color_space = read_big_u32(header->data_color_space);
profile->pcs = read_big_u32(header->pcs);
profile->creation_date_time = read_big_date_time(header->creation_date_time);
profile->signature = read_big_u32(header->signature);
profile->platform = read_big_u32(header->platform);
profile->flags = read_big_u32(header->flags);
profile->device_manufacturer = read_big_u32(header->device_manufacturer);
profile->device_model = read_big_u32(header->device_model);
profile->device_attributes = read_big_u64(header->device_attributes);
profile->rendering_intent = read_big_u32(header->rendering_intent);
profile->illuminant_X = read_big_fixed(header->illuminant_X);
profile->illuminant_Y = read_big_fixed(header->illuminant_Y);
profile->illuminant_Z = read_big_fixed(header->illuminant_Z);
profile->creator = read_big_u32(header->creator);
static_assert(sizeof(profile->profile_id) == sizeof(header->profile_id), "profile_id size");
memcpy(profile->profile_id, header->profile_id, sizeof(header->profile_id));
profile->tag_count = read_big_u32(header->tag_count);
// Validate signature, size (smaller than buffer, large enough to hold tag table),
// and major version
if (profile->signature != skcms_make_signature('a', 'c', 's', 'p') ||
profile->size > len ||
profile->size < sizeof(skcms_ICCHeader) + profile->tag_count*sizeof(skcms_ICCTag_Layout) ||
(profile->version >> 24) > 4) {
return false;
}
// Validate that illuminant is D50 white
if (fabsf(profile->illuminant_X - 0.9642f) > 0.0100f ||
fabsf(profile->illuminant_Y - 1.0000f) > 0.0100f ||
fabsf(profile->illuminant_Z - 0.8249f) > 0.0100f) {
return false;
}
// Validate that all tag entries have sane offset + size
const skcms_ICCTag_Layout* tags = skcms_ICCProfile_getTagTable(profile);
for (uint32_t i = 0; i < profile->tag_count; ++i) {
uint32_t tag_offset = read_big_u32(tags[i].offset);
uint32_t tag_size = read_big_u32(tags[i].size);
uint64_t tag_end = (uint64_t)tag_offset + (uint64_t)tag_size;
if (tag_end > profile->size) {
return false;
}
}
return true;
}
bool skcms_ICCProfile_toXYZD50(const skcms_ICCProfile* profile,
skcms_Matrix3x3* toXYZD50) {
(void)profile;
(void)toXYZD50;
return false;
}
bool skcms_ICCProfile_getTransferFunction(const skcms_ICCProfile* profile,
skcms_TransferFunction* transferFunction) {
(void)profile;
(void)transferFunction;
return false;
}
void skcms_ICCProfile_getTagByIndex(const skcms_ICCProfile* profile,
uint32_t index,
skcms_ICCTag* tag) {
if (!profile || !profile->buffer || !tag) { return; }
if (index > profile->tag_count) { return; }
const skcms_ICCTag_Layout* tags = skcms_ICCProfile_getTagTable(profile);
tag->signature = read_big_u32(tags[index].signature);
tag->size = read_big_u32(tags[index].size);
tag->buf = read_big_u32(tags[index].offset) + profile->buffer;
}
bool skcms_ICCProfile_getTagBySignature(const skcms_ICCProfile* profile,
uint32_t signature,
skcms_ICCTag* tag) {
if (!profile || !profile->buffer || !tag) { return false; }
const skcms_ICCTag_Layout* tags = skcms_ICCProfile_getTagTable(profile);
for (uint32_t i = 0; i < profile->tag_count; ++i) {
if (read_big_u32(tags[i].signature) == signature) {
tag->signature = signature;
tag->size = read_big_u32(tags[i].size);
tag->buf = read_big_u32(tags[i].offset) + profile->buffer;
return true;
}
}
return false;
}