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:
parent
05814de6ba
commit
bb33833ed2
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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 {
|
||||
|
@ -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
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user