jumper, finish blend modes

I've decided to ignore our existing CPU implementations and start from
scratch, mostly referencing the GL ES 3.2 spec and w3 spec.

This implementation ought to look a lot like the reference
implementation I've written in gm/hsl.cpp, with the addition of
handling alpha: unpremul, blend, re-premul with a simple SrcOver alpha.

Change-Id: I38cf6be2dc66a6f46d7b18b91847f6933d2fab62
Reviewed-on: https://skia-review.googlesource.com/15316
Reviewed-by: Herb Derby <herb@google.com>
Commit-Queue: Mike Klein <mtklein@chromium.org>
This commit is contained in:
Mike Klein 2017-05-04 12:42:52 -04:00 committed by Skia Commit-Bot
parent 05814de6ba
commit bb33833ed2
8 changed files with 8465 additions and 3122 deletions

View File

@ -13,7 +13,7 @@
bool SkBlendMode_SupportsCoverageAsAlpha(SkBlendMode);
bool SkBlendMode_CanOverflow(SkBlendMode);
bool SkBlendMode_AppendStages(SkBlendMode, SkRasterPipeline* = nullptr);
void SkBlendMode_AppendStages(SkBlendMode, SkRasterPipeline*);
#if SK_SUPPORT_GPU
#include "GrXferProcessor.h"

View File

