add clips and blends
This commit is contained in:
parent
9adeaf3e82
commit
adc9811776
|
@ -33,15 +33,17 @@ var<workgroup> sh_bbox: array<vec4<f32>, WG_SIZE>;
|
|||
var<workgroup> sh_link: array<i32, WG_SIZE>;
|
||||
|
||||
fn search_link(bic: ptr<function, Bic>, ix: u32) -> i32 {
|
||||
var ix = ix;
|
||||
var j = 0u;
|
||||
while j < firstTrailingBit(WG_SIZE) {
|
||||
let base = 2u * WG_SIZE - (2u << (firstTrailingBit(WG_SIZE) - j));
|
||||
if ((ix >> j) & 1u) != 0u {
|
||||
let test = bic_combine(sh_bic[base + (ix >> j) - 1u], *bic);
|
||||
if test.b >= 0u {
|
||||
*bic = test;
|
||||
ix -= 1u << j;
|
||||
if test.b > 0u {
|
||||
break;
|
||||
}
|
||||
*bic = test;
|
||||
ix -= 1u << j;
|
||||
}
|
||||
j += 1u;
|
||||
}
|
||||
|
@ -59,7 +61,7 @@ fn search_link(bic: ptr<function, Bic>, ix: u32) -> i32 {
|
|||
if ix > 0u {
|
||||
return i32(ix) - 1;
|
||||
} else {
|
||||
return i32(~(*bic).a);
|
||||
return i32(~0u - (*bic).a);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +69,9 @@ fn load_clip_inp(ix: u32) -> i32 {
|
|||
if ix < config.n_clip {
|
||||
return clip_inp[ix];
|
||||
} else {
|
||||
return i32(0x80000000);
|
||||
return -2147483648;
|
||||
// literal too large?
|
||||
// return 0x80000000;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,12 +133,12 @@ fn main(
|
|||
sh_bic[local_id.x] = bic;
|
||||
if is_push {
|
||||
let path_bbox = path_bboxes[inp];
|
||||
bbox = vec4<f32>(path_bbox.x0, path_bbox.y0, path_bbox.x1, path_bbox.y1);
|
||||
bbox = vec4<f32>(f32(path_bbox.x0), f32(path_bbox.y0), f32(path_bbox.x1), f32(path_bbox.y1));
|
||||
} else {
|
||||
bbox = vec4<f32>(-1e9, -1e9, 1e9, 1e9);
|
||||
}
|
||||
var inbase = 0u;
|
||||
for (var i = 0u; i < firstTrailingBit(WG_SIZE); i += 1u) {
|
||||
for (var i = 0u; i < firstTrailingBit(WG_SIZE) - 1u; i += 1u) {
|
||||
let outbase = 2u * WG_SIZE - (1u << (firstTrailingBit(WG_SIZE) - i));
|
||||
workgroupBarrier();
|
||||
if local_id.x < 1u << (firstTrailingBit(WG_SIZE) - 1u - i) {
|
||||
|
@ -191,5 +195,5 @@ fn main(
|
|||
bbox = vec4<f32>(-1e9, -1e9, 1e9, 1e9);
|
||||
}
|
||||
}
|
||||
clip_bboxes[global_id.x] = bbox
|
||||
clip_bboxes[global_id.x] = bbox;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ fn main(
|
|||
let path_ix = sh_path_ix[local_id.x];
|
||||
let path_bbox = path_bboxes[path_ix];
|
||||
let parent_ix = sh_parent[local_id.x] + wg_id.x * WG_SIZE;
|
||||
let bbox = vec4<f32>(path_bbox.x0, path_bbox.y0, path_bbox.x1, path_bbox.y1);
|
||||
let bbox = vec4<f32>(f32(path_bbox.x0), f32(path_bbox.y0), f32(path_bbox.x1), f32(path_bbox.y1));
|
||||
clip_out[global_id.x] = ClipEl(parent_ix, bbox);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -145,9 +145,32 @@ fn write_rad_grad(rad: CmdRadGrad) {
|
|||
alloc_cmd(12u);
|
||||
ptcl[cmd_offset] = CMD_RAD_GRAD;
|
||||
ptcl[cmd_offset + 1u] = rad.index;
|
||||
ptcl[cmd_offset + 2u] = bitcast<u32>(rad.matrx.x);
|
||||
ptcl[cmd_offset + 3u] = bitcast<u32>(rad.matrx.y);
|
||||
ptcl[cmd_offset + 4u] = bitcast<u32>(rad.matrx.z);
|
||||
ptcl[cmd_offset + 5u] = bitcast<u32>(rad.matrx.w);
|
||||
ptcl[cmd_offset + 6u] = bitcast<u32>(rad.xlat.x);
|
||||
ptcl[cmd_offset + 7u] = bitcast<u32>(rad.xlat.y);
|
||||
ptcl[cmd_offset + 8u] = bitcast<u32>(rad.c1.x);
|
||||
ptcl[cmd_offset + 9u] = bitcast<u32>(rad.c1.y);
|
||||
ptcl[cmd_offset + 10u] = bitcast<u32>(rad.ra);
|
||||
ptcl[cmd_offset + 11u] = bitcast<u32>(rad.roff);
|
||||
cmd_offset += 12u;
|
||||
}
|
||||
|
||||
fn write_begin_clip() {
|
||||
alloc_cmd(1u);
|
||||
ptcl[cmd_offset] = CMD_BEGIN_CLIP;
|
||||
cmd_offset += 1u;
|
||||
}
|
||||
|
||||
fn write_end_clip(blend: u32) {
|
||||
alloc_cmd(2u);
|
||||
ptcl[cmd_offset] = CMD_END_CLIP;
|
||||
ptcl[cmd_offset + 1u] = blend;
|
||||
cmd_offset += 2u;
|
||||
}
|
||||
|
||||
@compute @workgroup_size(256)
|
||||
fn main(
|
||||
@builtin(local_invocation_id) local_id: vec3<u32>,
|
||||
|
@ -166,15 +189,20 @@ fn main(
|
|||
let this_tile_ix = (bin_tile_y + tile_y) * config.width_in_tiles + bin_tile_x + tile_x;
|
||||
cmd_offset = this_tile_ix * PTCL_INITIAL_ALLOC;
|
||||
cmd_limit = cmd_offset + (PTCL_INITIAL_ALLOC - PTCL_HEADROOM);
|
||||
// TODO: clip state
|
||||
let clip_zero_depth = 0u;
|
||||
|
||||
// clip state
|
||||
var clip_zero_depth = 0u;
|
||||
var clip_depth = 0u;
|
||||
|
||||
var partition_ix = 0u;
|
||||
var rd_ix = 0u;
|
||||
var wr_ix = 0u;
|
||||
var part_start_ix = 0u;
|
||||
var ready_ix = 0u;
|
||||
// TODO: blend state
|
||||
|
||||
// blend state
|
||||
var render_blend_depth = 0u;
|
||||
var max_blend_depth = 0u;
|
||||
|
||||
while true {
|
||||
for (var i = 0u; i < N_SLICE; i += 1u) {
|
||||
|
@ -286,8 +314,16 @@ fn main(
|
|||
let y = sh_tile_y0[el_ix] + seq_ix / width;
|
||||
let tile_ix = sh_tile_base[el_ix] + sh_tile_stride[el_ix] * y + x;
|
||||
let tile = tiles[tile_ix];
|
||||
// TODO: this predicate becomes more interesting with clip
|
||||
let include_tile = tile.segments != 0u || tile.backdrop != 0;
|
||||
let is_clip = (tag & 1u) != 0u;
|
||||
var is_blend = false;
|
||||
if is_clip {
|
||||
let BLEND_CLIP = (128u << 8u) | 3u;
|
||||
let scene_offset = draw_monoids[drawobj_ix].scene_offset;
|
||||
let dd = config.drawdata_base + scene_offset;
|
||||
let blend = scene[dd];
|
||||
is_blend = blend != BLEND_CLIP;
|
||||
}
|
||||
let include_tile = tile.segments != 0u || (tile.backdrop == 0) == is_clip || is_blend;
|
||||
if include_tile {
|
||||
let el_slice = el_ix / 32u;
|
||||
let el_mask = 1u << (el_ix & 31u);
|
||||
|
@ -324,16 +360,18 @@ fn main(
|
|||
if clip_zero_depth == 0u {
|
||||
let tile_ix = sh_tile_base[el_ix] + sh_tile_stride[el_ix] * tile_y + tile_x;
|
||||
let tile = tiles[tile_ix];
|
||||
let linewidth = bitcast<f32>(info[di]);
|
||||
write_path(tile, linewidth);
|
||||
switch drawtag {
|
||||
// DRAWTAG_FILL_COLOR
|
||||
case 0x44u: {
|
||||
let linewidth = bitcast<f32>(info[di]);
|
||||
write_path(tile, linewidth);
|
||||
let rgba_color = scene[dd];
|
||||
write_color(CmdColor(rgba_color));
|
||||
}
|
||||
// DRAWTAG_FILL_LIN_GRADIENT
|
||||
case 0x114u: {
|
||||
let linewidth = bitcast<f32>(info[di]);
|
||||
write_path(tile, linewidth);
|
||||
var lin: CmdLinGrad;
|
||||
lin.index = scene[dd];
|
||||
lin.line_x = bitcast<f32>(info[di + 1u]);
|
||||
|
@ -343,6 +381,8 @@ fn main(
|
|||
}
|
||||
// DRAWTAG_FILL_RAD_GRADIENT
|
||||
case 0x2dcu: {
|
||||
let linewidth = bitcast<f32>(info[di]);
|
||||
write_path(tile, linewidth);
|
||||
var rad: CmdRadGrad;
|
||||
rad.index = scene[dd];
|
||||
let m0 = bitcast<f32>(info[di + 1u]);
|
||||
|
@ -356,6 +396,40 @@ fn main(
|
|||
rad.roff = bitcast<f32>(info[di + 10u]);
|
||||
write_rad_grad(rad);
|
||||
}
|
||||
// DRAWTAG_BEGIN_CLIP
|
||||
case 0x05u: {
|
||||
if tile.segments == 0u && tile.backdrop == 0 {
|
||||
clip_zero_depth = clip_depth + 1u;
|
||||
} else {
|
||||
write_begin_clip();
|
||||
render_blend_depth += 1u;
|
||||
max_blend_depth = max(max_blend_depth, render_blend_depth);
|
||||
}
|
||||
clip_depth += 1u;
|
||||
}
|
||||
// DRAWTAG_END_CLIP
|
||||
case 0x25u: {
|
||||
clip_depth -= 1u;
|
||||
write_path(tile, -1.0);
|
||||
write_end_clip(scene[dd]);
|
||||
render_blend_depth -= 1u;
|
||||
}
|
||||
default: {}
|
||||
}
|
||||
} else {
|
||||
// In "clip zero" state, suppress all drawing
|
||||
switch drawtag {
|
||||
// DRAWTAG_BEGIN_CLIP
|
||||
case 0x05u: {
|
||||
clip_depth += 1u;
|
||||
}
|
||||
// DRAWTAG_END_CLIP
|
||||
case 0x25u: {
|
||||
if clip_depth == clip_zero_depth {
|
||||
clip_zero_depth = 0u;
|
||||
}
|
||||
clip_depth -= 1u;
|
||||
}
|
||||
default: {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
// Finish prefix sum of drawtags, decode draw objects.
|
||||
|
||||
#import config
|
||||
#import clip
|
||||
#import drawtag
|
||||
#import bbox
|
||||
|
||||
|
@ -38,6 +39,9 @@ var<storage, read_write> draw_monoid: array<DrawMonoid>;
|
|||
@group(0) @binding(5)
|
||||
var<storage, read_write> info: array<u32>;
|
||||
|
||||
@group(0) @binding(6)
|
||||
var<storage, read_write> clip_inp: array<i32>;
|
||||
|
||||
let WG_SIZE = 256u;
|
||||
|
||||
// Possibly dedup?
|
||||
|
@ -183,4 +187,11 @@ fn main(
|
|||
default: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if tag_word == DRAWTAG_BEGIN_CLIP || tag_word == DRAWTAG_END_CLIP {
|
||||
var path_ix = ~ix;
|
||||
if tag_word == DRAWTAG_BEGIN_CLIP {
|
||||
path_ix = m.path_ix;
|
||||
}
|
||||
clip_inp[m.clip_ix] = i32(path_ix);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ var<storage> segments: array<Segment>;
|
|||
var<storage, read_write> output: array<u32>;
|
||||
|
||||
#ifdef full
|
||||
|
||||
#import blend
|
||||
#import ptcl
|
||||
|
||||
let GRADIENT_WIDTH = 512;
|
||||
|
@ -90,10 +92,6 @@ fn read_rad_grad(cmd_ix: u32) -> CmdRadGrad {
|
|||
return CmdRadGrad(index, matrx, xlat, c1, ra, roff);
|
||||
}
|
||||
|
||||
fn mix_blend_compose(backdrop: vec4<f32>, src: vec4<f32>, mode: u32) -> vec4<f32> {
|
||||
// TODO: ALL the blend modes. This is just vanilla src-over.
|
||||
return backdrop * (1.0 - src.a) + src;
|
||||
}
|
||||
#endif
|
||||
|
||||
let PIXELS_PER_THREAD = 4u;
|
||||
|
@ -233,7 +231,7 @@ fn main(
|
|||
let fg_i = fg_rgba * area[i];
|
||||
rgba[i] = rgba[i] * (1.0 - fg_i.a) + fg_i;
|
||||
}
|
||||
cmd_ix += 12u;
|
||||
cmd_ix += 5u;
|
||||
}
|
||||
// CMD_RAD_GRAD
|
||||
case 7u: {
|
||||
|
@ -278,7 +276,7 @@ fn main(
|
|||
}
|
||||
let bg = unpack4x8unorm(bg_rgba);
|
||||
let fg = rgba[i] * area[i];
|
||||
rgba[i] = mix_blend_compose(bg, fg, blend);
|
||||
rgba[i] = blend_mix_compose(bg, fg, blend);
|
||||
}
|
||||
cmd_ix += 2u;
|
||||
}
|
||||
|
|
|
@ -213,7 +213,7 @@ fn main(
|
|||
}
|
||||
for (var y = y0; y < y1; y += 1) {
|
||||
let tile_y0 = f32(y) * f32(TILE_HEIGHT);
|
||||
let xbackdrop = max(xray + 1, 0);
|
||||
let xbackdrop = max(xray + 1, bbox.x);
|
||||
if xymin.y < tile_y0 && xbackdrop < bbox.z {
|
||||
let backdrop = select(-1, 1, dp.y < 0.0);
|
||||
let tile_ix = base + xbackdrop;
|
||||
|
|
|
@ -0,0 +1,351 @@
|
|||
// Copyright 2022 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Also licensed under MIT license, at your choice.
|
||||
|
||||
|
||||
// Color mixing modes
|
||||
|
||||
let MIX_NORMAL = 0u;
|
||||
let MIX_MULTIPLY = 1u;
|
||||
let MIX_SCREEN = 2u;
|
||||
let MIX_OVERLAY = 3u;
|
||||
let MIX_DARKEN = 4u;
|
||||
let MIX_LIGHTEN = 5u;
|
||||
let MIX_COLOR_DODGE = 6u;
|
||||
let MIX_COLOR_BURN = 7u;
|
||||
let MIX_HARD_LIGHT = 8u;
|
||||
let MIX_SOFT_LIGHT = 9u;
|
||||
let MIX_DIFFERENCE = 10u;
|
||||
let MIX_EXCLUSION = 11u;
|
||||
let MIX_HUE = 12u;
|
||||
let MIX_SATURATION = 13u;
|
||||
let MIX_COLOR = 14u;
|
||||
let MIX_LUMINOSITY = 15u;
|
||||
let MIX_CLIP = 128u;
|
||||
|
||||
fn screen(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> {
|
||||
return cb + cs - (cb * cs);
|
||||
}
|
||||
|
||||
fn color_dodge(cb: f32, cs: f32) -> f32 {
|
||||
if cb == 0.0 {
|
||||
return 0.0;
|
||||
} else if cs == 1.0 {
|
||||
return 1.0;
|
||||
} else {
|
||||
return min(1.0, cb / (1.0 - cs));
|
||||
}
|
||||
}
|
||||
|
||||
fn color_burn(cb: f32, cs: f32) -> f32 {
|
||||
if cb == 1.0 {
|
||||
return 1.0;
|
||||
} else if cs == 0.0 {
|
||||
return 0.0;
|
||||
} else {
|
||||
return 1.0 - min(1.0, (1.0 - cb) / cs);
|
||||
}
|
||||
}
|
||||
|
||||
fn hard_light(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> {
|
||||
return mix(
|
||||
screen(cb, 2.0 * cs - 1.0),
|
||||
cb * 2.0 * cs,
|
||||
vec3<f32>(cs <= vec3<f32>(0.5))
|
||||
);
|
||||
}
|
||||
|
||||
fn soft_light(cb: vec3<f32>, cs: vec3<f32>) -> vec3<f32> {
|
||||
let d = mix(
|
||||
sqrt(cb),
|
||||
((16.0 * cb - vec3(12.0)) * cb + vec3(4.0)) * cb,
|
||||
vec3<f32>(cb <= vec3<f32>(0.25))
|
||||
);
|
||||
return mix(
|
||||
cb + (2.0 * cs - vec3(1.0)) * (d - cb),
|
||||
cb - (vec3(1.0) - 2.0 * cs) * cb * (vec3(1.0) - cb),
|
||||
vec3<f32>(cs <= vec3<f32>(0.5))
|
||||
);
|
||||
}
|
||||
|
||||
fn sat(c: vec3<f32>) -> f32 {
|
||||
return max(c.x, max(c.y, c.z)) - min(c.x, min(c.y, c.z));
|
||||
}
|
||||
|
||||
fn lum(c: vec3<f32>) -> f32 {
|
||||
let f = vec3<f32>(0.3, 0.59, 0.11);
|
||||
return dot(c, f);
|
||||
}
|
||||
|
||||
fn clip_color(c: vec3<f32>) -> vec3<f32> {
|
||||
var c = c;
|
||||
let l = lum(c);
|
||||
let n = min(c.x, min(c.y, c.z));
|
||||
let x = max(c.x, max(c.y, c.z));
|
||||
if n < 0.0 {
|
||||
c = l + (((c - l) * l) / (l - n));
|
||||
}
|
||||
if x > 1.0 {
|
||||
c = l + (((c - l) * (1.0 - l)) / (x - l));
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
fn set_lum(c: vec3<f32>, l: f32) -> vec3<f32> {
|
||||
return clip_color(c + (l - lum(c)));
|
||||
}
|
||||
|
||||
fn set_sat_inner(
|
||||
cmin: ptr<function, f32>,
|
||||
cmid: ptr<function, f32>,
|
||||
cmax: ptr<function, f32>,
|
||||
s: f32
|
||||
) {
|
||||
if *cmax > *cmin {
|
||||
*cmid = ((*cmid - *cmin) * s) / (*cmax - *cmin);
|
||||
*cmax = s;
|
||||
} else {
|
||||
*cmid = 0.0;
|
||||
*cmax = 0.0;
|
||||
}
|
||||
*cmin = 0.0;
|
||||
}
|
||||
|
||||
fn set_sat(c: vec3<f32>, s: f32) -> vec3<f32> {
|
||||
var r = c.r;
|
||||
var g = c.g;
|
||||
var b = c.b;
|
||||
if r <= g {
|
||||
if g <= b {
|
||||
set_sat_inner(&r, &g, &b, s);
|
||||
} else {
|
||||
if r <= b {
|
||||
set_sat_inner(&r, &b, &g, s);
|
||||
} else {
|
||||
set_sat_inner(&b, &r, &g, s);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if r <= b {
|
||||
set_sat_inner(&g, &r, &b, s);
|
||||
} else {
|
||||
if g <= b {
|
||||
set_sat_inner(&g, &b, &r, s);
|
||||
} else {
|
||||
set_sat_inner(&b, &g, &r, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
return vec3<f32>(r, g, b);
|
||||
}
|
||||
|
||||
// Blends two RGB colors together. The colors are assumed to be in sRGB
|
||||
// color space, and this function does not take alpha into account.
|
||||
fn blend_mix(cb: vec3<f32>, cs: vec3<f32>, mode: u32) -> vec3<f32> {
|
||||
var b = vec3<f32>(0.0);
|
||||
switch mode {
|
||||
// MIX_MULTIPLY
|
||||
case 1u: {
|
||||
b = cb * cs;
|
||||
}
|
||||
// MIX_SCREEN
|
||||
case 2u: {
|
||||
b = screen(cb, cs);
|
||||
}
|
||||
// MIX_OVERLAY
|
||||
case 3u: {
|
||||
b = hard_light(cs, cb);
|
||||
}
|
||||
// MIX_DARKEN
|
||||
case 4u: {
|
||||
b = min(cb, cs);
|
||||
}
|
||||
// MIX_LIGHTEN
|
||||
case 5u: {
|
||||
b = max(cb, cs);
|
||||
}
|
||||
// MIX_COLOR_DODGE
|
||||
case 6u: {
|
||||
b = vec3<f32>(color_dodge(cb.x, cs.x), color_dodge(cb.y, cs.y), color_dodge(cb.z, cs.z));
|
||||
}
|
||||
// MIX_COLOR_BURN
|
||||
case 7u: {
|
||||
b = vec3<f32>(color_burn(cb.x, cs.x), color_burn(cb.y, cs.y), color_burn(cb.z, cs.z));
|
||||
}
|
||||
// MIX_HARD_LIGHT
|
||||
case 8u: {
|
||||
b = hard_light(cb, cs);
|
||||
}
|
||||
// MIX_SOFT_LIGHT
|
||||
case 9u: {
|
||||
b = soft_light(cb, cs);
|
||||
}
|
||||
// MIX_DIFFERENCE
|
||||
case 10u: {
|
||||
b = abs(cb - cs);
|
||||
}
|
||||
// MIX_EXCLUSION
|
||||
case 11u: {
|
||||
b = cb + cs - 2.0 * cb * cs;
|
||||
}
|
||||
// MIX_HUE
|
||||
case 12u: {
|
||||
b = set_lum(set_sat(cs, sat(cb)), lum(cb));
|
||||
}
|
||||
// MIX_SATURATION
|
||||
case 13u: {
|
||||
b = set_lum(set_sat(cb, sat(cs)), lum(cb));
|
||||
}
|
||||
// MIX_COLOR
|
||||
case 14u: {
|
||||
b = set_lum(cs, lum(cb));
|
||||
}
|
||||
// MIX_LUMINOSITY
|
||||
case 15u: {
|
||||
b = set_lum(cb, lum(cs));
|
||||
}
|
||||
default: {
|
||||
b = cs;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
// Composition modes
|
||||
|
||||
let COMPOSE_CLEAR = 0u;
|
||||
let COMPOSE_COPY = 1u;
|
||||
let COMPOSE_DEST = 2u;
|
||||
let COMPOSE_SRC_OVER = 3u;
|
||||
let COMPOSE_DEST_OVER = 4u;
|
||||
let COMPOSE_SRC_IN = 5u;
|
||||
let COMPOSE_DEST_IN = 6u;
|
||||
let COMPOSE_SRC_OUT = 7u;
|
||||
let COMPOSE_DEST_OUT = 8u;
|
||||
let COMPOSE_SRC_ATOP = 9u;
|
||||
let COMPOSE_DEST_ATOP = 10u;
|
||||
let COMPOSE_XOR = 11u;
|
||||
let COMPOSE_PLUS = 12u;
|
||||
let COMPOSE_PLUS_LIGHTER = 13u;
|
||||
|
||||
// Apply general compositing operation.
|
||||
// Inputs are separated colors and alpha, output is premultiplied.
|
||||
fn blend_compose(
|
||||
cb: vec3<f32>,
|
||||
cs: vec3<f32>,
|
||||
ab: f32,
|
||||
as_: f32,
|
||||
mode: u32
|
||||
) -> vec4<f32> {
|
||||
var fa = 0.0;
|
||||
var fb = 0.0;
|
||||
switch mode {
|
||||
// COMPOSE_COPY
|
||||
case 1u: {
|
||||
fa = 1.0;
|
||||
fb = 0.0;
|
||||
}
|
||||
// COMPOSE_DEST
|
||||
case 2u: {
|
||||
fa = 0.0;
|
||||
fb = 1.0;
|
||||
}
|
||||
// COMPOSE_SRC_OVER
|
||||
case 3u: {
|
||||
fa = 1.0;
|
||||
fb = 1.0 - as_;
|
||||
}
|
||||
// COMPOSE_DEST_OVER
|
||||
case 4u: {
|
||||
fa = 1.0 - ab;
|
||||
fb = 1.0;
|
||||
}
|
||||
// COMPOSE_SRC_IN
|
||||
case 5u: {
|
||||
fa = ab;
|
||||
fb = 0.0;
|
||||
}
|
||||
// COMPOSE_DEST_IN
|
||||
case 6u: {
|
||||
fa = 0.0;
|
||||
fb = as_;
|
||||
}
|
||||
// COMPOSE_SRC_OUT
|
||||
case 7u: {
|
||||
fa = 1.0 - ab;
|
||||
fb = 0.0;
|
||||
}
|
||||
// COMPOSE_DEST_OUT
|
||||
case 8u: {
|
||||
fa = 0.0;
|
||||
fb = 1.0 - as_;
|
||||
}
|
||||
// COMPOSE_SRC_ATOP
|
||||
case 9u: {
|
||||
fa = ab;
|
||||
fb = 1.0 - as_;
|
||||
}
|
||||
// COMPOSE_DEST_ATOP
|
||||
case 10u: {
|
||||
fa = 1.0 - ab;
|
||||
fb = as_;
|
||||
}
|
||||
// COMPOSE_XOR
|
||||
case 11u: {
|
||||
fa = 1.0 - ab;
|
||||
fb = 1.0 - as_;
|
||||
}
|
||||
// COMPOSE_PLUS
|
||||
case 12u: {
|
||||
fa = 1.0;
|
||||
fb = 1.0;
|
||||
}
|
||||
// COMPOSE_PLUS_LIGHTER
|
||||
case 13u: {
|
||||
return min(vec4<f32>(1.0), vec4<f32>(as_ * cs + ab * cb, as_ + ab));
|
||||
}
|
||||
default: {}
|
||||
}
|
||||
let as_fa = as_ * fa;
|
||||
let ab_fb = ab * fb;
|
||||
let co = as_fa * cs + ab_fb * cb;
|
||||
return vec4<f32>(co, as_fa + ab_fb);
|
||||
}
|
||||
|
||||
// Apply color mixing and composition. Both input and output colors are
|
||||
// premultiplied RGB.
|
||||
fn blend_mix_compose(backdrop: vec4<f32>, src: vec4<f32>, mode: u32) -> vec4<f32> {
|
||||
let BLEND_DEFAULT = ((MIX_NORMAL << 8u) | COMPOSE_SRC_OVER);
|
||||
let EPSILON = 1e-15;
|
||||
if (mode & 0x7fffu) == BLEND_DEFAULT {
|
||||
// Both normal+src_over blend and clip case
|
||||
return backdrop * (1.0 - src.a) + src;
|
||||
}
|
||||
// Un-premultiply colors for blending
|
||||
let inv_src_a = 1.0 / (src.a + EPSILON);
|
||||
var cs = src.rgb * inv_src_a;
|
||||
let inv_backdrop_a = 1.0 / (backdrop.a + EPSILON);
|
||||
let cb = backdrop.rgb * inv_backdrop_a;
|
||||
let mix_mode = mode >> 8u;
|
||||
let mixed = blend_mix(cb, cs, mix_mode);
|
||||
cs = mix(cs, mixed, backdrop.a);
|
||||
let compose_mode = mode & 0xffu;
|
||||
if compose_mode == COMPOSE_SRC_OVER {
|
||||
let co = mix(backdrop.rgb, cs, src.a);
|
||||
return vec4<f32>(co, src.a + backdrop.a * (1.0 - src.a));
|
||||
} else {
|
||||
return blend_compose(cb, cs, backdrop.a, src.a, compose_mode);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
pub mod clip;
|
||||
pub mod draw;
|
||||
pub mod fine;
|
|
@ -0,0 +1,13 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct ClipEl {
|
||||
pub parent_ix: u32,
|
||||
pub pad: [u32; 3],
|
||||
pub bbox: [f32; 4],
|
||||
}
|
||||
|
||||
pub fn parse_clip_els(data: &[u8]) -> Vec<ClipEl> {
|
||||
Vec::from(bytemuck::cast_slice(data))
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct DrawMonoid {
|
||||
pub path_ix: u32,
|
||||
pub clip_ix: u32,
|
||||
pub scene_offset: u32,
|
||||
pub info_offset: u32,
|
||||
}
|
||||
|
||||
pub fn parse_draw_monoids(data: &[u8]) -> Vec<DrawMonoid> {
|
||||
Vec::from(bytemuck::cast_slice(data))
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Fill {
|
||||
pub tile: u32,
|
||||
pub backdrop: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Stroke {
|
||||
pub tile: u32,
|
||||
pub half_width: f32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Color {
|
||||
abgr: [u8; 4],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct LinGrad {
|
||||
pub index: u32,
|
||||
pub line_x: f32,
|
||||
pub line_y: f32,
|
||||
pub line_c: f32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct RadGrad {
|
||||
pub index: u32,
|
||||
pub matrix: [f32; 4],
|
||||
pub xlat: [f32; 2],
|
||||
pub c1: [f32; 2],
|
||||
pub ra: f32,
|
||||
pub roff: f32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Command {
|
||||
Fill(Fill),
|
||||
Stroke(Stroke),
|
||||
Solid,
|
||||
Color(Color),
|
||||
LinGrad(LinGrad),
|
||||
RadGrad(RadGrad),
|
||||
BeginClip,
|
||||
EndClip(u32),
|
||||
End,
|
||||
}
|
||||
|
||||
const PTCL_INITIAL_ALLOC: usize = 64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CommandList {
|
||||
pub tiles: Vec<(u32, u32, Vec<Command>)>,
|
||||
}
|
||||
|
||||
impl CommandList {
|
||||
pub fn parse(width: usize, height: usize, ptcl: &[u8]) -> Self {
|
||||
let mut tiles = vec![];
|
||||
let width_tiles = width / 16;
|
||||
let height_tiles = height / 16;
|
||||
for y in 0..height_tiles {
|
||||
for x in 0..width_tiles {
|
||||
let tile_ix = y * width_tiles + x;
|
||||
let ix = tile_ix * PTCL_INITIAL_ALLOC;
|
||||
let commands = parse_commands(ptcl, ix);
|
||||
if !commands.is_empty() {
|
||||
tiles.push((x as u32, y as u32, commands));
|
||||
}
|
||||
}
|
||||
}
|
||||
Self { tiles }
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_commands(ptcl: &[u8], mut ix: usize) -> Vec<Command> {
|
||||
let mut commands = vec![];
|
||||
let words: &[u32] = bytemuck::cast_slice(ptcl);
|
||||
while ix < words.len() {
|
||||
let tag = words[ix];
|
||||
ix += 1;
|
||||
match tag {
|
||||
0 => break,
|
||||
1 => {
|
||||
commands.push(Command::Fill(Fill {
|
||||
tile: words[ix],
|
||||
backdrop: words[ix + 1] as i32,
|
||||
}));
|
||||
ix += 2;
|
||||
}
|
||||
2 => {
|
||||
commands.push(Command::Stroke(Stroke {
|
||||
tile: words[ix],
|
||||
half_width: bytemuck::cast(words[ix + 1]),
|
||||
}));
|
||||
ix += 2;
|
||||
}
|
||||
3 => {
|
||||
commands.push(Command::Solid);
|
||||
}
|
||||
5 => {
|
||||
commands.push(Command::Color(Color {
|
||||
abgr: bytemuck::cast(words[ix]),
|
||||
}));
|
||||
ix += 1;
|
||||
}
|
||||
6 => {
|
||||
commands.push(Command::LinGrad(LinGrad {
|
||||
index: words[ix],
|
||||
line_x: bytemuck::cast(words[ix + 1]),
|
||||
line_y: bytemuck::cast(words[ix + 2]),
|
||||
line_c: bytemuck::cast(words[ix + 3]),
|
||||
}));
|
||||
ix += 4;
|
||||
}
|
||||
7 => {
|
||||
let matrix = [
|
||||
bytemuck::cast(words[ix + 1]),
|
||||
bytemuck::cast(words[ix + 2]),
|
||||
bytemuck::cast(words[ix + 3]),
|
||||
bytemuck::cast(words[ix + 4]),
|
||||
];
|
||||
let xlat = [bytemuck::cast(words[ix + 5]), bytemuck::cast(words[ix + 6])];
|
||||
let c1 = [bytemuck::cast(words[ix + 7]), bytemuck::cast(words[ix + 8])];
|
||||
commands.push(Command::RadGrad(RadGrad {
|
||||
index: words[ix],
|
||||
matrix,
|
||||
xlat,
|
||||
c1,
|
||||
ra: bytemuck::cast(words[ix + 9]),
|
||||
roff: bytemuck::cast(words[ix + 10]),
|
||||
}));
|
||||
ix += 11;
|
||||
}
|
||||
9 => {
|
||||
commands.push(Command::BeginClip);
|
||||
}
|
||||
10 => {
|
||||
commands.push(Command::EndClip(words[ix]));
|
||||
ix += 1;
|
||||
}
|
||||
11 => {
|
||||
ix = words[ix] as usize;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
commands
|
||||
}
|
|
@ -341,7 +341,10 @@ impl Recording {
|
|||
impl BufProxy {
|
||||
pub fn new(size: u64) -> Self {
|
||||
let id = Id::next();
|
||||
BufProxy { id, size }
|
||||
BufProxy {
|
||||
id,
|
||||
size: size.max(16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,6 +363,20 @@ impl ResourceProxy {
|
|||
pub fn new_image(width: u32, height: u32) -> Self {
|
||||
Self::Image(ImageProxy::new(width, height))
|
||||
}
|
||||
|
||||
pub fn as_buf(&self) -> Option<&BufProxy> {
|
||||
match self {
|
||||
Self::Buf(proxy) => Some(&proxy),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_image(&self) -> Option<&ImageProxy> {
|
||||
match self {
|
||||
Self::Image(proxy) => Some(&proxy),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BufProxy> for ResourceProxy {
|
||||
|
|
|
@ -22,6 +22,7 @@ use engine::Engine;
|
|||
|
||||
use wgpu::{Device, Limits, Queue};
|
||||
|
||||
mod debug;
|
||||
mod engine;
|
||||
mod pico_svg;
|
||||
mod ramp;
|
||||
|
|
|
@ -14,6 +14,10 @@ const PATH_BBOX_SIZE: u64 = 24;
|
|||
const CUBIC_SIZE: u64 = 40;
|
||||
const DRAWMONOID_SIZE: u64 = 16;
|
||||
const MAX_DRAWINFO_SIZE: u64 = 44;
|
||||
const CLIP_BIC_SIZE: u64 = 8;
|
||||
const CLIP_EL_SIZE: u64 = 32;
|
||||
const CLIP_INP_SIZE: u64 = 4;
|
||||
const CLIP_BBOX_SIZE: u64 = 16;
|
||||
const PATH_SIZE: u64 = 32;
|
||||
const DRAW_BBOX_SIZE: u64 = 16;
|
||||
const BUMP_SIZE: u64 = 16;
|
||||
|
@ -187,7 +191,7 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy
|
|||
let n_path = data.n_path;
|
||||
// TODO: calculate for real when we do rectangles
|
||||
let n_drawobj = n_path;
|
||||
let n_clip = 0; // TODO: wire up correctly
|
||||
let n_clip = data.n_clip;
|
||||
let config = Config {
|
||||
width_in_tiles: 64,
|
||||
height_in_tiles: 64,
|
||||
|
@ -251,6 +255,7 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy
|
|||
);
|
||||
let draw_monoid_buf = ResourceProxy::new_buf(n_drawobj as u64 * DRAWMONOID_SIZE);
|
||||
let info_buf = ResourceProxy::new_buf(n_drawobj as u64 * MAX_DRAWINFO_SIZE);
|
||||
let clip_inp_buf = ResourceProxy::new_buf(data.n_clip as u64 * CLIP_INP_SIZE);
|
||||
recording.dispatch(
|
||||
shaders.draw_leaf,
|
||||
(drawobj_wgs, 1, 1),
|
||||
|
@ -261,12 +266,45 @@ pub fn render_full(scene: &Scene, shaders: &FullShaders) -> (Recording, BufProxy
|
|||
path_bbox_buf,
|
||||
draw_monoid_buf,
|
||||
info_buf,
|
||||
clip_inp_buf,
|
||||
],
|
||||
);
|
||||
let clip_el_buf = ResourceProxy::new_buf(data.n_clip as u64 * CLIP_EL_SIZE);
|
||||
let clip_bic_buf =
|
||||
ResourceProxy::new_buf((n_clip / shaders::CLIP_REDUCE_WG) as u64 * CLIP_BIC_SIZE);
|
||||
let clip_wg_reduce = n_clip.saturating_sub(1) / shaders::CLIP_REDUCE_WG;
|
||||
if clip_wg_reduce > 0 {
|
||||
recording.dispatch(
|
||||
shaders.clip_reduce,
|
||||
(clip_wg_reduce, 1, 1),
|
||||
[
|
||||
config_buf,
|
||||
clip_inp_buf,
|
||||
path_bbox_buf,
|
||||
clip_bic_buf,
|
||||
clip_el_buf,
|
||||
],
|
||||
);
|
||||
}
|
||||
let clip_wg = (n_clip + shaders::CLIP_REDUCE_WG - 1) / shaders::CLIP_REDUCE_WG;
|
||||
let clip_bbox_buf = ResourceProxy::new_buf(n_clip as u64 * CLIP_BBOX_SIZE);
|
||||
if clip_wg > 0 {
|
||||
recording.dispatch(
|
||||
shaders.clip_leaf,
|
||||
(clip_wg, 1, 1),
|
||||
[
|
||||
config_buf,
|
||||
clip_inp_buf,
|
||||
path_bbox_buf,
|
||||
clip_bic_buf,
|
||||
clip_el_buf,
|
||||
draw_monoid_buf,
|
||||
clip_bbox_buf,
|
||||
],
|
||||
);
|
||||
}
|
||||
let draw_bbox_buf = ResourceProxy::new_buf(n_path as u64 * DRAW_BBOX_SIZE);
|
||||
let bump_buf = BufProxy::new(BUMP_SIZE);
|
||||
// Not actually used yet.
|
||||
let clip_bbox_buf = ResourceProxy::new_buf(1024);
|
||||
let bin_data_buf = ResourceProxy::new_buf(1 << 20);
|
||||
let width_in_bins = (config.width_in_tiles + 15) / 16;
|
||||
let height_in_bins = (config.height_in_tiles + 15) / 16;
|
||||
|
|
|
@ -28,6 +28,7 @@ pub const PATHTAG_REDUCE_WG: u32 = 256;
|
|||
pub const PATH_BBOX_WG: u32 = 256;
|
||||
pub const PATH_COARSE_WG: u32 = 256;
|
||||
pub const PATH_DRAWOBJ_WG: u32 = 256;
|
||||
pub const CLIP_REDUCE_WG: u32 = 256;
|
||||
|
||||
pub struct Shaders {
|
||||
pub pathtag_reduce: ShaderId,
|
||||
|
@ -45,6 +46,8 @@ pub struct FullShaders {
|
|||
pub pathseg: ShaderId,
|
||||
pub draw_reduce: ShaderId,
|
||||
pub draw_leaf: ShaderId,
|
||||
pub clip_reduce: ShaderId,
|
||||
pub clip_leaf: ShaderId,
|
||||
pub binning: ShaderId,
|
||||
pub tile_alloc: ShaderId,
|
||||
pub path_coarse: ShaderId,
|
||||
|
@ -178,6 +181,31 @@ pub fn full_shaders(device: &Device, engine: &mut Engine) -> Result<FullShaders,
|
|||
BindType::BufReadOnly,
|
||||
BindType::Buffer,
|
||||
BindType::Buffer,
|
||||
BindType::Buffer,
|
||||
],
|
||||
)?;
|
||||
let clip_reduce = engine.add_shader(
|
||||
device,
|
||||
preprocess::preprocess(&read_shader("clip_reduce"), &empty, &imports).into(),
|
||||
&[
|
||||
BindType::BufReadOnly,
|
||||
BindType::BufReadOnly,
|
||||
BindType::BufReadOnly,
|
||||
BindType::Buffer,
|
||||
BindType::Buffer,
|
||||
],
|
||||
)?;
|
||||
let clip_leaf = engine.add_shader(
|
||||
device,
|
||||
preprocess::preprocess(&read_shader("clip_leaf"), &empty, &imports).into(),
|
||||
&[
|
||||
BindType::BufReadOnly,
|
||||
BindType::BufReadOnly,
|
||||
BindType::BufReadOnly,
|
||||
BindType::BufReadOnly,
|
||||
BindType::BufReadOnly,
|
||||
BindType::Buffer,
|
||||
BindType::Buffer,
|
||||
],
|
||||
)?;
|
||||
let binning = engine.add_shader(
|
||||
|
@ -265,6 +293,8 @@ pub fn full_shaders(device: &Device, engine: &mut Engine) -> Result<FullShaders,
|
|||
pathseg,
|
||||
draw_reduce,
|
||||
draw_leaf,
|
||||
clip_reduce,
|
||||
clip_leaf,
|
||||
binning,
|
||||
tile_alloc,
|
||||
path_coarse,
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
|
||||
use kurbo::BezPath;
|
||||
use piet_scene::{
|
||||
Affine, Brush, Color, Fill, GradientStop, LinearGradient, PathElement, Point, Scene,
|
||||
SceneBuilder, Stroke,
|
||||
Affine, BlendMode, Brush, Color, Compose, ExtendMode, Fill, GradientStop, LinearGradient, Mix,
|
||||
PathElement, Point, RadialGradient, Rect, Scene, SceneBuilder, SceneFragment, Stroke,
|
||||
};
|
||||
|
||||
use crate::pico_svg::PicoSvg;
|
||||
|
@ -47,40 +47,7 @@ pub fn gen_test_scene() -> Scene {
|
|||
builder.stroke(&style, transform, &brush, None, &path);
|
||||
}
|
||||
1 => {
|
||||
let path = [
|
||||
PathElement::MoveTo(Point::new(100.0, 100.0)),
|
||||
PathElement::LineTo(Point::new(300.0, 100.0)),
|
||||
PathElement::LineTo(Point::new(300.0, 300.0)),
|
||||
PathElement::LineTo(Point::new(100.0, 300.0)),
|
||||
PathElement::Close,
|
||||
];
|
||||
let gradient = Brush::LinearGradient(LinearGradient {
|
||||
start: Point::new(100.0, 100.0),
|
||||
end: Point::new(300.0, 300.0),
|
||||
extend: piet_scene::ExtendMode::Pad,
|
||||
stops: vec![
|
||||
GradientStop {
|
||||
offset: 0.0,
|
||||
color: Color::rgb8(255, 0, 0),
|
||||
},
|
||||
GradientStop {
|
||||
offset: 0.5,
|
||||
color: Color::rgb8(0, 255, 0),
|
||||
},
|
||||
GradientStop {
|
||||
offset: 1.0,
|
||||
color: Color::rgb8(0, 0, 255),
|
||||
},
|
||||
]
|
||||
.into(),
|
||||
});
|
||||
builder.fill(
|
||||
Fill::NonZero,
|
||||
Affine::scale(3.0, 3.0),
|
||||
&gradient,
|
||||
None,
|
||||
&path,
|
||||
);
|
||||
render_blend_grid(&mut builder);
|
||||
}
|
||||
_ => {
|
||||
let xml_str =
|
||||
|
@ -90,6 +57,7 @@ pub fn gen_test_scene() -> Scene {
|
|||
render_svg(&mut builder, &svg, false);
|
||||
}
|
||||
}
|
||||
builder.finish();
|
||||
scene
|
||||
}
|
||||
|
||||
|
@ -151,3 +119,163 @@ fn simple_stroke(width: f32) -> Stroke<[f32; 0]> {
|
|||
scale: true,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn render_blend_grid(sb: &mut SceneBuilder) {
|
||||
const BLEND_MODES: &[Mix] = &[
|
||||
Mix::Normal,
|
||||
Mix::Multiply,
|
||||
Mix::Darken,
|
||||
Mix::Screen,
|
||||
Mix::Lighten,
|
||||
Mix::Overlay,
|
||||
Mix::ColorDodge,
|
||||
Mix::ColorBurn,
|
||||
Mix::HardLight,
|
||||
Mix::SoftLight,
|
||||
Mix::Difference,
|
||||
Mix::Exclusion,
|
||||
Mix::Hue,
|
||||
Mix::Saturation,
|
||||
Mix::Color,
|
||||
Mix::Luminosity,
|
||||
];
|
||||
for (ix, &blend) in BLEND_MODES.iter().enumerate() {
|
||||
let i = ix % 4;
|
||||
let j = ix / 4;
|
||||
let transform = Affine::translate(i as f32 * 225., j as f32 * 225.);
|
||||
let square = blend_square(blend.into());
|
||||
sb.append(&square, Some(transform));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn render_blend_square(sb: &mut SceneBuilder, blend: BlendMode, transform: Affine) {
|
||||
// Inspired by https://developer.mozilla.org/en-US/docs/Web/CSS/mix-blend-mode
|
||||
let rect = Rect::from_origin_size(Point::new(0., 0.), 200., 200.);
|
||||
let stops = &[
|
||||
GradientStop {
|
||||
color: Color::rgb8(0, 0, 0),
|
||||
offset: 0.0,
|
||||
},
|
||||
GradientStop {
|
||||
color: Color::rgb8(255, 255, 255),
|
||||
offset: 1.0,
|
||||
},
|
||||
][..];
|
||||
let linear = Brush::LinearGradient(LinearGradient {
|
||||
start: Point::new(0.0, 0.0),
|
||||
end: Point::new(200.0, 0.0),
|
||||
stops: stops.into(),
|
||||
extend: ExtendMode::Pad,
|
||||
});
|
||||
sb.fill(Fill::NonZero, transform, &linear, None, rect.elements());
|
||||
const GRADIENTS: &[(f32, f32, Color)] = &[
|
||||
(150., 0., Color::rgb8(255, 240, 64)),
|
||||
(175., 100., Color::rgb8(255, 96, 240)),
|
||||
(125., 200., Color::rgb8(64, 192, 255)),
|
||||
];
|
||||
for (x, y, c) in GRADIENTS {
|
||||
let mut color2 = c.clone();
|
||||
color2.a = 0;
|
||||
let stops = &[
|
||||
GradientStop {
|
||||
color: c.clone(),
|
||||
offset: 0.0,
|
||||
},
|
||||
GradientStop {
|
||||
color: color2,
|
||||
offset: 1.0,
|
||||
},
|
||||
][..];
|
||||
let rad = Brush::RadialGradient(RadialGradient {
|
||||
center0: Point::new(*x, *y),
|
||||
center1: Point::new(*x, *y),
|
||||
radius0: 0.0,
|
||||
radius1: 100.0,
|
||||
stops: stops.into(),
|
||||
extend: ExtendMode::Pad,
|
||||
});
|
||||
sb.fill(Fill::NonZero, transform, &rad, None, rect.elements());
|
||||
}
|
||||
const COLORS: &[Color] = &[
|
||||
Color::rgb8(255, 0, 0),
|
||||
Color::rgb8(0, 255, 0),
|
||||
Color::rgb8(0, 0, 255),
|
||||
];
|
||||
sb.push_layer(Mix::Normal.into(), transform, rect.elements());
|
||||
for (i, c) in COLORS.iter().enumerate() {
|
||||
let stops = &[
|
||||
GradientStop {
|
||||
color: Color::rgb8(255, 255, 255),
|
||||
offset: 0.0,
|
||||
},
|
||||
GradientStop {
|
||||
color: c.clone(),
|
||||
offset: 1.0,
|
||||
},
|
||||
][..];
|
||||
let linear = Brush::LinearGradient(LinearGradient {
|
||||
start: Point::new(0.0, 0.0),
|
||||
end: Point::new(0.0, 200.0),
|
||||
stops: stops.into(),
|
||||
extend: ExtendMode::Pad,
|
||||
});
|
||||
sb.push_layer(blend, transform, rect.elements());
|
||||
// squash the ellipse
|
||||
let a = transform
|
||||
* Affine::translate(100., 100.)
|
||||
* Affine::rotate(std::f32::consts::FRAC_PI_3 * (i * 2 + 1) as f32)
|
||||
* Affine::scale(1.0, 0.357)
|
||||
* Affine::translate(-100., -100.);
|
||||
sb.fill(
|
||||
Fill::NonZero,
|
||||
a,
|
||||
&linear,
|
||||
None,
|
||||
make_ellipse(100., 100., 90., 90.),
|
||||
);
|
||||
sb.pop_layer();
|
||||
}
|
||||
sb.pop_layer();
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
fn blend_square(blend: BlendMode) -> SceneFragment {
|
||||
let mut fragment = SceneFragment::default();
|
||||
let mut sb = SceneBuilder::for_fragment(&mut fragment);
|
||||
render_blend_square(&mut sb, blend, Affine::IDENTITY);
|
||||
sb.finish();
|
||||
fragment
|
||||
}
|
||||
|
||||
fn make_ellipse(cx: f32, cy: f32, rx: f32, ry: f32) -> impl Iterator<Item = PathElement> + Clone {
|
||||
let a = 0.551915024494;
|
||||
let arx = a * rx;
|
||||
let ary = a * ry;
|
||||
let elements = [
|
||||
PathElement::MoveTo(Point::new(cx + rx, cy)),
|
||||
PathElement::CurveTo(
|
||||
Point::new(cx + rx, cy + ary),
|
||||
Point::new(cx + arx, cy + ry),
|
||||
Point::new(cx, cy + ry),
|
||||
),
|
||||
PathElement::CurveTo(
|
||||
Point::new(cx - arx, cy + ry),
|
||||
Point::new(cx - rx, cy + ary),
|
||||
Point::new(cx - rx, cy),
|
||||
),
|
||||
PathElement::CurveTo(
|
||||
Point::new(cx - rx, cy - ary),
|
||||
Point::new(cx - arx, cy - ry),
|
||||
Point::new(cx, cy - ry),
|
||||
),
|
||||
PathElement::CurveTo(
|
||||
Point::new(cx + arx, cy - ry),
|
||||
Point::new(cx + rx, cy - ary),
|
||||
Point::new(cx + rx, cy),
|
||||
),
|
||||
PathElement::Close,
|
||||
];
|
||||
(0..elements.len()).map(move |i| elements[i])
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue