Merge branch 'master' into android2

This commit is contained in:
Raph Levien 2021-04-12 16:00:50 -07:00
commit e1aced9c5a
14 changed files with 144 additions and 151 deletions

View file

@ -41,7 +41,7 @@ fn main() -> Result<(), Error> {
render_scene(&mut ctx); render_scene(&mut ctx);
let n_paths = ctx.path_count(); let n_paths = ctx.path_count();
let n_pathseg = ctx.pathseg_count(); let n_pathseg = ctx.pathseg_count();
let n_trans = ctx.pathseg_count(); let n_trans = ctx.trans_count();
let scene = ctx.get_scene_buf(); let scene = ctx.get_scene_buf();
let renderer = Renderer::new(&session, scene, n_paths, n_pathseg, n_trans)?; let renderer = Renderer::new(&session, scene, n_paths, n_pathseg, n_trans)?;

View file

@ -48,13 +48,13 @@ void main() {
if (element_ix < conf.n_elements) { if (element_ix < conf.n_elements) {
AnnotatedTag tag = Annotated_tag(conf.anno_alloc, ref); AnnotatedTag tag = Annotated_tag(conf.anno_alloc, ref);
switch (tag.tag) { switch (tag.tag) {
case Annotated_Image:
case Annotated_BeginClip:
case Annotated_Color: case Annotated_Color:
if (fill_mode_from_flags(tag.flags) != MODE_NONZERO) { if (fill_mode_from_flags(tag.flags) != MODE_NONZERO) {
break; break;
} }
// Fall through. // Fall through.
case Annotated_Image:
case Annotated_BeginClip:
PathRef path_ref = PathRef(conf.tile_alloc.offset + element_ix * Path_size); PathRef path_ref = PathRef(conf.tile_alloc.offset + element_ix * Path_size);
Path path = Path_read(conf.tile_alloc, path_ref); Path path = Path_read(conf.tile_alloc, path_ref);
sh_row_width[th_ix] = path.bbox.z - path.bbox.x; sh_row_width[th_ix] = path.bbox.z - path.bbox.x;

Binary file not shown.

View file

@ -91,6 +91,23 @@ bool alloc_cmd(inout Alloc cmd_alloc, inout CmdRef cmd_ref, inout uint cmd_limit
return true; return true;
} }
void write_fill(Alloc alloc, inout CmdRef cmd_ref, uint flags, Tile tile, float linewidth) {
if (fill_mode_from_flags(flags) == MODE_NONZERO) {
if (tile.tile.offset != 0) {
CmdFill cmd_fill = CmdFill(tile.tile.offset, tile.backdrop);
Cmd_Fill_write(alloc, cmd_ref, cmd_fill);
cmd_ref.offset += 4 + CmdFill_size;
} else {
Cmd_Solid_write(alloc, cmd_ref);
cmd_ref.offset += 4;
}
} else {
CmdStroke cmd_stroke = CmdStroke(tile.tile.offset, 0.5 * linewidth);
Cmd_Stroke_write(alloc, cmd_ref, cmd_stroke);
cmd_ref.offset += 4 + CmdStroke_size;
}
}
void main() { void main() {
if (mem_error != NO_ERROR) { if (mem_error != NO_ERROR) {
return; return;
@ -135,6 +152,12 @@ void main() {
uint part_start_ix = 0; uint part_start_ix = 0;
uint ready_ix = 0; uint ready_ix = 0;
// Leave room for the fine rasterizer scratch allocation.
Alloc scratch_alloc = slice_mem(cmd_alloc, 0, Alloc_size);
cmd_ref.offset += Alloc_size;
uint num_begin_slots = 0;
uint begin_slot = 0;
while (true) { while (true) {
for (uint i = 0; i < N_SLICE; i++) { for (uint i = 0; i < N_SLICE; i++) {
sh_bitmaps[i][th_ix] = 0; sh_bitmaps[i][th_ix] = 0;
@ -320,20 +343,7 @@ void main() {
if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) {
break; break;
} }
if (fill_mode_from_flags(tag.flags) == MODE_NONZERO) { write_fill(cmd_alloc, cmd_ref, tag.flags, tile, fill.linewidth);
if (tile.tile.offset != 0) {
CmdFill cmd_fill = CmdFill(tile.tile.offset, tile.backdrop);
Cmd_Fill_write(cmd_alloc, cmd_ref, cmd_fill);
cmd_ref.offset += 4 + CmdFill_size;
} else {
Cmd_Solid_write(cmd_alloc, cmd_ref);
cmd_ref.offset += 4;
}
} else {
CmdStroke cmd_stroke = CmdStroke(tile.tile.offset, 0.5 * fill.linewidth);
Cmd_Stroke_write(cmd_alloc, cmd_ref, cmd_stroke);
cmd_ref.offset += 4 + CmdStroke_size;
}
Cmd_Color_write(cmd_alloc, cmd_ref, CmdColor(fill.rgba_color)); Cmd_Color_write(cmd_alloc, cmd_ref, CmdColor(fill.rgba_color));
cmd_ref.offset += 4 + CmdColor_size; cmd_ref.offset += 4 + CmdColor_size;
break; break;
@ -344,20 +354,7 @@ void main() {
if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) {
break; break;
} }
if (fill_mode_from_flags(tag.flags) == MODE_NONZERO) { write_fill(cmd_alloc, cmd_ref, tag.flags, tile, fill_img.linewidth);
if (tile.tile.offset != 0) {
CmdFill cmd_fill = CmdFill(tile.tile.offset, tile.backdrop);
Cmd_Fill_write(cmd_alloc, cmd_ref, cmd_fill);
cmd_ref.offset += 4 + CmdFill_size;
} else {
Cmd_Solid_write(cmd_alloc, cmd_ref);
cmd_ref.offset += 4;
}
} else {
CmdStroke cmd_stroke = CmdStroke(tile.tile.offset, 0.5 * fill_img.linewidth);
Cmd_Stroke_write(cmd_alloc, cmd_ref, cmd_stroke);
cmd_ref.offset += 4 + CmdStroke_size;
}
Cmd_Image_write(cmd_alloc, cmd_ref, CmdImage(fill_img.index, fill_img.offset)); Cmd_Image_write(cmd_alloc, cmd_ref, CmdImage(fill_img.index, fill_img.offset));
cmd_ref.offset += 4 + CmdImage_size; cmd_ref.offset += 4 + CmdImage_size;
break; break;
@ -373,27 +370,14 @@ void main() {
if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) { if (!alloc_cmd(cmd_alloc, cmd_ref, cmd_limit)) {
break; break;
} }
if (fill_mode_from_flags(tag.flags) == MODE_NONZERO) { write_fill(cmd_alloc, cmd_ref, tag.flags, tile, begin_clip.linewidth);
if (tile.tile.offset != 0) {
CmdFill cmd_fill = CmdFill(tile.tile.offset, tile.backdrop);
Cmd_Fill_write(cmd_alloc, cmd_ref, cmd_fill);
cmd_ref.offset += 4 + CmdFill_size;
} else {
// TODO: here is where a bunch of optimization magic should happen
float alpha = tile.backdrop == 0 ? 0.0 : 1.0;
Cmd_Alpha_write(cmd_alloc, cmd_ref, CmdAlpha(alpha));
cmd_ref.offset += 4 + CmdAlpha_size;
}
} else {
CmdStroke cmd_stroke = CmdStroke(tile.tile.offset, 0.5 * begin_clip.linewidth);
Cmd_Stroke_write(cmd_alloc, cmd_ref, cmd_stroke);
cmd_ref.offset += 4 + CmdStroke_size;
}
Cmd_BeginClip_write(cmd_alloc, cmd_ref); Cmd_BeginClip_write(cmd_alloc, cmd_ref);
cmd_ref.offset += 4; cmd_ref.offset += 4;
if (clip_depth < 32) { if (clip_depth < 32) {
clip_one_mask &= ~(1 << clip_depth); clip_one_mask &= ~(1 << clip_depth);
} }
begin_slot++;
num_begin_slots = max(num_begin_slots, begin_slot);
} }
clip_depth++; clip_depth++;
break; break;
@ -405,6 +389,7 @@ void main() {
} }
Cmd_Solid_write(cmd_alloc, cmd_ref); Cmd_Solid_write(cmd_alloc, cmd_ref);
cmd_ref.offset += 4; cmd_ref.offset += 4;
begin_slot--;
Cmd_EndClip_write(cmd_alloc, cmd_ref); Cmd_EndClip_write(cmd_alloc, cmd_ref);
cmd_ref.offset += 4; cmd_ref.offset += 4;
} }
@ -432,5 +417,13 @@ void main() {
} }
if (bin_tile_x + tile_x < conf.width_in_tiles && bin_tile_y + tile_y < conf.height_in_tiles) { if (bin_tile_x + tile_x < conf.width_in_tiles && bin_tile_y + tile_y < conf.height_in_tiles) {
Cmd_End_write(cmd_alloc, cmd_ref); Cmd_End_write(cmd_alloc, cmd_ref);
if (num_begin_slots > 0) {
// Write scratch allocation: one state per BeginClip per rasterizer chunk.
uint scratch_size = num_begin_slots * TILE_WIDTH_PX * TILE_HEIGHT_PX * CLIP_STATE_SIZE * 4;
MallocResult scratch = malloc(scratch_size);
// Ignore scratch.failed; we don't use the allocation and kernel4
// checks for memory overflow before using it.
alloc_write(scratch_alloc, scratch_alloc.offset, scratch.alloc);
}
} }
} }

Binary file not shown.

View file

@ -386,10 +386,10 @@ void main() {
case Element_BeginClip: case Element_BeginClip:
Clip begin_clip = Element_BeginClip_read(this_ref); Clip begin_clip = Element_BeginClip_read(this_ref);
AnnoBeginClip anno_begin_clip; AnnoBeginClip anno_begin_clip;
// This is the absolute bbox, it's been transformed during encoding.
anno_begin_clip.bbox = begin_clip.bbox;
if (is_stroke) { if (is_stroke) {
vec2 lw = get_linewidth(st); vec2 lw = get_linewidth(st);
// This is the absolute bbox, it's been transformed during encoding.
anno_begin_clip.bbox = begin_clip.bbox + vec4(-lw, lw);
anno_begin_clip.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z)); anno_begin_clip.linewidth = st.linewidth * sqrt(abs(st.mat.x * st.mat.w - st.mat.y * st.mat.z));
} else { } else {
anno_begin_clip.bbox = begin_clip.bbox; anno_begin_clip.bbox = begin_clip.bbox;

Binary file not shown.

View file

@ -37,30 +37,6 @@ layout(rgba8, set = 0, binding = 3) uniform readonly image2D images[1];
#include "ptcl.h" #include "ptcl.h"
#include "tile.h" #include "tile.h"
#define BLEND_STACK_SIZE 4
// Layout of a clip scratch frame:
// Each frame is WIDTH * HEIGHT 32-bit words, then a link reference.
// Link offset and frame size in 32-bit words.
#define CLIP_LINK_OFFSET (TILE_WIDTH_PX * TILE_HEIGHT_PX)
#define CLIP_BUF_SIZE (CLIP_LINK_OFFSET + 1)
shared MallocResult sh_clip_alloc;
// Allocate a scratch buffer for clipping.
MallocResult alloc_clip_buf(uint link) {
if (gl_LocalInvocationID.x == 0 && gl_LocalInvocationID.y == 0) {
MallocResult m = malloc(CLIP_BUF_SIZE * 4);
if (!m.failed) {
write_mem(m.alloc, (m.alloc.offset >> 2) + CLIP_LINK_OFFSET, link);
}
sh_clip_alloc = m;
}
barrier();
return sh_clip_alloc;
}
vec3 tosRGB(vec3 rgb) { vec3 tosRGB(vec3 rgb) {
bvec3 cutoff = greaterThanEqual(rgb, vec3(0.0031308)); bvec3 cutoff = greaterThanEqual(rgb, vec3(0.0031308));
vec3 below = vec3(12.92)*rgb; vec3 below = vec3(12.92)*rgb;
@ -117,27 +93,31 @@ void main() {
Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC); Alloc cmd_alloc = slice_mem(conf.ptcl_alloc, tile_ix * PTCL_INITIAL_ALLOC, PTCL_INITIAL_ALLOC);
CmdRef cmd_ref = CmdRef(cmd_alloc.offset); CmdRef cmd_ref = CmdRef(cmd_alloc.offset);
// Read scrach space allocation, written first in the command list.
Alloc scratch_alloc = alloc_read(cmd_alloc, cmd_ref.offset);
cmd_ref.offset += Alloc_size;
uvec2 xy_uint = uvec2(gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_WorkGroupID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y); uvec2 xy_uint = uvec2(gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_WorkGroupID.x, gl_LocalInvocationID.y + TILE_HEIGHT_PX * gl_WorkGroupID.y);
vec2 xy = vec2(xy_uint); vec2 xy = vec2(xy_uint);
vec3 rgb[CHUNK]; vec4 rgba[CHUNK];
uint blend_stack[BLEND_STACK_SIZE][CHUNK];
uint blend_spill = 0;
uint blend_sp = 0;
Alloc clip_tos = new_alloc(0, 0);
for (uint i = 0; i < CHUNK; i++) { for (uint i = 0; i < CHUNK; i++) {
rgb[i] = vec3(0.5); rgba[i] = vec4(0.0);
// TODO: remove this debug image support when the actual image method is plumbed.
#ifdef DEBUG_IMAGES
#ifdef ENABLE_IMAGE_INDICES #ifdef ENABLE_IMAGE_INDICES
if (xy_uint.x < 1024 && xy_uint.y < 1024) { if (xy_uint.x < 1024 && xy_uint.y < 1024) {
rgb[i] = imageLoad(images[gl_WorkGroupID.x / 64], ivec2(xy_uint + chunk_offset(i))/4).rgb; rgba[i] = imageLoad(images[gl_WorkGroupID.x / 64], ivec2(xy_uint + chunk_offset(i))/4);
} }
#else #else
if (xy_uint.x < 1024 && xy_uint.y < 1024) { if (xy_uint.x < 1024 && xy_uint.y < 1024) {
rgb[i] = imageLoad(images[0], ivec2(xy_uint + chunk_offset(i))/4).rgb; rgb[i] = imageLoad(images[0], ivec2(xy_uint + chunk_offset(i))/4).rgb;
} }
#endif
#endif #endif
} }
float area[CHUNK]; float area[CHUNK];
uint clip_depth = 0;
while (true) { while (true) {
uint tag = Cmd_tag(cmd_alloc, cmd_ref).tag; uint tag = Cmd_tag(cmd_alloc, cmd_ref).tag;
if (tag == Cmd_End) { if (tag == Cmd_End) {
@ -216,7 +196,7 @@ void main() {
vec4 fg = unpacksRGB(color.rgba_color); vec4 fg = unpacksRGB(color.rgba_color);
for (uint k = 0; k < CHUNK; k++) { for (uint k = 0; k < CHUNK; k++) {
vec4 fg_k = fg * area[k]; vec4 fg_k = fg * area[k];
rgb[k] = rgb[k] * (1.0 - fg_k.a) + fg_k.rgb; rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k;
} }
cmd_ref.offset += 4 + CmdColor_size; cmd_ref.offset += 4 + CmdColor_size;
break; break;
@ -225,58 +205,46 @@ void main() {
vec4 img[CHUNK] = fillImage(xy_uint, fill_img); vec4 img[CHUNK] = fillImage(xy_uint, fill_img);
for (uint k = 0; k < CHUNK; k++) { for (uint k = 0; k < CHUNK; k++) {
vec4 fg_k = img[k] * area[k]; vec4 fg_k = img[k] * area[k];
rgb[k] = rgb[k] * (1.0 - fg_k.a) + fg_k.rgb; rgba[k] = rgba[k] * (1.0 - fg_k.a) + fg_k;
} }
cmd_ref.offset += 4 + CmdImage_size; cmd_ref.offset += 4 + CmdImage_size;
break; break;
case Cmd_BeginClip: case Cmd_BeginClip:
uint blend_slot = blend_sp % BLEND_STACK_SIZE; uint base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX +
if (blend_sp == blend_spill + BLEND_STACK_SIZE) { gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y);
// spill to scratch buffer
MallocResult m = alloc_clip_buf(clip_tos.offset);
if (m.failed) {
return;
}
clip_tos = m.alloc;
uint base_ix = (clip_tos.offset >> 2) + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y;
for (uint k = 0; k < CHUNK; k++) { for (uint k = 0; k < CHUNK; k++) {
uvec2 offset = chunk_offset(k); uvec2 offset = chunk_offset(k);
write_mem(clip_tos, base_ix + offset.x + offset.y * TILE_WIDTH_PX, blend_stack[blend_slot][k]); uint srgb = packsRGB(vec4(rgba[k]));
float alpha = clamp(abs(area[k]), 0.0, 1.0);
write_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), srgb);
write_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX), floatBitsToUint(alpha));
rgba[k] = vec4(0.0);
} }
blend_spill++; clip_depth++;
}
for (uint k = 0; k < CHUNK; k++) {
blend_stack[blend_slot][k] = packsRGB(vec4(rgb[k], clamp(abs(area[k]), 0.0, 1.0)));
}
blend_sp++;
cmd_ref.offset += 4; cmd_ref.offset += 4;
break; break;
case Cmd_EndClip: case Cmd_EndClip:
blend_slot = (blend_sp - 1) % BLEND_STACK_SIZE; clip_depth--;
if (blend_sp == blend_spill) { base_ix = (scratch_alloc.offset >> 2) + CLIP_STATE_SIZE * (clip_depth * TILE_WIDTH_PX * TILE_HEIGHT_PX +
uint base_ix = (clip_tos.offset >> 2) + gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y; gl_LocalInvocationID.x + TILE_WIDTH_PX * gl_LocalInvocationID.y);
for (uint k = 0; k < CHUNK; k++) { for (uint k = 0; k < CHUNK; k++) {
uvec2 offset = chunk_offset(k); uvec2 offset = chunk_offset(k);
blend_stack[blend_slot][k] = read_mem(clip_tos, base_ix + offset.x + offset.y * TILE_WIDTH_PX); uint srgb = read_mem(scratch_alloc, base_ix + 0 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX));
} uint alpha = read_mem(scratch_alloc, base_ix + 1 + CLIP_STATE_SIZE * (offset.x + offset.y * TILE_WIDTH_PX));
clip_tos.offset = read_mem(clip_tos, (clip_tos.offset >> 2) + CLIP_LINK_OFFSET); vec4 bg = unpacksRGB(srgb);
blend_spill--; vec4 fg = rgba[k] * area[k] * uintBitsToFloat(alpha);
} rgba[k] = bg * (1.0 - fg.a) + fg;
blend_sp--;
for (uint k = 0; k < CHUNK; k++) {
vec4 rgba = unpacksRGB(blend_stack[blend_slot][k]);
rgb[k] = mix(rgba.rgb, rgb[k], area[k] * rgba.a);
} }
cmd_ref.offset += 4; cmd_ref.offset += 4;
break; break;
case Cmd_Jump: case Cmd_Jump:
cmd_ref = CmdRef(Cmd_Jump_read(cmd_alloc, cmd_ref).new_ref); cmd_ref = CmdRef(Cmd_Jump_read(cmd_alloc, cmd_ref).new_ref);
cmd_alloc.offset = cmd_ref.offset; cmd_alloc.offset = cmd_ref.offset;
continue; break;
} }
} }
for (uint i = 0; i < CHUNK; i++) { for (uint i = 0; i < CHUNK; i++) {
imageStore(image, ivec2(xy_uint + chunk_offset(i)), vec4(tosRGB(rgb[i]), 1.0)); imageStore(image, ivec2(xy_uint + chunk_offset(i)), vec4(tosRGB(rgba[i].rgb), rgba[i].a));
} }
} }

Binary file not shown.

Binary file not shown.

View file

@ -21,7 +21,11 @@ layout(set = 0, binding = 0) buffer Memory {
#define ERR_OUT_OF_BOUNDS 2 #define ERR_OUT_OF_BOUNDS 2
#define ERR_UNALIGNED_ACCESS 3 #define ERR_UNALIGNED_ACCESS 3
#ifdef MEM_DEBUG
#define Alloc_size 16
#else
#define Alloc_size 8 #define Alloc_size 8
#endif
// Alloc represents a memory allocation. // Alloc represents a memory allocation.
struct Alloc { struct Alloc {
@ -39,7 +43,7 @@ struct MallocResult {
bool failed; bool failed;
}; };
// new_alloc synthesizes an Alloc when its offset and size are derived. // new_alloc synthesizes an Alloc from an offset and size.
Alloc new_alloc(uint offset, uint size) { Alloc new_alloc(uint offset, uint size) {
Alloc a; Alloc a;
a.offset = offset; a.offset = offset;
@ -118,3 +122,21 @@ Alloc slice_mem(Alloc a, uint offset, uint size) {
#endif #endif
return new_alloc(a.offset + offset, size); return new_alloc(a.offset + offset, size);
} }
// alloc_write writes alloc to memory at offset bytes.
void alloc_write(Alloc a, uint offset, Alloc alloc) {
write_mem(a, offset >> 2, alloc.offset);
#ifdef MEM_DEBUG
write_mem(a, (offset >> 2) + 1, alloc.size);
#endif
}
// alloc_read reads an Alloc from memory at offset bytes.
Alloc alloc_read(Alloc a, uint offset) {
Alloc alloc;
alloc.offset = read_mem(a, offset >> 2);
#ifdef MEM_DEBUG
alloc.size = read_mem(a, (offset >> 2) + 1);
#endif
return alloc;
}

View file

@ -45,6 +45,9 @@ struct Config {
#define MODE_NONZERO 0 #define MODE_NONZERO 0
#define MODE_STROKE 1 #define MODE_STROKE 1
// Size of kernel4 clip state, in words.
#define CLIP_STATE_SIZE 2
// fill_mode_from_flags extracts the fill mode from tag flags. // fill_mode_from_flags extracts the fill mode from tag flags.
uint fill_mode_from_flags(uint flags) { uint fill_mode_from_flags(uint flags) {
return flags & 0x1; return flags & 0x1;

View file

@ -127,25 +127,12 @@ fn render_clip_test(rc: &mut impl RenderContext) {
#[allow(unused)] #[allow(unused)]
fn render_alpha_test(rc: &mut impl RenderContext) { fn render_alpha_test(rc: &mut impl RenderContext) {
// Alpha compositing tests. kernel4 expects colors encoded in alpha-premultiplied sRGB: // Alpha compositing tests.
// rc.fill(diamond(Point::new(1024.0, 100.0)), &Color::Rgba32(0xff0000ff));
// [α,sRGB(α⋅R),sRGB(α⋅G),sRGB(α⋅B)] rc.fill(diamond(Point::new(1024.0, 125.0)), &Color::Rgba32(0x00ff0080));
//
// See also http://ssp.impulsetrain.com/gamma-premult.html.
rc.fill(
diamond(Point::new(1024.0, 100.0)),
&Color::Rgba32(0xff0000ff),
);
rc.fill(
diamond(Point::new(1024.0, 125.0)),
&Color::Rgba32(0x00ba0080),
);
rc.save(); rc.save();
rc.clip(diamond(Point::new(1024.0, 150.0))); rc.clip(diamond(Point::new(1024.0, 150.0)));
rc.fill( rc.fill(diamond(Point::new(1024.0, 175.0)), &Color::Rgba32(0x0000ff80));
diamond(Point::new(1024.0, 175.0)),
&Color::Rgba32(0x0000ba80),
);
rc.restore(); rc.restore();
} }

View file

@ -1,21 +1,19 @@
use std::{borrow::Cow, ops::RangeBounds}; use std::{borrow::Cow, ops::RangeBounds};
use piet_gpu_types::encoder::{Encode, Encoder};
use piet_gpu_types::scene::{
Clip, CubicSeg, Element, FillColor, LineSeg, QuadSeg, SetFillMode, SetLineWidth, Transform,
};
use piet::{ use piet::{
kurbo::{Affine, Insets, PathEl, Point, Rect, Shape, Size}, HitTestPosition,
HitTestPosition, TextAttribute, TextStorage, kurbo::{Affine, Insets, PathEl, Point, Rect, Shape, Size}, TextAttribute, TextStorage,
}; };
use piet::{ use piet::{
Color, Error, FixedGradient, FontFamily, HitTestPoint, ImageFormat, InterpolationMode, Color, Error, FixedGradient, FontFamily, HitTestPoint, ImageFormat, InterpolationMode,
IntoBrush, LineMetric, RenderContext, StrokeStyle, Text, TextLayout, TextLayoutBuilder, IntoBrush, LineMetric, RenderContext, StrokeStyle, Text, TextLayout, TextLayoutBuilder,
}; };
use piet_gpu_types::encoder::{Encode, Encoder};
use piet_gpu_types::scene::{
Clip, CubicSeg, Element, FillColor, LineSeg, QuadSeg, SetFillMode, SetLineWidth, Transform,
};
pub struct PietGpuImage; pub struct PietGpuImage;
#[derive(Clone)] #[derive(Clone)]
@ -139,7 +137,14 @@ impl RenderContext for PietGpuRenderContext {
} }
fn solid_brush(&mut self, color: Color) -> Self::Brush { fn solid_brush(&mut self, color: Color) -> Self::Brush {
PietGpuBrush::Solid(color.as_rgba_u32()) // kernel4 expects colors encoded in alpha-premultiplied sRGB:
//
// [α,sRGB(α⋅R),sRGB(α⋅G),sRGB(α⋅B)]
//
// See also http://ssp.impulsetrain.com/gamma-premult.html.
let (r, g, b, a) = color.as_rgba();
let premul = Color::rgba(to_srgb(from_srgb(r) * a), to_srgb(from_srgb(g) * a), to_srgb(from_srgb(b) * a), a);
PietGpuBrush::Solid(premul.as_rgba_u32())
} }
fn gradient(&mut self, _gradient: impl Into<FixedGradient>) -> Result<Self::Brush, Error> { fn gradient(&mut self, _gradient: impl Into<FixedGradient>) -> Result<Self::Brush, Error> {
@ -177,8 +182,7 @@ impl RenderContext for PietGpuRenderContext {
_brush: &impl IntoBrush<Self>, _brush: &impl IntoBrush<Self>,
_width: f64, _width: f64,
_style: &StrokeStyle, _style: &StrokeStyle,
) { ) {}
}
fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>) { fn fill(&mut self, shape: impl Shape, brush: &impl IntoBrush<Self>) {
let brush = brush.make_brush(self, || shape.bounding_box()).into_owned(); let brush = brush.make_brush(self, || shape.bounding_box()).into_owned();
@ -280,8 +284,7 @@ impl RenderContext for PietGpuRenderContext {
_image: &Self::Image, _image: &Self::Image,
_rect: impl Into<Rect>, _rect: impl Into<Rect>,
_interp: InterpolationMode, _interp: InterpolationMode,
) { ) {}
}
fn draw_image_area( fn draw_image_area(
&mut self, &mut self,
@ -289,8 +292,7 @@ impl RenderContext for PietGpuRenderContext {
_src_rect: impl Into<Rect>, _src_rect: impl Into<Rect>,
_dst_rect: impl Into<Rect>, _dst_rect: impl Into<Rect>,
_interp: InterpolationMode, _interp: InterpolationMode,
) { ) {}
}
fn blurred_rect(&mut self, _rect: Rect, _blur_radius: f64, _brush: &impl IntoBrush<Self>) {} fn blurred_rect(&mut self, _rect: Rect, _blur_radius: f64, _brush: &impl IntoBrush<Self>) {}
@ -587,3 +589,21 @@ fn to_scene_transform(transform: Affine) -> Transform {
translate: [c[4] as f32, c[5] as f32], translate: [c[4] as f32, c[5] as f32],
} }
} }
fn to_srgb(f: f64) -> f64 {
if f <= 0.0031308 {
f * 12.92
} else {
let a = 0.055;
(1. + a) * f64::powf(f, f64::recip(2.4)) - a
}
}
fn from_srgb(f: f64) -> f64 {
if f <= 0.04045 {
f / 12.92
} else {
let a = 0.055;
f64::powf((f + a) * f64::recip(1. + a), 2.4)
}
}