@ -98,10 +98,10 @@ bool SkModeColorFilter::onAppendStages(SkRasterPipeline* p,
p->append(SkRasterPipeline::move_src_dst);
p->append(SkRasterPipeline::constant_color, color);
auto mode = (SkBlendMode)fMode;
if (!SkBlendMode_AppendStages(mode, p)) {
return false;
SkBlendMode_AppendStages(mode, p);
if (SkBlendMode_CanOverflow(mode)) {
p->append(SkRasterPipeline::clamp_a);
}
if (SkBlendMode_CanOverflow(mode)) { p->append(SkRasterPipeline::clamp_a); }
return true;
}

View File

@ -80,6 +80,7 @@
M(clear) M(modulate) M(multiply) M(plus_) M(screen) M(xor_) \
M(colorburn) M(colordodge) M(darken) M(difference) \
M(exclusion) M(hardlight) M(lighten) M(overlay) M(softlight) \
M(hue) M(saturation) M(color) M(luminosity) \
M(luminance_to_alpha) \
M(matrix_2x3) M(matrix_3x4) M(matrix_4x5) \
M(matrix_perspective) \

View File

@ -104,7 +104,7 @@ SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
SkColorFilter* colorFilter = paint.getColorFilter();
// TODO: all temporary
if (!supported(dst.info()) || !SkBlendMode_AppendStages(*blend)) {
if (!supported(dst.info())) {
return nullptr;
}
@ -219,7 +219,7 @@ void SkRasterPipelineBlitter::append_store(SkRasterPipeline* p) const {
}
void SkRasterPipelineBlitter::append_blend(SkRasterPipeline* p) const {
SkAssertResult(SkBlendMode_AppendStages(fBlend, p));
SkBlendMode_AppendStages(fBlend, p);
}
void SkRasterPipelineBlitter::maybe_clamp(SkRasterPipeline* p) const {

View File

@ -1484,11 +1484,11 @@ const GrXPFactory* SkBlendMode_AsXPFactory(SkBlendMode mode) {
bool SkBlendMode_CanOverflow(SkBlendMode mode) { return mode == SkBlendMode::kPlus; }
bool SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) {
void SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) {
auto stage = SkRasterPipeline::srcover;
switch (mode) {
case SkBlendMode::kClear: stage = SkRasterPipeline::clear; break;
case SkBlendMode::kSrc: return true; // This stage is a no-op.
case SkBlendMode::kSrc: return; // This stage is a no-op.
case SkBlendMode::kDst: stage = SkRasterPipeline::move_dst_src; break;
case SkBlendMode::kSrcOver: stage = SkRasterPipeline::srcover; break;
case SkBlendMode::kDstOver: stage = SkRasterPipeline::dstover; break;
@ -1514,13 +1514,10 @@ bool SkBlendMode_AppendStages(SkBlendMode mode, SkRasterPipeline* p) {
case SkBlendMode::kExclusion: stage = SkRasterPipeline::exclusion; break;
case SkBlendMode::kMultiply: stage = SkRasterPipeline::multiply; break;
case SkBlendMode::kHue:
case SkBlendMode::kSaturation:
case SkBlendMode::kColor:
case SkBlendMode::kLuminosity: return false; // TODO
case SkBlendMode::kHue: stage = SkRasterPipeline::hue; break;
case SkBlendMode::kSaturation: stage = SkRasterPipeline::saturation; break;
case SkBlendMode::kColor: stage = SkRasterPipeline::color; break;
case SkBlendMode::kLuminosity: stage = SkRasterPipeline::luminosity; break;
}
if (p) {
p->append(stage);
}
return true;
p->append(stage);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -413,6 +413,109 @@ BLEND_MODE(softlight) {
}
#undef BLEND_MODE
// We're basing our implemenation of non-separable blend modes on
// https://www.w3.org/TR/compositing-1/#blendingnonseparable.
// and
// https://www.khronos.org/registry/OpenGL/specs/es/3.2/es_spec_3.2.pdf
// They're equivalent, but ES' math has been better simplified.
SI F max(F r, F g, F b) { return max(r, max(g, b)); }
SI F min(F r, F g, F b) { return min(r, min(g, b)); }
SI F sat(F r, F g, F b) { return max(r,g,b) - min(r,g,b); }
SI F lum(F r, F g, F b) { return r*0.30f + g*0.59f + b*0.11f; }
SI void set_sat(F* r, F* g, F* b, F s) {
F mn = min(*r,*g,*b),
mx = max(*r,*g,*b),
sat = mx - mn;
// Map min channel to 0, max channel to s, and scale the middle proportionally.
auto scale = [=](F c) {
return if_then_else(sat == 0, 0, (c - mn) * s / sat);
};
*r = scale(*r);
*g = scale(*g);
*b = scale(*b);
}
SI void clip_color(F* r, F* g, F* b) {
F mn = min(*r, *g, *b),
mx = max(*r, *g, *b),
l = lum(*r, *g, *b);
auto clip = [=](F c) {
c = if_then_else(mn >= 0, c, l + (c - l) * ( l) / (l - mn) );
c = if_then_else(mx > 1, l + (c - l) * (1 - l) / (mx - l), c);
c = max(c, 0); // Sometimes without this we may dip just a little negative.
return c;
};
*r = clip(*r);
*g = clip(*g);
*b = clip(*b);
}
SI void set_lum(F* r, F* g, F* b, F l) {
F diff = l - lum(*r, *g, *b);
*r += diff;
*g += diff;
*b += diff;
clip_color(r, g, b);
}
SI F unpremultiply(F c, F a) {
return c * if_then_else(a == 0, 0, 1.0f / a);
}
STAGE(hue) {
F R = unpremultiply(r,a),
G = unpremultiply(g,a),
B = unpremultiply(b,a);
set_sat(&R, &G, &B, sat(dr,dg,db));
set_lum(&R, &G, &B, lum(dr,dg,db));
a = a + da - a*da;
r = R * a;
g = G * a;
b = B * a;
}
STAGE(saturation) {
F R = unpremultiply(dr,da),
G = unpremultiply(dg,da),
B = unpremultiply(db,da);
set_sat(&R, &G, &B, sat( r, g, b));
set_lum(&R, &G, &B, lum(dr,dg,db)); // (This is not redundant.)
a = a + da - a*da;
r = R * a;
g = G * a;
b = B * a;
}
STAGE(color) {
F R = unpremultiply(r,a),
G = unpremultiply(g,a),
B = unpremultiply(b,a);
set_lum(&R, &G, &B, lum(dr,dg,db));
a = a + da - a*da;
r = R * a;
g = G * a;
b = B * a;
}
STAGE(luminosity) {
F R = unpremultiply(dr,da),
G = unpremultiply(dg,da),
B = unpremultiply(db,da);
set_lum(&R, &G, &B, lum(r,g,b));
a = a + da - a*da;
r = R * a;
g = G * a;
b = B * a;
}
STAGE(clamp_0) {
r = max(r, 0);
g = max(g, 0);
@ -476,10 +579,9 @@ STAGE(premul) {
b = b * a;
}
STAGE(unpremul) {
auto scale = if_then_else(a == 0, 0, 1.0f / a);
r = r * scale;
g = g * scale;
b = b * scale;
r = unpremultiply(r,a);
g = unpremultiply(g,a);
b = unpremultiply(b,a);
}
STAGE(from_srgb) {