1ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein/*
2ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein * Copyright 2016 Google Inc.
3ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein *
4ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein * Use of this source code is governed by a BSD-style license that can be
5ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein * found in the LICENSE file.
6ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein */
7ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein
8ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein#ifndef SkSRGB_DEFINED
9ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein#define SkSRGB_DEFINED
10ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein
11ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein#include "SkNx.h"
12ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein
13ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein/** Components for building our canonical sRGB -> linear and linear -> sRGB transformations.
14ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein *
15ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein *  Current best practices:
16ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein *      - for sRGB -> linear, lookup R,G,B in sk_linear_from_srgb;
17566ea9b9fc6746ffad390a4029e56d985eb2aec8mtklein *      - for linear -> sRGB, call sk_linear_to_srgb() for R,G,B;
18ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein *      - the alpha channel is linear in both formats, needing at most *(1/255.0f) or *255.0f.
19ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein *
20ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein *  sk_linear_to_srgb() will run a little faster than usual when compiled with SSE4.1+.
21ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein */
22ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein
2308541e8d7f28d875f5a6238aea1bae871e4aa276Matt Sarettextern const float    sk_linear_from_srgb[256];
2408541e8d7f28d875f5a6238aea1bae871e4aa276Matt Sarettextern const uint16_t sk_linear12_from_srgb[256];
2508541e8d7f28d875f5a6238aea1bae871e4aa276Matt Sarettextern const uint8_t  sk_linear12_to_srgb[4096];
26ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein
27f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein// [0.0f, 1.0f] -> [0, 255].
28f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Kleinstatic inline Sk4i sk_linear_to_srgb(const Sk4f& x) {
29ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein    // Approximation of the sRGB gamma curve (within 1 when scaled to 8-bit pixels).
30566ea9b9fc6746ffad390a4029e56d985eb2aec8mtklein    //
31b5acf6e702e318b405a04d38bfdac602dc3ed773mtklein    // Constants tuned by brute force to minimize (in order of importance) after truncation:
32b5acf6e702e318b405a04d38bfdac602dc3ed773mtklein    //    1) the number of bytes that fail to round trip (0 of 256);
33b5acf6e702e318b405a04d38bfdac602dc3ed773mtklein    //    2) the number of points in [FLT_MIN, 1.0f] that are non-monotonic (0 of ~1 billion);
34b5acf6e702e318b405a04d38bfdac602dc3ed773mtklein    //    3) the number of points halfway between bytes that hit the wrong byte (131 of 255).
35ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein    auto rsqrt = x.rsqrt(),
36ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein         sqrt  = rsqrt.invert(),
37ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein         ftrt  = rsqrt.rsqrt();
38ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein
39566ea9b9fc6746ffad390a4029e56d985eb2aec8mtklein    auto lo = (13.0471f * 255.0f) * x;
40ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein
41f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein    auto hi = SkNx_fma(Sk4f{+0.412999f  * 255.0f}, ftrt,
42f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein              SkNx_fma(Sk4f{+0.687999f  * 255.0f}, sqrt,
43f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein                       Sk4f{-0.0974983f * 255.0f}));
44f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein    auto s = (x < 0.0048f).thenElse(lo, hi);
45f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein
46f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein    // Now clamp and truncate.
47f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein    // The order of the arguments is important here.  We want to make sure that NaN
48f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein    // clamps to zero.  Note that max(NaN, 0) = 0, while max(0, NaN) = NaN.
49f0429db88cd0846c2da2b2f31ee241a79f58ba1eMike Klein    return SkNx_cast<int>(Sk4f::Min(Sk4f::Max(s, 0.0f), 255.0f));
50b5acf6e702e318b405a04d38bfdac602dc3ed773mtklein}
51b5acf6e702e318b405a04d38bfdac602dc3ed773mtklein
52ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein#endif//SkSRGB_DEFINED
53