Support sRGB 565.

It looks like I'm not going to be able to avoid supporting sRGB G8, I8, 565, 4444, 8888.
(A8 and F16 will always be linear.)  This fixes 565, and lays out the rest of the accum_*.

I did a little reorganization to keep things in ascending bit depth, just for sanity.

GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=5145
CQ_INCLUDE_TRYBOTS=master.client.skia:Test-Ubuntu-GCC-GCE-CPU-AVX2-x86_64-Release-SKNX_NO_SIMD-Trybot

Change-Id: Ib0508e5a4ee1bab2044a76bcabc367841d634cd2
Reviewed-on: https://skia-review.googlesource.com/5145
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Mike Klein <mtklein@chromium.org>
This commit is contained in:
Mike Klein 2016-11-22 14:58:45 -05:00 committed by Skia Commit-Bot
parent 2cb6cb7218
commit cb5338cadc
4 changed files with 114 additions and 64 deletions

View File

@ -79,7 +79,12 @@
M(color_lookup_table) M(lab_to_xyz) M(swap_rb) \
M(clamp_x) M(mirror_x) M(repeat_x) \
M(clamp_y) M(mirror_y) M(repeat_y) \
M(accum_565) M(accum_8888) M(accum_srgb) M(accum_f16) \
M(accum_f16) M(accum_a8) \
M(accum_g8) M(accum_g8_srgb) \
M(accum_i8) M(accum_i8_srgb) \
M(accum_565) M(accum_565_srgb) \
M(accum_4444) M(accum_4444_srgb) \
M(accum_8888) M(accum_8888_srgb) \
M(top_left) M(top_right) M(bottom_left) M(bottom_right)
class SkRasterPipeline {

View File

@ -57,24 +57,36 @@ static inline SkNx<N,int> sk_linear_to_srgb(const SkNx<N,float>& x) {
return SkNx_cast<int>(sk_clamp_0_255(f));
}
// sRGB -> linear, using math instead of table lookups, scaling better to larger SIMD vectors.
// sRGB -> linear, using math instead of table lookups.
template <int N>
static inline SkNx<N,float> sk_linear_from_srgb_math(const SkNx<N,float>& x) {
// Non-linear segment of sRGB curve approximated by
// l = 0.0025 + 0.6975x^2 + 0.3x^3
const SkNx<N,float> k0 = 0.0025f,
k2 = 0.6975f,
k3 = 0.3000f;
auto hi = SkNx_fma(x*x, SkNx_fma(x, k3, k2), k0);
// Linear segment of sRGB curve: the normal slope, extended a little further than normal.
auto lo = x * (1/12.92f);
return (x < 0.055f).thenElse(lo, hi);
}
// Same as above, starting from ints.
template <int N>
static inline SkNx<N,float> sk_linear_from_srgb_math(const SkNx<N,int>& s) {
auto x = SkNx_cast<float>(s);
const float u = 1/255.0f; // x is [0,255], so x^n needs scaling by u^n.
// Same math as above, but working with x in [0,255], so x^n needs scaling by u^n.
const float u = 1/255.0f;
// Non-linear segment of sRGB curve approximated by
// l = 0.0025 + 0.6975x^2 + 0.3x^3
const SkNx<N,float> k0 = 0.0025f,
k2 = 0.6975f * u*u,
k3 = 0.3000f * u*u*u;
auto hi = SkNx_fma(x*x, SkNx_fma(x, k3, k2), k0);
// Linear segment of sRGB curve: the normal slope, extended a little further than normal.
auto lo = x * (u/12.92f);
return (x < 14.025f).thenElse(lo, hi);
return (x < (0.055f/u)).thenElse(lo, hi);
}
#endif//SkSRGB_DEFINED

View File

@ -345,21 +345,23 @@ bool SkImageShader::onAppendStages(SkRasterPipeline* p, SkColorSpace* dst, SkFal
case kRepeat_TileMode: p->append(SkRasterPipeline::repeat_y, &ctx->height); break;
}
bool srgb = info.gammaCloseToSRGB() && dst != nullptr;
switch (info.colorType()) {
case kRGB_565_SkColorType:
p->append(srgb ? SkRasterPipeline::accum_565_srgb
: SkRasterPipeline::accum_565, ctx);
break;
case kRGBA_8888_SkColorType:
case kBGRA_8888_SkColorType:
if (info.gammaCloseToSRGB() && dst) {
p->append(SkRasterPipeline::accum_srgb, ctx);
} else {
p->append(SkRasterPipeline::accum_8888, ctx);
}
p->append(srgb ? SkRasterPipeline::accum_8888_srgb
: SkRasterPipeline::accum_8888, ctx);
break;
case kRGBA_F16_SkColorType:
p->append(SkRasterPipeline::accum_f16, ctx);
break;
case kRGB_565_SkColorType:
p->append(SkRasterPipeline::accum_565, ctx);
break;
default:
SkASSERT(false);

View File

@ -777,6 +777,13 @@ SI SkNi offset_and_ptr(T** ptr, const void* ctx, const SkNf& x, const SkNf& y) {
return offset;
}
STAGE(accum_a8, true) {} // TODO
STAGE(accum_g8, true) {} // TODO
STAGE(accum_g8_srgb, true) {} // TODO
STAGE(accum_i8, true) {} // TODO
STAGE(accum_i8_srgb, true) {} // TODO
STAGE(accum_565, true) {
const uint16_t* p;
SkNi offset = offset_and_ptr(&p, ctx, r, g);
@ -798,6 +805,77 @@ STAGE(accum_565, true) {
db += scale * B;
da += scale;
}
STAGE(accum_565_srgb, true) {
const uint16_t* p;
SkNi offset = offset_and_ptr(&p, ctx, r, g);
uint16_t px[N];
for (size_t i = 0; i < N; i++) {
if (kIsTail && i >= tail) {
px[i] = 0;
continue;
}
px[i] = p[offset[i]];
}
SkNf R,G,B;
from_565(SkNh::Load(px), &R, &G, &B);
SkNf scale = b;
dr += scale * sk_linear_from_srgb_math(R);
dg += scale * sk_linear_from_srgb_math(G);
db += scale * sk_linear_from_srgb_math(B);
da += scale;
}
STAGE(accum_4444, true) {} // TODO
STAGE(accum_4444_srgb, true) {} // TODO
STAGE(accum_8888, true) {
const uint32_t* p;
SkNi offset = offset_and_ptr(&p, ctx, r, g);
uint8_t R[N], G[N], B[N], A[N];
for (size_t i = 0; i < N; i++) {
if (kIsTail && i >= tail) {
R[i] = G[i] = B[i] = A[i] = 0;
continue;
}
uint32_t rgba = p[offset[i]];
R[i] = rgba >> 0;
G[i] = rgba >> 8;
B[i] = rgba >> 16;
A[i] = rgba >> 24;
}
SkNf scale = b;
dr += scale * SkNx_cast<float>(SkNb::Load(R)) * (1/255.0f);
dg += scale * SkNx_cast<float>(SkNb::Load(G)) * (1/255.0f);
db += scale * SkNx_cast<float>(SkNb::Load(B)) * (1/255.0f);
da += scale * SkNx_cast<float>(SkNb::Load(A)) * (1/255.0f);
}
STAGE(accum_8888_srgb, true) {
const uint32_t* p;
SkNi offset = offset_and_ptr(&p, ctx, r, g);
uint8_t R[N], G[N], B[N], A[N];
for (size_t i = 0; i < N; i++) {
if (kIsTail && i >= tail) {
R[i] = G[i] = B[i] = A[i] = 0;
continue;
}
uint32_t rgba = p[offset[i]];
R[i] = rgba >> 0;
G[i] = rgba >> 8;
B[i] = rgba >> 16;
A[i] = rgba >> 24;
}
SkNf scale = b;
dr += scale * sk_linear_from_srgb_math(SkNx_cast<int>(SkNb::Load(R)));
dg += scale * sk_linear_from_srgb_math(SkNx_cast<int>(SkNb::Load(G)));
db += scale * sk_linear_from_srgb_math(SkNx_cast<int>(SkNb::Load(B)));
da += scale * SkNx_cast<float>(SkNb::Load(A)) * (1/255.0f);
}
STAGE(accum_f16, true) {
const uint64_t* p;
@ -822,53 +900,6 @@ STAGE(accum_f16, true) {
da += scale * SkHalfToFloat_finite_ftz(SkNh::Load(A));
}
STAGE(accum_8888, true) {
const uint32_t* p;
SkNi offset = offset_and_ptr(&p, ctx, r, g);
uint8_t R[N], G[N], B[N], A[N];
for (size_t i = 0; i < N; i++) {
if (kIsTail && i >= tail) {
R[i] = G[i] = B[i] = A[i] = 0;
continue;
}
uint32_t rgba = p[offset[i]];
R[i] = rgba >> 0;
G[i] = rgba >> 8;
B[i] = rgba >> 16;
A[i] = rgba >> 24;
}
SkNf scale = b;
dr += scale * SkNx_cast<float>(SkNb::Load(R)) * (1/255.0f);
dg += scale * SkNx_cast<float>(SkNb::Load(G)) * (1/255.0f);
db += scale * SkNx_cast<float>(SkNb::Load(B)) * (1/255.0f);
da += scale * SkNx_cast<float>(SkNb::Load(A)) * (1/255.0f);
}
STAGE(accum_srgb, true) {
const uint32_t* p;
SkNi offset = offset_and_ptr(&p, ctx, r, g);
uint8_t R[N], G[N], B[N], A[N];
for (size_t i = 0; i < N; i++) {
if (kIsTail && i >= tail) {
R[i] = G[i] = B[i] = A[i] = 0;
continue;
}
uint32_t rgba = p[offset[i]];
R[i] = rgba >> 0;
G[i] = rgba >> 8;
B[i] = rgba >> 16;
A[i] = rgba >> 24;
}
SkNf scale = b;
dr += scale * sk_linear_from_srgb_math(SkNx_cast<int>(SkNb::Load(R)));
dg += scale * sk_linear_from_srgb_math(SkNx_cast<int>(SkNb::Load(G)));
db += scale * sk_linear_from_srgb_math(SkNx_cast<int>(SkNb::Load(B)));
da += scale * SkNx_cast<float>(SkNb::Load(A)) * (1/255.0f);
}
template <typename Fn>
SI Fn enum_to_Fn(SkRasterPipeline::StockStage st) {