34constexpr size_t kNumChannels = 3;
37constexpr float kD50_x = 0.9642f;
38constexpr float kD50_y = 1.0000f;
39constexpr float kD50_z = 0.8249f;
45SkFixed float_round_to_fixed(
float x) {
50uint16_t float_to_uInt16Number(
float x, uint16_t one) {
52 if (
x > one)
return one;
54 return static_cast<uint16_t
>(
x);
58constexpr uint16_t kOne16CurveType = 0xFFFF;
62constexpr uint16_t kOne16XYZ = 0x8000;
69 uint32_t cmm_type = 0;
87 uint16_t creation_date_hours = 0;
88 uint16_t creation_date_minutes = 0;
89 uint16_t creation_date_seconds = 0;
95 uint32_t platform = 0;
98 uint32_t
flags = 0x00000000;
101 uint32_t device_manufacturer = 0;
104 uint32_t device_model = 0;
107 uint8_t device_attributes[8] = {0};
118 uint32_t creator = 0;
121 uint8_t profile_id[16] = {0};
124 uint8_t reserved[28] = {0};
127 uint32_t tag_count = 0;
145 for (
int i = 0;
i < 3; ++
i) {
146 for (
int j = 0; j < 3; ++j) {
150 for (
int i = 0;
i < 3; ++
i) {
164 static constexpr float kTolerance = 1.0f / (1 << 11);
180 for (
int r = 0; r < 3; r++) {
181 for (
int c = 0; c < 3; c++) {
190constexpr uint32_t kCICPPrimariesSRGB = 1;
191constexpr uint32_t kCICPPrimariesP3 = 12;
192constexpr uint32_t kCICPPrimariesRec2020 = 9;
196 return kCICPPrimariesSRGB;
198 return kCICPPrimariesP3;
200 return kCICPPrimariesRec2020;
205constexpr uint32_t kCICPTrfnSRGB = 1;
206constexpr uint32_t kCICPTrfn2Dot2 = 4;
207constexpr uint32_t kCICPTrfnLinear = 8;
208constexpr uint32_t kCICPTrfnPQ = 16;
209constexpr uint32_t kCICPTrfnHLG = 18;
217 return kCICPTrfnSRGB;
219 return kCICPTrfn2Dot2;
221 return kCICPTrfnLinear;
239 const uint32_t cicp_trfn = get_cicp_trfn(fn);
240 const uint32_t cicp_primaries = get_cicp_primaries(toXYZD50);
243 if (cicp_trfn == kCICPPrimariesSRGB && cicp_primaries == kCICPTrfnSRGB) {
248 if (cicp_primaries && cicp_trfn) {
250 switch (cicp_primaries) {
251 case kCICPPrimariesSRGB:
254 case kCICPPrimariesP3:
257 case kCICPPrimariesRec2020:
269 case kCICPTrfnLinear:
291 md5.write(&toXYZD50,
sizeof(toXYZD50));
292 md5.write(&fn,
sizeof(fn));
298 uint32_t text_length = strlen(
text);
310 for (
size_t i = 0;
i < text_length;
i++) {
316 return s.detachAsData();
328 return s.detachAsData();
331constexpr float kToneMapInputMax = 1000.f / 203.f;
332constexpr float kToneMapOutputMax = 1.f;
335float tone_map_gain(
float x) {
339 constexpr float kToneMapA = kToneMapOutputMax / (kToneMapInputMax * kToneMapInputMax);
340 constexpr float kToneMapB = 1.f / kToneMapOutputMax;
341 return (1.f + kToneMapA *
x) / (1.f + kToneMapB *
x);
345float tone_map_inverse(
float y) {
346 constexpr float kToneMapA = kToneMapOutputMax / (kToneMapInputMax * kToneMapInputMax);
347 constexpr float kToneMapB = 1.f / kToneMapOutputMax;
350 const float a = kToneMapA;
351 const float b = (1 - kToneMapB *
y);
353 const float discriminant =
b *
b - 4.f *
a * c;
354 if (discriminant < 0.f) {
357 return (-
b + sqrtf(discriminant)) / (2.f *
a);
366 x *= std::pow(
x, 0.2);
374 x *= kToneMapInputMax;
386 uint16_t
value =
reinterpret_cast<const uint16_t*
>(trc.
table_16)[
i];
394 if (fn.
a == 1.f && fn.
b == 0.f && fn.
c == 0.f && fn.
d == 0.f && fn.
e == 0.f &&
412 return s.detachAsData();
415sk_sp<SkData> write_clut(
const uint8_t* grid_points,
const uint8_t* grid_16) {
417 for (
size_t i = 0;
i < 16; ++
i) {
418 s.write8(
i < kNumChannels ? grid_points[
i] : 0);
425 uint32_t value_count = kNumChannels;
426 for (uint32_t
i = 0;
i < kNumChannels; ++
i) {
427 value_count *= grid_points[
i];
429 for (uint32_t
i = 0;
i < value_count; ++
i) {
430 uint16_t
value =
reinterpret_cast<const uint16_t*
>(grid_16)[
i];
434 return s.detachAsData();
441 const uint8_t* grid_points,
442 const uint8_t* grid_16,
448 size_t b_curves_offset =
offset;
451 for (
size_t i = 0;
i < kNumChannels; ++
i) {
452 b_curves_data[
i] = write_trc_tag(b_curves[
i]);
458 size_t clut_offset = 0;
463 clut = write_clut(grid_points, grid_16);
469 size_t a_curves_offset = 0;
475 for (
size_t i = 0;
i < kNumChannels; ++
i) {
476 a_curves_data[
i] = write_trc_tag(a_curves[
i]);
483 size_t matrix_offset = 0;
488 matrix_data = write_matrix(
matrix);
493 size_t m_curves_offset = 0;
498 for (
size_t i = 0;
i < kNumChannels; ++
i) {
499 m_curves_data[
i] = write_trc_tag(m_curves[
i]);
508 s.write8(kNumChannels);
509 s.write8(kNumChannels);
516 SkASSERT(
s.bytesWritten() == b_curves_offset);
517 for (
size_t i = 0;
i < kNumChannels; ++
i) {
518 s.write(b_curves_data[
i]->
data(), b_curves_data[
i]->
size());
521 SkASSERT(
s.bytesWritten() == clut_offset);
525 SkASSERT(
s.bytesWritten() == a_curves_offset);
526 for (
size_t i = 0;
i < kNumChannels; ++
i) {
527 s.write(a_curves_data[
i]->
data(), a_curves_data[
i]->
size());
531 SkASSERT(
s.bytesWritten() == matrix_offset);
532 s.write(matrix_data->
data(), matrix_data->
size());
535 SkASSERT(
s.bytesWritten() == m_curves_offset);
536 for (
size_t i = 0;
i < kNumChannels; ++
i) {
537 s.write(m_curves_data[
i]->
data(), m_curves_data[
i]->
size());
540 return s.detachAsData();
548 std::vector<std::pair<uint32_t, sk_sp<SkData>>> tags;
553 tags.emplace_back(
kTAG_rXYZ, write_xyz_tag(
m.vals[0][0],
m.vals[1][0],
m.vals[2][0]));
554 tags.emplace_back(
kTAG_gXYZ, write_xyz_tag(
m.vals[0][1],
m.vals[1][1],
m.vals[2][1]));
555 tags.emplace_back(
kTAG_bXYZ, write_xyz_tag(
m.vals[0][2],
m.vals[1][2],
m.vals[2][2]));
559 tags.emplace_back(
kTAG_wtpt, write_xyz_tag(kD50_x, kD50_y, kD50_z));
589 const auto& a2b =
profile->A2B;
590 SkASSERT(a2b.output_channels == kNumChannels);
593 a2b.input_channels ? a2b.input_curves :
nullptr,
594 a2b.input_channels ? a2b.grid_points :
nullptr,
595 a2b.input_channels ? a2b.grid_16 :
nullptr,
596 a2b.matrix_channels ? a2b.matrix_curves :
nullptr,
597 a2b.matrix_channels ? &a2b.matrix :
nullptr);
598 tags.emplace_back(
kTAG_A2B0, std::move(a2b_data));
603 const auto& b2a =
profile->B2A;
604 SkASSERT(b2a.input_channels == kNumChannels);
607 b2a.output_channels ? b2a.input_curves :
nullptr,
608 b2a.output_channels ? b2a.grid_points :
nullptr,
609 b2a.output_channels ? b2a.grid_16 :
nullptr,
610 b2a.matrix_channels ? b2a.matrix_curves :
nullptr,
611 b2a.matrix_channels ? &b2a.matrix :
nullptr);
612 tags.emplace_back(
kTAG_B2A0, std::move(b2a_data));
616 tags.emplace_back(
kTAG_cprt, write_text_tag(
"Google Inc. 2016"));
619 std::string generatedDesc;
622 for (
const auto& tag : tags) {
623 md5.write(&tag.first,
sizeof(tag.first));
624 md5.write(tag.second->bytes(), tag.second->size());
628 desc = generatedDesc.c_str();
634 size_t tag_data_size = 0;
635 for (
const auto& tag : tags) {
636 tag_data_size += tag.second->size();
639 size_t profile_size =
kICCHeaderSize + tag_table_size + tag_data_size;
648 uint8_t* ptr = (uint8_t*)profile_data.
get();
655 size_t last_tag_offset =
sizeof(
header) + tag_table_size;
656 size_t last_tag_size = 0;
657 for (
const auto& tag : tags) {
658 if (!tag.second->isEmpty()) {
659 last_tag_offset = last_tag_offset + last_tag_size;
660 last_tag_size = tag.second->size();
662 uint32_t tag_table_entry[3] = {
667 memcpy(ptr, tag_table_entry,
sizeof(tag_table_entry));
668 ptr +=
sizeof(tag_table_entry);
672 for (
const auto& tag : tags) {
673 if (tag.second->isEmpty())
continue;
674 memcpy(ptr, tag.second->data(), tag.second->size());
675 ptr += tag.second->size();
678 SkASSERT(profile_size ==
static_cast<size_t>(ptr - (uint8_t*)profile_data.
get()));
685 std::vector<uint16_t> trc_table;
686 std::vector<uint16_t> a2b_grid;
700 profile.trc[0].table_entries = 0;
701 profile.trc[0].parametric = fn;
709 constexpr uint32_t kTrcTableSize = 65;
710 trc_table.resize(kTrcTableSize);
711 for (uint32_t
i = 0;
i < kTrcTableSize; ++
i) {
712 float x =
i / (kTrcTableSize - 1.f);
713 x = hdr_trfn_eval(fn,
x);
714 x *= tone_map_gain(
x);
719 constexpr uint32_t kGridSize = 11;
720 a2b_grid.resize(kGridSize * kGridSize * kGridSize * kNumChannels);
721 size_t a2b_grid_index = 0;
722 for (uint32_t r_index = 0; r_index < kGridSize; ++r_index) {
723 for (uint32_t g_index = 0; g_index < kGridSize; ++g_index) {
724 for (uint32_t b_index = 0; b_index < kGridSize; ++b_index) {
726 r_index / (kGridSize - 1.f),
727 g_index / (kGridSize - 1.f),
728 b_index / (kGridSize - 1.f),
732 for (
auto& c : rgb) {
733 c = tone_map_inverse(c);
739 for (
auto& c : rgb) {
740 c /= kToneMapInputMax;
744 for (
auto& c : rgb) {
745 c = std::pow(c, 1 / 1.2);
749 float Y = 0.2627f * rgb[0] + 0.6780f * rgb[1] + 0.0593f * rgb[2];
750 for (
auto& c : rgb) {
751 c *= std::pow(
Y, 0.2);
755 for (
auto& c : rgb) {
756 c *= kToneMapInputMax;
763 for (
auto& c : rgb) {
764 c *= tone_map_gain(0.5 * (c + max_rgb));
770 for (
const auto& c : rgb) {
771 a2b_grid[a2b_grid_index++] =
780 profile.A2B.input_channels = kNumChannels;
781 profile.A2B.output_channels = kNumChannels;
782 profile.A2B.matrix_channels = kNumChannels;
783 for (
size_t i = 0;
i < kNumChannels; ++
i) {
784 profile.A2B.grid_points[
i] = kGridSize;
786 profile.A2B.input_curves[
i].table_entries = kTrcTableSize;
787 profile.A2B.input_curves[
i].table_16 =
reinterpret_cast<uint8_t*
>(trc_table.data());
792 for (
size_t j = 0; j < 3; ++j) {
795 profile.A2B.matrix.vals[
i][3] = 0.f;
797 profile.A2B.grid_16 =
reinterpret_cast<const uint8_t*
>(a2b_grid.data());
801 profile.B2A.input_channels = kNumChannels;
802 for (
size_t i = 0;
i < 3; ++
i) {
810 profile.CICP.color_primaries = get_cicp_primaries(toXYZD50);
811 profile.CICP.transfer_characteristics = get_cicp_trfn(fn);
812 profile.CICP.matrix_coefficients = 0;
813 profile.CICP.video_full_range_flag = 1;
818 std::string description = get_desc_string(fn, toXYZD50);
static SkMD5::Digest md5(const SkBitmap &bm)
static constexpr float kTolerance
#define SkEndian_SwapBE32(n)
#define SkEndian_SwapBE16(n)
static constexpr int sk_float_saturate2int(float x)
static constexpr uint32_t kTAG_mBAType
static constexpr uint32_t kTAG_desc
static constexpr uint32_t kTAG_gTRC
static constexpr uint32_t kTAG_TextType
static constexpr uint32_t kTAG_cprt
static constexpr uint32_t kXYZ_PCSSpace
static constexpr uint32_t kTAG_bTRC
static constexpr size_t kICCHeaderSize
static constexpr uint32_t kTAG_gXYZ
static constexpr uint32_t kTAG_wtpt
static constexpr uint32_t kTAG_A2B0
static constexpr uint32_t kACSP_Signature
static constexpr uint32_t kTAG_B2A0
static constexpr uint32_t kTAG_ParaCurveType
static constexpr size_t kICCTagTableEntrySize
static constexpr uint32_t kTAG_mABType
static constexpr uint32_t kTAG_bXYZ
static constexpr uint32_t kRGB_ColorSpace
static constexpr uint32_t kTAG_rXYZ
static constexpr uint32_t kDisplay_Profile
static constexpr uint32_t kTAG_rTRC
static constexpr uint32_t kTAG_cicp
@ kExponential_ParaCurveType
static constexpr uint32_t kTAG_CurveType
sk_sp< SkData > SkWriteICCProfile(const skcms_ICCProfile *profile, const char *desc)
bool SkWStreamWriteU32BE(SkWStream *s, uint32_t value)
bool SkWStreamWriteU16BE(SkWStream *s, uint16_t value)
static constexpr SkFourByteTag SkSetFourByteTag(char a, char b, char c, char d)
static sk_sp< SkData > MakeFromMalloc(const void *data, size_t length)
const void * data() const
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
static sk_sp< SkData > MakeEmpty()
const char * c_str() const
FlutterSemanticsFlag flags
static float max(float r, float g, float b)
static float min(float r, float g, float b)
static constexpr skcms_Matrix3x3 kSRGB
static constexpr skcms_Matrix3x3 kRec2020
static constexpr skcms_Matrix3x3 kDisplayP3
static constexpr skcms_TransferFunction k2Dot2
static constexpr skcms_TransferFunction kSRGB
static constexpr skcms_TransferFunction kHLG
static constexpr skcms_TransferFunction kPQ
static constexpr skcms_TransferFunction kLinear
unsigned useCenter Optional< SkMatrix > matrix
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
SIN Vec< N, float > floor(const Vec< N, float > &x)
static bool nearly_equal(SkColor4f x, SkColor4f y)
bool skcms_TransferFunction_isHLGish(const skcms_TransferFunction *tf)
skcms_TFType skcms_TransferFunction_getType(const skcms_TransferFunction *tf)
float skcms_TransferFunction_eval(const skcms_TransferFunction *tf, float x)
bool skcms_TransferFunction_isPQish(const skcms_TransferFunction *tf)
bool skcms_TransferFunction_isSRGBish(const skcms_TransferFunction *tf)
static const char header[]
SkString toHexString() const
uint8_t matrix_coefficients
uint8_t video_full_range_flag
uint8_t transfer_characteristics
std::shared_ptr< const fml::Mapping > data
skcms_TransferFunction parametric