blob: de078a5528d6834338a262917beccd18d7b92225 [file] [log] [blame]
mtkleinac41bac2016-07-08 06:33:16 -07001/*
2 * Copyright 2016 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#ifndef SkSRGB_DEFINED
9#define SkSRGB_DEFINED
10
11#include "SkNx.h"
12
13/** Components for building our canonical sRGB -> linear and linear -> sRGB transformations.
14 *
15 * Current best practices:
16 * - for sRGB -> linear, lookup R,G,B in sk_linear_from_srgb;
mtklein566ea9b2016-07-20 12:10:11 -070017 * - for linear -> sRGB, call sk_linear_to_srgb() for R,G,B;
mtkleinac41bac2016-07-08 06:33:16 -070018 * - the alpha channel is linear in both formats, needing at most *(1/255.0f) or *255.0f.
19 *
mtkleinac41bac2016-07-08 06:33:16 -070020 * sk_linear_to_srgb() will run a little faster than usual when compiled with SSE4.1+.
21 */
22
Matt Sarett08541e82017-03-08 16:30:18 -050023extern const float sk_linear_from_srgb[256];
24extern const uint16_t sk_linear12_from_srgb[256];
25extern const uint8_t sk_linear12_to_srgb[4096];
mtkleinac41bac2016-07-08 06:33:16 -070026
Mike Kleinf0429db2017-11-01 11:52:10 -040027// [0.0f, 1.0f] -> [0, 255].
28static inline Sk4i sk_linear_to_srgb(const Sk4f& x) {
mtkleinac41bac2016-07-08 06:33:16 -070029 // Approximation of the sRGB gamma curve (within 1 when scaled to 8-bit pixels).
mtklein566ea9b2016-07-20 12:10:11 -070030 //
mtkleinb5acf6e2016-07-25 06:13:47 -070031 // Constants tuned by brute force to minimize (in order of importance) after truncation:
32 // 1) the number of bytes that fail to round trip (0 of 256);
33 // 2) the number of points in [FLT_MIN, 1.0f] that are non-monotonic (0 of ~1 billion);
34 // 3) the number of points halfway between bytes that hit the wrong byte (131 of 255).
mtkleinac41bac2016-07-08 06:33:16 -070035 auto rsqrt = x.rsqrt(),
36 sqrt = rsqrt.invert(),
37 ftrt = rsqrt.rsqrt();
38
mtklein566ea9b2016-07-20 12:10:11 -070039 auto lo = (13.0471f * 255.0f) * x;
mtkleinac41bac2016-07-08 06:33:16 -070040
Mike Kleinf0429db2017-11-01 11:52:10 -040041 auto hi = SkNx_fma(Sk4f{+0.412999f * 255.0f}, ftrt,
42 SkNx_fma(Sk4f{+0.687999f * 255.0f}, sqrt,
43 Sk4f{-0.0974983f * 255.0f}));
44 auto s = (x < 0.0048f).thenElse(lo, hi);
45
46 // Now clamp and truncate.
47 // The order of the arguments is important here. We want to make sure that NaN
48 // clamps to zero. Note that max(NaN, 0) = 0, while max(0, NaN) = NaN.
49 return SkNx_cast<int>(Sk4f::Min(Sk4f::Max(s, 0.0f), 255.0f));
mtkleinb5acf6e2016-07-25 06:13:47 -070050}
mtkleinac41bac2016-07-08 06:33:16 -070051
mtkleinac41bac2016-07-08 06:33:16 -070052#endif//SkSRGB_DEFINED