Introduce the concept of a base color in order to handle canvas shadow alpha

correctly
This commit is contained in:
Patrick Walton 2020-04-13 11:52:10 -07:00
parent fda9b8b9e1
commit cdbe2fbb6b
20 changed files with 697 additions and 575 deletions

View File

@ -28,7 +28,7 @@ use pathfinder_content::render_target::RenderTargetId;
use pathfinder_content::stroke::{LineJoin as StrokeLineJoin};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_renderer::paint::{Paint, PaintId};
use pathfinder_renderer::paint::{Paint, PaintCompositeOp};
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, RenderTarget, Scene};
use std::borrow::Cow;
use std::default::Default;
@ -236,15 +236,12 @@ impl CanvasRenderingContext2D {
#[inline]
pub fn shadow_color(&self) -> ColorU {
match self.current_state.shadow_paint {
Paint::Color(color) => color,
_ => panic!("Unexpected shadow paint!"),
}
self.current_state.shadow_color
}
#[inline]
pub fn set_shadow_color(&mut self, new_shadow_color: ColorU) {
self.current_state.shadow_paint = Paint::Color(new_shadow_color);
self.current_state.shadow_color = new_shadow_color;
}
#[inline]
@ -261,16 +258,11 @@ impl CanvasRenderingContext2D {
#[inline]
pub fn fill_path(&mut self, path: Path2D, fill_rule: FillRule) {
let paint = self.current_state.resolve_paint(&self.current_state.fill_paint);
let paint_id = self.canvas.scene.push_paint(&paint);
self.push_path(path.into_outline(), paint_id, fill_rule);
self.push_path(path.into_outline(), PathOp::Fill, fill_rule);
}
#[inline]
pub fn stroke_path(&mut self, path: Path2D) {
let paint = self.current_state.resolve_paint(&self.current_state.stroke_paint);
let paint_id = self.canvas.scene.push_paint(&paint);
let mut stroke_style = self.current_state.resolve_stroke_style();
// The smaller scale is relevant here, as we multiply by it and want to ensure it is always
@ -296,7 +288,7 @@ impl CanvasRenderingContext2D {
stroke_to_fill.offset();
outline = stroke_to_fill.into_outline();
self.push_path(outline, paint_id, FillRule::Winding);
self.push_path(outline, PathOp::Stroke, FillRule::Winding);
}
pub fn clip_path(&mut self, path: Path2D, fill_rule: FillRule) {
@ -310,19 +302,27 @@ impl CanvasRenderingContext2D {
self.current_state.clip_path = Some(clip_path_id);
}
fn push_path(&mut self, mut outline: Outline, paint_id: PaintId, fill_rule: FillRule) {
fn push_path(&mut self, mut outline: Outline, path_op: PathOp, fill_rule: FillRule) {
let paint = self.current_state.resolve_paint(match path_op {
PathOp::Fill => &self.current_state.fill_paint,
PathOp::Stroke => &self.current_state.stroke_paint,
});
let paint_id = self.canvas.scene.push_paint(&paint);
let transform = self.current_state.transform;
let clip_path = self.current_state.clip_path;
let blend_mode = self.current_state.global_composite_operation.to_blend_mode();
outline.transform(&transform);
if !self.current_state.shadow_paint.is_fully_transparent() {
if !self.current_state.shadow_color.is_fully_transparent() {
let mut outline = outline.clone();
outline.transform(&Transform2F::from_translation(self.current_state.shadow_offset));
let shadow_blur_info =
self.push_shadow_blur_render_targets_if_needed(outline.bounds());
push_shadow_blur_render_targets_if_needed(&mut self.canvas.scene,
&self.current_state,
outline.bounds());
if let Some(ref shadow_blur_info) = shadow_blur_info {
outline.transform(&Transform2F::from_translation(-shadow_blur_info.bounds
@ -330,10 +330,19 @@ impl CanvasRenderingContext2D {
.to_f32()));
}
let paint = self.current_state.resolve_paint(&self.current_state.shadow_paint);
let paint_id = self.canvas.scene.push_paint(&paint);
// Per spec the shadow must respect the alpha of the shadowed path, but otherwise have
// the color of the shadow paint.
let mut shadow_paint = (*paint).clone();
let shadow_base_alpha = shadow_paint.base_color().a;
let mut shadow_color = self.current_state.shadow_color.to_f32();
shadow_color.set_a(shadow_color.a() * shadow_base_alpha as f32 / 255.0);
shadow_paint.set_base_color(shadow_color.to_u8());
if let &mut Some(ref mut shadow_paint_overlay) = shadow_paint.overlay_mut() {
shadow_paint_overlay.set_composite_op(PaintCompositeOp::DestIn);
}
let shadow_paint_id = self.canvas.scene.push_paint(&shadow_paint);
let mut path = DrawPath::new(outline, paint_id);
let mut path = DrawPath::new(outline, shadow_paint_id);
if shadow_blur_info.is_none() {
path.set_clip_path(clip_path);
}
@ -341,7 +350,9 @@ impl CanvasRenderingContext2D {
path.set_blend_mode(blend_mode);
self.canvas.scene.push_path(path);
self.composite_shadow_blur_render_targets_if_needed(shadow_blur_info, clip_path);
composite_shadow_blur_render_targets_if_needed(&mut self.canvas.scene,
shadow_blur_info,
clip_path);
}
let mut path = DrawPath::new(outline, paint_id);
@ -349,21 +360,22 @@ impl CanvasRenderingContext2D {
path.set_fill_rule(fill_rule);
path.set_blend_mode(blend_mode);
self.canvas.scene.push_path(path);
}
fn push_shadow_blur_render_targets_if_needed(&mut self, outline_bounds: RectF)
fn push_shadow_blur_render_targets_if_needed(scene: &mut Scene,
current_state: &State,
outline_bounds: RectF)
-> Option<ShadowBlurRenderTargetInfo> {
if self.current_state.shadow_blur == 0.0 {
if current_state.shadow_blur == 0.0 {
return None;
}
let sigma = self.current_state.shadow_blur * 0.5;
let sigma = current_state.shadow_blur * 0.5;
let bounds = outline_bounds.dilate(sigma * 3.0).round_out().to_i32();
let render_target_y = RenderTarget::new(bounds.size(), String::new());
let render_target_id_y = self.canvas.scene.push_render_target(render_target_y);
let render_target_id_y = scene.push_render_target(render_target_y);
let render_target_x = RenderTarget::new(bounds.size(), String::new());
let render_target_id_x = self.canvas.scene.push_render_target(render_target_x);
let render_target_id_x = scene.push_render_target(render_target_x);
Some(ShadowBlurRenderTargetInfo {
id_x: render_target_id_x,
@ -373,7 +385,7 @@ impl CanvasRenderingContext2D {
})
}
fn composite_shadow_blur_render_targets_if_needed(&mut self,
fn composite_shadow_blur_render_targets_if_needed(scene: &mut Scene,
info: Option<ShadowBlurRenderTargetInfo>,
clip_path: Option<ClipPathId>) {
let info = match info {
@ -389,8 +401,8 @@ impl CanvasRenderingContext2D {
paint_x.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::X, sigma }));
paint_y.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::Y, sigma }));
let paint_id_x = self.canvas.scene.push_paint(&Paint::Pattern(paint_x));
let paint_id_y = self.canvas.scene.push_paint(&Paint::Pattern(paint_y));
let paint_id_x = scene.push_paint(&Paint::from_pattern(paint_x));
let paint_id_y = scene.push_paint(&Paint::from_pattern(paint_y));
// TODO(pcwalton): Apply clip as necessary.
let outline_x = Outline::from_rect(RectF::new(vec2f(0.0, 0.0),
@ -400,10 +412,12 @@ impl CanvasRenderingContext2D {
let mut path_y = DrawPath::new(outline_y, paint_id_y);
path_y.set_clip_path(clip_path);
self.canvas.scene.pop_render_target();
self.canvas.scene.push_path(path_x);
self.canvas.scene.pop_render_target();
self.canvas.scene.push_path(path_y);
scene.pop_render_target();
scene.push_path(path_x);
scene.pop_render_target();
scene.push_path(path_y);
}
}
// Transformations
@ -550,7 +564,7 @@ struct State {
line_dash_offset: f32,
fill_paint: Paint,
stroke_paint: Paint,
shadow_paint: Paint,
shadow_color: ColorU,
shadow_blur: f32,
shadow_offset: Vector2F,
text_align: TextAlign,
@ -576,7 +590,7 @@ impl State {
line_dash_offset: 0.0,
fill_paint: Paint::black(),
stroke_paint: Paint::black(),
shadow_paint: Paint::transparent_black(),
shadow_color: ColorU::transparent_black(),
shadow_blur: 0.0,
shadow_offset: Vector2F::zero(),
text_align: TextAlign::Left,
@ -592,7 +606,7 @@ impl State {
fn resolve_paint<'a>(&self, paint: &'a Paint) -> Cow<'a, Paint> {
let mut must_copy = !self.transform.is_identity() || self.global_alpha < 1.0;
if !must_copy {
if let Paint::Pattern(ref pattern) = *paint {
if let Some(ref pattern) = paint.pattern() {
must_copy = self.image_smoothing_enabled != pattern.smoothing_enabled()
}
}
@ -603,8 +617,12 @@ impl State {
let mut paint = (*paint).clone();
paint.apply_transform(&self.transform);
paint.apply_opacity(self.global_alpha);
if let Paint::Pattern(ref mut pattern) = paint {
let mut base_color = paint.base_color().to_f32();
base_color.set_a(base_color.a() * self.global_alpha);
paint.set_base_color(base_color.to_u8());
if let Some(ref mut pattern) = paint.pattern_mut() {
pattern.set_smoothing_enabled(self.image_smoothing_enabled);
}
Cow::Owned(paint)
@ -750,9 +768,9 @@ pub enum FillStyle {
impl FillStyle {
fn into_paint(self) -> Paint {
match self {
FillStyle::Color(color) => Paint::Color(color),
FillStyle::Gradient(gradient) => Paint::Gradient(gradient),
FillStyle::Pattern(pattern) => Paint::Pattern(pattern),
FillStyle::Color(color) => Paint::from_color(color),
FillStyle::Gradient(gradient) => Paint::from_gradient(gradient),
FillStyle::Pattern(pattern) => Paint::from_pattern(pattern),
}
}
}
@ -937,3 +955,8 @@ struct ShadowBlurRenderTargetInfo {
bounds: RectI,
sigma: f32,
}
enum PathOp {
Fill,
Stroke,
}

View File

@ -187,6 +187,26 @@ impl ColorF {
pub fn a(&self) -> f32 {
self.0[3]
}
#[inline]
pub fn set_r(&mut self, r: f32) {
self.0[0] = r;
}
#[inline]
pub fn set_g(&mut self, g: f32) {
self.0[1] = g;
}
#[inline]
pub fn set_b(&mut self, b: f32) {
self.0[2] = b;
}
#[inline]
pub fn set_a(&mut self, a: f32) {
self.0[3] = a;
}
}
impl Debug for ColorF {

View File

@ -150,6 +150,16 @@ impl Gradient {
.lerp(upper_stop.color.to_f32(), (t - lower_stop.offset) / denom)
.to_u8()
}
#[inline]
pub fn is_opaque(&self) -> bool {
self.stops.array.iter().all(|stop| stop.color.is_opaque())
}
#[inline]
pub fn is_fully_transparent(&self) -> bool {
self.stops.array.iter().all(|stop| stop.color.is_fully_transparent())
}
}
impl ColorStop {

View File

@ -31,7 +31,6 @@ pub struct Pattern {
transform: Transform2F,
filter: Option<PatternFilter>,
flags: PatternFlags,
opacity: u8,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
@ -70,7 +69,6 @@ impl Pattern {
transform: Transform2F::default(),
filter: None,
flags: PatternFlags::empty(),
opacity: !0,
}
}
@ -142,19 +140,9 @@ impl Pattern {
self.flags.set(PatternFlags::NO_SMOOTHING, !enable);
}
#[inline]
pub fn opacity(&self) -> u8 {
self.opacity
}
#[inline]
pub fn set_opacity(&mut self, opacity: u8) {
self.opacity = opacity
}
#[inline]
pub fn is_opaque(&self) -> bool {
self.source.is_opaque() && self.opacity == !0
self.source.is_opaque()
}
#[inline]

View File

@ -766,9 +766,9 @@ fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, filter: Option<PatternFi
let mut built_svg = BuiltSVG::from_tree_and_scene(&tree, scene);
if let Some(FilterInfo { filter, render_target_id, render_target_size }) = filter_info {
let mut paint = Pattern::from_render_target(render_target_id, render_target_size);
paint.set_filter(Some(filter));
let paint_id = built_svg.scene.push_paint(&Paint::Pattern(paint));
let mut pattern = Pattern::from_render_target(render_target_id, render_target_size);
pattern.set_filter(Some(filter));
let paint_id = built_svg.scene.push_paint(&Paint::from_pattern(pattern));
let outline = Outline::from_rect(RectI::new(vec2i(0, 0), viewport_size).to_f32());
let path = DrawPath::new(outline, paint_id);

View File

@ -10,7 +10,6 @@
use pathfinder_content::outline::ContourIterFlags;
use pathfinder_content::segment::SegmentKind;
use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::Scene;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use std::fmt;
@ -77,14 +76,9 @@ fn export_pdf<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
};
for (paint, outline, _) in scene.paths() {
match paint {
Paint::Color(color) => pdf.set_fill_color(*color),
Paint::Gradient(_) => {
// TODO(pcwalton): Gradients.
}
Paint::Pattern(_) => {
// TODO(pcwalton): Patterns.
}
// TODO(pcwalton): Gradients and patterns.
if paint.is_color() {
pdf.set_fill_color(paint.base_color());
}
for contour in outline.contours() {
@ -185,17 +179,11 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
}
}
match paint {
Paint::Color(color) => {
// TODO(pcwalton): Gradients and patterns.
if paint.is_color() {
let color = paint.base_color();
writeln!(writer, "{} {} {} setrgbcolor", color.r, color.g, color.b)?;
}
Paint::Gradient(_) => {
// TODO(pcwalton): Gradients.
}
Paint::Pattern(_) => {
// TODO(pcwalton): Patterns.
}
}
writeln!(writer, "fill")?;
}

View File

@ -12,7 +12,7 @@
use crate::concurrent::executor::Executor;
use crate::gpu::renderer::{BlendModeExt, MASK_TILES_ACROSS, MASK_TILES_DOWN};
use crate::gpu_data::{FillBatchPrimitive, RenderCommand, TexturePageId, Tile, TileBatch};
use crate::gpu_data::{FillBatchPrimitive, RenderCommand, Tile, TileBatch};
use crate::gpu_data::{TileBatchTexture, TileObjectPrimitive};
use crate::options::{PreparedBuildOptions, PreparedRenderTransform, RenderCommandListener};
use crate::paint::{PaintInfo, PaintMetadata};
@ -54,9 +54,7 @@ struct BuiltDrawPath {
path: BuiltPath,
blend_mode: BlendMode,
filter: Filter,
color_texture_page_0: TexturePageId,
color_texture_page_1: TexturePageId,
sampling_flags_0: TextureSamplingFlags,
color_texture: Option<TileBatchTexture>,
sampling_flags_1: TextureSamplingFlags,
mask_0_fill_rule: FillRule,
mask_1_fill_rule: Option<FillRule>,
@ -124,8 +122,6 @@ impl<'a> SceneBuilder<'a> {
let PaintInfo {
render_commands,
paint_metadata,
opacity_tile_page,
opacity_tile_transform: _,
render_target_metadata: _,
} = self.scene.build_paint_info(render_transform);
for render_command in render_commands {
@ -152,7 +148,6 @@ impl<'a> SceneBuilder<'a> {
scene: &self.scene,
},
paint_metadata: &paint_metadata,
opacity_tile_page,
built_clip_paths: &built_clip_paths,
})
});
@ -184,7 +179,6 @@ impl<'a> SceneBuilder<'a> {
let DrawPathBuildParams {
path_build_params: PathBuildParams { path_index, view_box, built_options, scene },
paint_metadata,
opacity_tile_page,
built_clip_paths,
} = params;
@ -216,9 +210,7 @@ impl<'a> SceneBuilder<'a> {
path: tiler.object_builder.built_path,
blend_mode: path_object.blend_mode(),
filter: paint_metadata.filter(),
color_texture_page_0: paint_metadata.location.page,
sampling_flags_0: paint_metadata.sampling_flags,
color_texture_page_1: opacity_tile_page,
color_texture: paint_metadata.tile_batch_texture(),
sampling_flags_1: TextureSamplingFlags::empty(),
mask_0_fill_rule: path_object.fill_rule(),
mask_1_fill_rule: built_clip_path.map(|_| FillRule::Winding),
@ -268,14 +260,7 @@ impl<'a> SceneBuilder<'a> {
for draw_path_index in start_draw_path_index..end_draw_path_index {
let built_draw_path = &built_draw_paths[draw_path_index as usize];
let layer_z_buffer = layer_z_buffers_stack.last().unwrap();
let color_texture_0 = Some(TileBatchTexture {
page: built_draw_path.color_texture_page_0,
sampling_flags: built_draw_path.sampling_flags_0,
});
let color_texture_1 = Some(TileBatchTexture {
page: built_draw_path.color_texture_page_1,
sampling_flags: built_draw_path.sampling_flags_1,
});
let color_texture = built_draw_path.color_texture;
debug_assert!(built_draw_path.path.empty_tiles.is_empty() ||
built_draw_path.blend_mode.is_destructive());
@ -284,7 +269,6 @@ impl<'a> SceneBuilder<'a> {
&built_draw_path.path.empty_tiles,
current_depth,
None,
None,
built_draw_path.blend_mode,
built_draw_path.filter,
None,
@ -294,8 +278,7 @@ impl<'a> SceneBuilder<'a> {
layer_z_buffer,
&built_draw_path.path.single_mask_tiles,
current_depth,
color_texture_0,
color_texture_1,
color_texture,
built_draw_path.blend_mode,
built_draw_path.filter,
Some(built_draw_path.mask_0_fill_rule),
@ -306,8 +289,7 @@ impl<'a> SceneBuilder<'a> {
layer_z_buffer,
&built_draw_path.path.dual_mask_tiles,
current_depth,
color_texture_0,
color_texture_1,
color_texture,
built_draw_path.blend_mode,
built_draw_path.filter,
Some(built_draw_path.mask_0_fill_rule),
@ -320,8 +302,7 @@ impl<'a> SceneBuilder<'a> {
layer_z_buffer,
tiles,
current_depth,
color_texture_0,
color_texture_1,
color_texture,
built_draw_path.blend_mode,
built_draw_path.filter,
None,
@ -386,8 +367,7 @@ impl<'a> SceneBuilder<'a> {
layer_z_buffer: &ZBuffer,
alpha_tiles: &[Tile],
current_depth: u32,
color_texture_0: Option<TileBatchTexture>,
color_texture_1: Option<TileBatchTexture>,
color_texture: Option<TileBatchTexture>,
blend_mode: BlendMode,
filter: Filter,
mask_0_fill_rule: Option<FillRule>,
@ -405,14 +385,12 @@ impl<'a> SceneBuilder<'a> {
match culled_tiles.display_list.last() {
Some(&CulledDisplayItem::DrawTiles(TileBatch {
tiles: _,
color_texture_0: ref batch_color_texture_0,
color_texture_1: ref batch_color_texture_1,
color_texture: ref batch_color_texture,
blend_mode: batch_blend_mode,
filter: batch_filter,
mask_0_fill_rule: batch_mask_0_fill_rule,
mask_1_fill_rule: batch_mask_1_fill_rule,
})) if *batch_color_texture_0 == color_texture_0 &&
*batch_color_texture_1 == color_texture_1 &&
})) if *batch_color_texture == color_texture &&
batch_blend_mode == blend_mode &&
batch_filter == filter &&
batch_mask_0_fill_rule == mask_0_fill_rule &&
@ -421,8 +399,7 @@ impl<'a> SceneBuilder<'a> {
_ => {
let batch = TileBatch {
tiles: vec![],
color_texture_0,
color_texture_1,
color_texture,
blend_mode,
filter,
mask_0_fill_rule,
@ -506,7 +483,6 @@ struct PathBuildParams<'a> {
struct DrawPathBuildParams<'a> {
path_build_params: PathBuildParams<'a>,
paint_metadata: &'a [PaintMetadata],
opacity_tile_page: TexturePageId,
built_clip_paths: &'a [BuiltPath],
}

View File

@ -17,6 +17,7 @@ use crate::gpu::shaders::{TileProgram, TileVertexArray};
use crate::gpu_data::{FillBatchPrimitive, RenderCommand, TextureLocation, TextureMetadataEntry};
use crate::gpu_data::{TexturePageDescriptor, TexturePageId, Tile, TileBatchTexture};
use crate::options::BoundingQuad;
use crate::paint::PaintCompositeOp;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use half::f16;
use pathfinder_color::{self as color, ColorF, ColorU};
@ -55,8 +56,8 @@ const SQRT_2_PI_INV: f32 = 0.3989422804014327;
const TEXTURE_CACHE_SIZE: usize = 8;
const TIMER_QUERY_CACHE_SIZE: usize = 8;
const TEXTURE_METADATA_ENTRIES_PER_ROW: i32 = 256;
const TEXTURE_METADATA_TEXTURE_WIDTH: i32 = TEXTURE_METADATA_ENTRIES_PER_ROW * 2;
const TEXTURE_METADATA_ENTRIES_PER_ROW: i32 = 128;
const TEXTURE_METADATA_TEXTURE_WIDTH: i32 = TEXTURE_METADATA_ENTRIES_PER_ROW * 4;
const TEXTURE_METADATA_TEXTURE_HEIGHT: i32 = 65536 / TEXTURE_METADATA_ENTRIES_PER_ROW;
// FIXME(pcwalton): Shrink this again!
@ -66,7 +67,8 @@ const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * MASK_TILES_DOWN as i32
const COMBINER_CTRL_MASK_WINDING: i32 = 0x1;
const COMBINER_CTRL_MASK_EVEN_ODD: i32 = 0x2;
const COMBINER_CTRL_COLOR_ENABLE_MASK: i32 = 0x1;
const COMBINER_CTRL_COLOR_COMBINE_SRC_IN: i32 = 0x1;
const COMBINER_CTRL_COLOR_COMBINE_DEST_IN: i32 = 0x2;
const COMBINER_CTRL_FILTER_RADIAL_GRADIENT: i32 = 0x1;
const COMBINER_CTRL_FILTER_TEXT: i32 = 0x2;
@ -91,9 +93,8 @@ const COMBINER_CTRL_COMPOSITE_LUMINOSITY: i32 = 0xf;
const COMBINER_CTRL_MASK_0_SHIFT: i32 = 0;
const COMBINER_CTRL_MASK_1_SHIFT: i32 = 2;
const COMBINER_CTRL_COLOR_0_FILTER_SHIFT: i32 = 4;
const COMBINER_CTRL_COLOR_0_ENABLE_SHIFT: i32 = 6;
const COMBINER_CTRL_COLOR_1_ENABLE_SHIFT: i32 = 7;
const COMBINER_CTRL_COLOR_FILTER_SHIFT: i32 = 4;
const COMBINER_CTRL_COLOR_COMBINE_SHIFT: i32 = 6;
const COMBINER_CTRL_COMPOSITE_SHIFT: i32 = 8;
pub struct Renderer<D>
@ -330,8 +331,7 @@ where
self.stats.alpha_tile_count += count;
self.upload_tiles(&batch.tiles);
self.draw_tiles(count as u32,
batch.color_texture_0,
batch.color_texture_1,
batch.color_texture,
batch.mask_0_fill_rule,
batch.mask_1_fill_rule,
batch.blend_mode,
@ -486,6 +486,7 @@ where
TEXTURE_METADATA_TEXTURE_WIDTH * 4) as usize;
let mut texels = Vec::with_capacity(padded_texel_size);
for entry in metadata {
let base_color = entry.base_color.to_f32();
texels.extend_from_slice(&[
f16::from_f32(entry.color_0_transform.m11()),
f16::from_f32(entry.color_0_transform.m21()),
@ -493,7 +494,15 @@ where
f16::from_f32(entry.color_0_transform.m22()),
f16::from_f32(entry.color_0_transform.m31()),
f16::from_f32(entry.color_0_transform.m32()),
f16::from_f32(entry.opacity),
f16::default(),
f16::default(),
f16::from_f32(base_color.r()),
f16::from_f32(base_color.g()),
f16::from_f32(base_color.b()),
f16::from_f32(base_color.a()),
f16::default(),
f16::default(),
f16::default(),
f16::default(),
]);
}
@ -618,7 +627,6 @@ where
fn draw_tiles(&mut self,
tile_count: u32,
color_texture_0: Option<TileBatchTexture>,
color_texture_1: Option<TileBatchTexture>,
mask_0_fill_rule: Option<FillRule>,
mask_1_fill_rule: Option<FillRule>,
blend_mode: BlendMode,
@ -687,16 +695,9 @@ where
uniforms.push((&self.tile_program.color_texture_0_size_uniform,
UniformData::Vec2(color_texture_size.0)));
textures.push(color_texture_page);
ctrl |= COMBINER_CTRL_COLOR_ENABLE_MASK << COMBINER_CTRL_COLOR_0_ENABLE_SHIFT;
}
if let Some(color_texture) = color_texture_1 {
let color_texture_page = self.texture_page(color_texture.page);
self.device.set_texture_sampling_mode(color_texture_page,
color_texture.sampling_flags);
uniforms.push((&self.tile_program.color_texture_1_uniform,
UniformData::TextureUnit(textures.len() as u32)));
textures.push(color_texture_page);
ctrl |= COMBINER_CTRL_COLOR_ENABLE_MASK << COMBINER_CTRL_COLOR_1_ENABLE_SHIFT;
ctrl |= color_texture.composite_op.to_combine_mode() <<
COMBINER_CTRL_COLOR_COMBINE_SHIFT;
}
ctrl |= blend_mode.to_composite_ctrl() << COMBINER_CTRL_COMPOSITE_SHIFT;
@ -704,7 +705,7 @@ where
match filter {
Filter::None => {}
Filter::RadialGradient { line, radii, uv_origin } => {
ctrl |= COMBINER_CTRL_FILTER_RADIAL_GRADIENT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT;
ctrl |= COMBINER_CTRL_FILTER_RADIAL_GRADIENT << COMBINER_CTRL_COLOR_FILTER_SHIFT;
self.set_uniforms_for_radial_gradient_filter(&mut uniforms, line, radii, uv_origin)
}
Filter::PatternFilter(PatternFilter::Text {
@ -713,7 +714,7 @@ where
defringing_kernel,
gamma_correction,
}) => {
ctrl |= COMBINER_CTRL_FILTER_TEXT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT;
ctrl |= COMBINER_CTRL_FILTER_TEXT << COMBINER_CTRL_COLOR_FILTER_SHIFT;
self.set_uniforms_for_text_filter(&mut textures,
&mut uniforms,
fg_color,
@ -722,7 +723,7 @@ where
gamma_correction);
}
Filter::PatternFilter(PatternFilter::Blur { direction, sigma }) => {
ctrl |= COMBINER_CTRL_FILTER_BLUR << COMBINER_CTRL_COLOR_0_FILTER_SHIFT;
ctrl |= COMBINER_CTRL_FILTER_BLUR << COMBINER_CTRL_COLOR_FILTER_SHIFT;
self.set_uniforms_for_blur_filter(&mut uniforms, direction, sigma);
}
}
@ -1417,3 +1418,16 @@ impl ToCompositeCtrl for BlendMode {
}
}
}
trait ToCombineMode {
fn to_combine_mode(self) -> i32;
}
impl ToCombineMode for PaintCompositeOp {
fn to_combine_mode(self) -> i32 {
match self {
PaintCompositeOp::DestIn => COMBINER_CTRL_COLOR_COMBINE_DEST_IN,
PaintCompositeOp::SrcIn => COMBINER_CTRL_COLOR_COMBINE_SRC_IN,
}
}
}

View File

@ -11,6 +11,7 @@
//! Packed data ready to be sent to the GPU.
use crate::options::BoundingQuad;
use crate::paint::PaintCompositeOp;
use pathfinder_color::ColorU;
use pathfinder_content::effects::{BlendMode, Filter};
use pathfinder_content::fill::FillRule;
@ -77,7 +78,7 @@ pub enum RenderCommand {
Finish { cpu_build_time: Duration },
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub struct TexturePageId(pub u32);
#[derive(Clone, Debug)]
@ -85,7 +86,7 @@ pub struct TexturePageDescriptor {
pub size: Vector2I,
}
#[derive(Clone, Copy, PartialEq, Debug)]
#[derive(Clone, Copy, PartialEq, Debug, Default)]
pub struct TextureLocation {
pub page: TexturePageId,
pub rect: RectI,
@ -94,8 +95,7 @@ pub struct TextureLocation {
#[derive(Clone, Debug)]
pub struct TileBatch {
pub tiles: Vec<Tile>,
pub color_texture_0: Option<TileBatchTexture>,
pub color_texture_1: Option<TileBatchTexture>,
pub color_texture: Option<TileBatchTexture>,
pub mask_0_fill_rule: Option<FillRule>,
pub mask_1_fill_rule: Option<FillRule>,
pub filter: Filter,
@ -106,6 +106,7 @@ pub struct TileBatch {
pub struct TileBatchTexture {
pub page: TexturePageId,
pub sampling_flags: TextureSamplingFlags,
pub composite_op: PaintCompositeOp,
}
#[derive(Clone, Copy, Debug)]
@ -128,7 +129,7 @@ pub struct TileObjectPrimitive {
#[repr(C)]
pub struct TextureMetadataEntry {
pub color_0_transform: Transform2F,
pub opacity: f32,
pub base_color: ColorU,
}
// FIXME(pcwalton): Move `subpx` before `px` and remove `repr(packed)`.
@ -179,10 +180,9 @@ impl Debug for RenderCommand {
RenderCommand::BeginTileDrawing => write!(formatter, "BeginTileDrawing"),
RenderCommand::DrawTiles(ref batch) => {
write!(formatter,
"DrawTiles(x{}, C0 {:?}, C1 {:?}, M0 {:?}, {:?})",
"DrawTiles(x{}, C0 {:?}, M0 {:?}, {:?})",
batch.tiles.len(),
batch.color_texture_0,
batch.color_texture_1,
batch.color_texture,
batch.mask_0_fill_rule,
batch.blend_mode)
}

View File

@ -9,8 +9,8 @@
// except according to those terms.
use crate::allocator::{AllocationMode, TextureAllocator};
use crate::gpu_data::{RenderCommand, TextureLocation, TextureMetadataEntry};
use crate::gpu_data::{TexturePageDescriptor, TexturePageId};
use crate::gpu_data::{RenderCommand, TextureLocation, TextureMetadataEntry, TexturePageDescriptor};
use crate::gpu_data::{TexturePageId, TileBatchTexture};
use crate::scene::RenderTarget;
use hashbrown::HashMap;
use pathfinder_color::ColorU;
@ -21,10 +21,9 @@ use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
use pathfinder_geometry::util;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_gpu::TextureSamplingFlags;
use pathfinder_simd::default::{F32x2, F32x4};
use pathfinder_simd::default::F32x2;
use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
@ -33,9 +32,6 @@ use std::sync::Arc;
// TODO(pcwalton): Choose this size dynamically!
const GRADIENT_TILE_LENGTH: u32 = 256;
const SOLID_COLOR_TILE_LENGTH: u32 = 16;
const MAX_SOLID_COLORS_PER_TILE: u32 = SOLID_COLOR_TILE_LENGTH * SOLID_COLOR_TILE_LENGTH;
#[derive(Clone)]
pub struct Palette {
pub(crate) paints: Vec<Paint>,
@ -43,9 +39,20 @@ pub struct Palette {
cache: HashMap<Paint, PaintId>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Paint {
base_color: ColorU,
overlay: Option<PaintOverlay>,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct PaintOverlay {
composite_op: PaintCompositeOp,
contents: PaintContents,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Paint {
Color(ColorU),
pub enum PaintContents {
Gradient(Gradient),
Pattern(Pattern),
}
@ -56,15 +63,21 @@ pub struct PaintId(pub u16);
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct GradientId(pub u32);
impl Debug for Paint {
/// How a paint is to be composited over a base color, or vice versa.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum PaintCompositeOp {
SrcIn,
DestIn,
}
impl Debug for PaintContents {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
match *self {
Paint::Color(color) => color.fmt(formatter),
Paint::Gradient(_) => {
PaintContents::Gradient(_) => {
// TODO(pcwalton)
write!(formatter, "(gradient)")
}
Paint::Pattern(ref pattern) => pattern.fmt(formatter),
PaintContents::Pattern(ref pattern) => pattern.fmt(formatter),
}
}
}
@ -77,45 +90,78 @@ impl Palette {
}
impl Paint {
#[inline]
pub fn from_color(color: ColorU) -> Paint {
Paint { base_color: color, overlay: None }
}
#[inline]
pub fn from_gradient(gradient: Gradient) -> Paint {
Paint {
base_color: ColorU::white(),
overlay: Some(PaintOverlay {
composite_op: PaintCompositeOp::SrcIn,
contents: PaintContents::Gradient(gradient),
}),
}
}
#[inline]
pub fn from_pattern(pattern: Pattern) -> Paint {
Paint {
base_color: ColorU::white(),
overlay: Some(PaintOverlay {
composite_op: PaintCompositeOp::SrcIn,
contents: PaintContents::Pattern(pattern),
}),
}
}
#[inline]
pub fn black() -> Paint {
Paint::Color(ColorU::black())
Paint::from_color(ColorU::black())
}
#[inline]
pub fn transparent_black() -> Paint {
Paint::Color(ColorU::transparent_black())
Paint::from_color(ColorU::transparent_black())
}
pub fn is_opaque(&self) -> bool {
match *self {
Paint::Color(color) => color.is_opaque(),
Paint::Gradient(ref gradient) => {
gradient.stops().iter().all(|stop| stop.color.is_opaque())
if !self.base_color.is_opaque() {
return false;
}
match self.overlay {
None => true,
Some(ref overlay) => {
match overlay.contents {
PaintContents::Gradient(ref gradient) => gradient.is_opaque(),
PaintContents::Pattern(ref pattern) => pattern.is_opaque(),
}
}
Paint::Pattern(ref pattern) => pattern.is_opaque(),
}
}
pub fn is_fully_transparent(&self) -> bool {
match *self {
Paint::Color(color) => color.is_fully_transparent(),
Paint::Gradient(ref gradient) => {
gradient.stops().iter().all(|stop| stop.color.is_fully_transparent())
if !self.base_color.is_fully_transparent() {
return false;
}
match self.overlay {
None => true,
Some(ref overlay) => {
match overlay.contents {
PaintContents::Gradient(ref gradient) => gradient.is_fully_transparent(),
PaintContents::Pattern(_) => false,
}
Paint::Pattern(_) => {
// TODO(pcwalton): Should we support this?
false
}
}
}
#[inline]
pub fn is_color(&self) -> bool {
match *self {
Paint::Color(_) => true,
Paint::Gradient(_) | Paint::Pattern(_) => false,
}
self.overlay.is_none()
}
pub fn apply_transform(&mut self, transform: &Transform2F) {
@ -123,39 +169,93 @@ impl Paint {
return;
}
match *self {
Paint::Color(_) => {}
Paint::Gradient(ref mut gradient) => {
if let Some(ref mut overlay) = self.overlay {
match overlay.contents {
PaintContents::Gradient(ref mut gradient) => {
gradient.set_line(*transform * gradient.line());
if let Some(radii) = gradient.radii() {
gradient.set_radii(Some(radii * F32x2::splat(util::lerp(transform.matrix.m11(),
transform.matrix.m22(),
0.5))));
gradient.set_radii(Some(radii * transform.extract_scale().0));
}
}
Paint::Pattern(ref mut pattern) => pattern.apply_transform(*transform),
PaintContents::Pattern(ref mut pattern) => pattern.apply_transform(*transform),
}
}
}
fn opacity(&self) -> u8 {
match *self {
Paint::Color(_) | Paint::Gradient(_) => !0,
Paint::Pattern(ref pattern) => pattern.opacity(),
#[inline]
pub fn base_color(&self) -> ColorU {
self.base_color
}
#[inline]
pub fn set_base_color(&mut self, new_base_color: ColorU) {
self.base_color = new_base_color;
}
#[inline]
pub fn overlay(&self) -> &Option<PaintOverlay> {
&self.overlay
}
#[inline]
pub fn overlay_mut(&mut self) -> &mut Option<PaintOverlay> {
&mut self.overlay
}
#[inline]
pub fn pattern(&self) -> Option<&Pattern> {
match self.overlay {
None => None,
Some(ref overlay) => {
match overlay.contents {
PaintContents::Pattern(ref pattern) => Some(pattern),
_ => None,
}
}
}
}
pub fn apply_opacity(&mut self, new_opacity: f32) {
match *self {
Paint::Color(ref mut color) => color.a = (color.a as f32 * new_opacity) as u8,
Paint::Gradient(ref mut gradient) => {
for stop in gradient.stops_mut() {
stop.color.a = (stop.color.a as f32 * new_opacity) as u8
#[inline]
pub fn pattern_mut(&mut self) -> Option<&mut Pattern> {
match self.overlay {
None => None,
Some(ref mut overlay) => {
match overlay.contents {
PaintContents::Pattern(ref mut pattern) => Some(pattern),
_ => None,
}
}
Paint::Pattern(ref mut pattern) => {
pattern.set_opacity((pattern.opacity() as f32 * new_opacity) as u8)
}
}
#[inline]
pub fn gradient(&self) -> Option<&Gradient> {
match self.overlay {
None => None,
Some(ref overlay) => {
match overlay.contents {
PaintContents::Gradient(ref gradient) => Some(gradient),
_ => None,
}
}
}
}
}
impl PaintOverlay {
#[inline]
pub fn contents(&self) -> &PaintContents {
&self.contents
}
#[inline]
pub fn composite_op(&self) -> PaintCompositeOp {
self.composite_op
}
#[inline]
pub fn set_composite_op(&mut self, new_composite_op: PaintCompositeOp) {
self.composite_op = new_composite_op
}
}
@ -170,26 +270,30 @@ pub struct PaintInfo {
///
/// The indices of this vector are render target IDs.
pub render_target_metadata: Vec<RenderTargetMetadata>,
/// The page containing the opacity tile.
pub opacity_tile_page: TexturePageId,
/// The transform for the opacity tile.
pub opacity_tile_transform: Transform2F,
}
#[derive(Debug)]
pub struct PaintMetadata {
/// Metadata associated with the color texture, if applicable.
pub color_texture_metadata: Option<PaintColorTextureMetadata>,
/// The base color that the color texture gets mixed into.
pub base_color: ColorU,
/// True if this paint is fully opaque.
pub is_opaque: bool,
}
#[derive(Debug)]
pub struct PaintColorTextureMetadata {
/// The location of the paint.
pub location: TextureLocation,
/// The transform to apply to screen coordinates to translate them into UVs.
pub texture_transform: Transform2F,
pub transform: Transform2F,
/// The sampling mode for the texture.
pub sampling_flags: TextureSamplingFlags,
/// True if this paint is fully opaque.
pub is_opaque: bool,
/// The filter to be applied to this paint.
pub filter: PaintFilter,
/// The paint opacity (global alpha, in canvas terminology).
pub opacity: u8,
/// How the color texture is to be composited over the base color.
pub composite_op: PaintCompositeOp,
}
#[derive(Clone, Copy, Debug)]
@ -249,48 +353,46 @@ impl Palette {
}
// Assign paint locations.
let opacity_tile_builder = OpacityTileBuilder::new(&mut allocator);
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
let mut gradient_tile_builder = GradientTileBuilder::new();
let mut image_texel_info = vec![];
for paint in &self.paints {
let (texture_location, mut sampling_flags, filter);
match paint {
Paint::Color(color) => {
texture_location = solid_color_tile_builder.allocate(&mut allocator, *color);
sampling_flags = TextureSamplingFlags::empty();
filter = PaintFilter::None;
}
Paint::Gradient(ref gradient) => {
let color_texture_metadata = paint.overlay.as_ref().map(|overlay| {
match overlay.contents {
PaintContents::Gradient(ref gradient) => {
// FIXME(pcwalton): The gradient size might not be big enough. Detect this.
texture_location = gradient_tile_builder.allocate(&mut allocator, gradient);
sampling_flags = TextureSamplingFlags::empty();
filter = match gradient.radii() {
PaintColorTextureMetadata {
location: gradient_tile_builder.allocate(&mut allocator, gradient),
sampling_flags: TextureSamplingFlags::empty(),
filter: match gradient.radii() {
None => PaintFilter::None,
Some(radii) => {
PaintFilter::RadialGradient { line: gradient.line(), radii }
}
};
},
transform: Transform2F::default(),
composite_op: overlay.composite_op(),
}
Paint::Pattern(ref pattern) => {
}
PaintContents::Pattern(ref pattern) => {
let location;
match *pattern.source() {
PatternSource::RenderTarget { id: render_target_id, .. } => {
texture_location =
location =
render_target_metadata[render_target_id.0 as usize].location;
}
PatternSource::Image(ref image) => {
// TODO(pcwalton): We should be able to use tile cleverness to repeat
// inside the atlas in some cases.
// TODO(pcwalton): We should be able to use tile cleverness to
// repeat inside the atlas in some cases.
let allocation_mode = AllocationMode::OwnPage;
texture_location = allocator.allocate(image.size(), allocation_mode);
location = allocator.allocate(image.size(), allocation_mode);
image_texel_info.push(ImageTexelInfo {
location: texture_location,
location,
texels: (*image.pixels()).clone(),
});
}
}
sampling_flags = TextureSamplingFlags::empty();
let mut sampling_flags = TextureSamplingFlags::empty();
if pattern.repeat_x() {
sampling_flags.insert(TextureSamplingFlags::REPEAT_U);
}
@ -302,63 +404,79 @@ impl Palette {
TextureSamplingFlags::NEAREST_MAG);
}
filter = match pattern.filter() {
let filter = match pattern.filter() {
None => PaintFilter::None,
Some(pattern_filter) => PaintFilter::PatternFilter(pattern_filter),
};
PaintColorTextureMetadata {
location,
sampling_flags,
filter,
transform: Transform2F::default(),
composite_op: overlay.composite_op(),
}
};
}
}
});
paint_metadata.push(PaintMetadata {
location: texture_location,
texture_transform: Transform2F::default(),
sampling_flags,
color_texture_metadata,
is_opaque: paint.is_opaque(),
filter,
opacity: paint.opacity(),
base_color: paint.base_color(),
});
}
// Calculate texture transforms.
for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter_mut()) {
let texture_scale = allocator.page_scale(metadata.location.page);
metadata.texture_transform = match paint {
Paint::Color(_) => {
let matrix = Matrix2x2F(F32x4::default());
let vector = rect_to_inset_uv(metadata.location.rect, texture_scale).origin();
Transform2F { matrix, vector } * render_transform.inverse()
}
Paint::Gradient(Gradient { line: gradient_line, radii: None, .. }) => {
let v0 = metadata.location.rect.to_f32().center().y() * texture_scale.y();
let mut color_texture_metadata = match metadata.color_texture_metadata {
None => continue,
Some(ref mut color_texture_metadata) => color_texture_metadata,
};
let texture_scale = allocator.page_scale(color_texture_metadata.location.page);
let texture_rect = color_texture_metadata.location.rect;
color_texture_metadata.transform = match paint.overlay
.as_ref()
.expect("Why do we have color texture \
metadata but no overlay?")
.contents {
PaintContents::Gradient(Gradient {
line: gradient_line,
radii: None,
..
}) => {
let v0 = texture_rect.to_f32().center().y() * texture_scale.y();
let length_inv = 1.0 / gradient_line.square_length();
let (p0, d) = (gradient_line.from(), gradient_line.vector());
Transform2F {
matrix: Matrix2x2F::row_major(d.x(), d.y(), 0.0, 0.0).scale(length_inv),
matrix: Matrix2x2F::row_major(
d.x(), d.y(), 0.0, 0.0).scale(length_inv),
vector: Vector2F::new(-p0.dot(d) * length_inv, v0),
} * render_transform
}
Paint::Gradient(Gradient { radii: Some(_), .. }) => {
let texture_origin_uv =
rect_to_inset_uv(metadata.location.rect, texture_scale).origin();
let gradient_tile_scale = texture_scale * (GRADIENT_TILE_LENGTH - 1) as f32;
PaintContents::Gradient(Gradient { radii: Some(_), .. }) => {
let texture_origin_uv = rect_to_inset_uv(texture_rect, texture_scale).origin();
let gradient_tile_scale = texture_scale * (GRADIENT_TILE_LENGTH - 1) as
f32;
Transform2F {
matrix: Matrix2x2F::from_scale(gradient_tile_scale),
vector: texture_origin_uv,
} * render_transform
}
Paint::Pattern(pattern) => {
PaintContents::Pattern(ref pattern) => {
match pattern.source() {
PatternSource::Image(_) => {
let texture_origin_uv =
rect_to_uv(metadata.location.rect, texture_scale).origin();
rect_to_uv(texture_rect, texture_scale).origin();
Transform2F::from_translation(texture_origin_uv) *
Transform2F::from_scale(texture_scale) *
pattern.transform().inverse() * render_transform
}
PatternSource::RenderTarget { .. } => {
// FIXME(pcwalton): Only do this in GL, not Metal!
let texture_origin_uv = rect_to_uv(metadata.location.rect,
texture_scale).lower_left();
let texture_origin_uv =
rect_to_uv(texture_rect, texture_scale).lower_left();
Transform2F::from_translation(texture_origin_uv) *
Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0)) *
pattern.transform().inverse() * render_transform
@ -375,15 +493,14 @@ impl Palette {
texture_page_descriptors.push(TexturePageDescriptor { size: page_size });
}
// Gather opacity tile metadata.
let opacity_tile_page = opacity_tile_builder.tile_location.page;
let opacity_tile_transform = opacity_tile_builder.tile_transform(&allocator);
// Create texture metadata.
let texture_metadata = paint_metadata.iter().map(|paint_metadata| {
TextureMetadataEntry {
color_0_transform: paint_metadata.texture_transform,
opacity: paint_metadata.opacity as f32 / 255.0,
color_0_transform: match paint_metadata.color_texture_metadata {
None => Transform2F::default(),
Some(ref color_texture_metadata) => color_texture_metadata.transform,
},
base_color: paint_metadata.base_color,
}
}).collect();
@ -399,9 +516,7 @@ impl Palette {
location: metadata.location,
});
}
solid_color_tile_builder.create_render_commands(&mut render_commands);
gradient_tile_builder.create_render_commands(&mut render_commands);
opacity_tile_builder.create_render_commands(&mut render_commands);
for image_texel_info in image_texel_info {
render_commands.push(RenderCommand::UploadTexelData {
texels: image_texel_info.texels,
@ -409,27 +524,32 @@ impl Palette {
});
}
PaintInfo {
render_commands,
paint_metadata,
render_target_metadata,
opacity_tile_page,
opacity_tile_transform,
}
PaintInfo { render_commands, paint_metadata, render_target_metadata }
}
}
impl PaintMetadata {
pub(crate) fn filter(&self) -> Filter {
match self.filter {
match self.color_texture_metadata {
None => Filter::None,
Some(ref color_metadata) => {
match color_metadata.filter {
PaintFilter::None => Filter::None,
PaintFilter::RadialGradient { line, radii } => {
let uv_origin = self.texture_transform.vector;
let uv_origin = color_metadata.transform.vector;
Filter::RadialGradient { line, radii, uv_origin }
}
PaintFilter::PatternFilter(pattern_filter) => Filter::PatternFilter(pattern_filter),
PaintFilter::PatternFilter(pattern_filter) => {
Filter::PatternFilter(pattern_filter)
}
}
}
}
}
pub(crate) fn tile_batch_texture(&self) -> Option<TileBatchTexture> {
self.color_texture_metadata.as_ref().map(PaintColorTextureMetadata::as_tile_batch_texture)
}
}
fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
@ -440,97 +560,6 @@ fn rect_to_inset_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
rect_to_uv(rect, texture_scale).contract(texture_scale * 0.5)
}
// Opacity allocation
struct OpacityTileBuilder {
tile_location: TextureLocation,
}
impl OpacityTileBuilder {
fn new(allocator: &mut TextureAllocator) -> OpacityTileBuilder {
OpacityTileBuilder {
tile_location: allocator.allocate(Vector2I::splat(16), AllocationMode::Atlas),
}
}
fn create_texels(&self) -> Vec<ColorU> {
let mut texels = Vec::with_capacity(256);
for alpha in 0..=255 {
texels.push(ColorU::new(255, 255, 255, alpha));
}
texels
}
fn tile_transform(&self, allocator: &TextureAllocator) -> Transform2F {
let texture_scale = allocator.page_scale(self.tile_location.page);
let matrix = Matrix2x2F::from_scale(texture_scale * 16.0);
let vector = rect_to_uv(self.tile_location.rect, texture_scale).origin();
Transform2F { matrix, vector }
}
fn create_render_commands(self, render_commands: &mut Vec<RenderCommand>) {
render_commands.push(RenderCommand::UploadTexelData {
texels: Arc::new(self.create_texels()),
location: self.tile_location,
});
}
}
// Solid color allocation
struct SolidColorTileBuilder {
tiles: Vec<SolidColorTile>,
}
struct SolidColorTile {
texels: Vec<ColorU>,
location: TextureLocation,
next_index: u32,
}
impl SolidColorTileBuilder {
fn new() -> SolidColorTileBuilder {
SolidColorTileBuilder { tiles: vec![] }
}
fn allocate(&mut self, allocator: &mut TextureAllocator, color: ColorU) -> TextureLocation {
if self.tiles.is_empty() ||
self.tiles.last().unwrap().next_index == MAX_SOLID_COLORS_PER_TILE {
let area = SOLID_COLOR_TILE_LENGTH as usize * SOLID_COLOR_TILE_LENGTH as usize;
self.tiles.push(SolidColorTile {
texels: vec![ColorU::black(); area],
location: allocator.allocate(Vector2I::splat(SOLID_COLOR_TILE_LENGTH as i32),
AllocationMode::Atlas),
next_index: 0,
});
}
let mut data = self.tiles.last_mut().unwrap();
let subtile_origin = vec2i((data.next_index % SOLID_COLOR_TILE_LENGTH) as i32,
(data.next_index / SOLID_COLOR_TILE_LENGTH) as i32);
data.next_index += 1;
let location = TextureLocation {
page: data.location.page,
rect: RectI::new(data.location.rect.origin() + subtile_origin, vec2i(1, 1)),
};
data.texels[subtile_origin.y() as usize * SOLID_COLOR_TILE_LENGTH as usize +
subtile_origin.x() as usize] = color;
location
}
fn create_render_commands(self, render_commands: &mut Vec<RenderCommand>) {
for tile in self.tiles {
render_commands.push(RenderCommand::UploadTexelData {
texels: Arc::new(tile.texels),
location: tile.location,
});
}
}
}
// Gradient allocation
struct GradientTileBuilder {
@ -599,3 +628,13 @@ struct ImageTexelInfo {
location: TextureLocation,
texels: Arc<Vec<ColorU>>,
}
impl PaintColorTextureMetadata {
pub(crate) fn as_tile_batch_texture(&self) -> TileBatchTexture {
TileBatchTexture {
page: self.location.page,
sampling_flags: self.sampling_flags,
composite_op: self.composite_op,
}
}
}

View File

@ -14,7 +14,7 @@ use crate::builder::SceneBuilder;
use crate::concurrent::executor::Executor;
use crate::options::{BuildOptions, PreparedBuildOptions};
use crate::options::{PreparedRenderTransform, RenderCommandListener};
use crate::paint::{Paint, PaintId, PaintInfo, Palette};
use crate::paint::{Paint, PaintContents, PaintId, PaintInfo, Palette};
use pathfinder_content::effects::BlendMode;
use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::Outline;
@ -103,8 +103,11 @@ impl Scene {
let mut paint_mapping = HashMap::new();
for (old_paint_index, old_paint) in scene.palette.paints.iter().enumerate() {
let old_paint_id = PaintId(old_paint_index as u16);
let new_paint_id = match old_paint {
Paint::Pattern(pattern) => {
let new_paint_id = match *old_paint.overlay() {
None => self.palette.push_paint(old_paint),
Some(ref overlay) => {
match *overlay.contents() {
PaintContents::Pattern(ref pattern) => {
match pattern.source() {
PatternSource::RenderTarget { id: old_render_target_id, size } => {
let mut new_pattern =
@ -114,12 +117,14 @@ impl Scene {
new_pattern.set_repeat_x(pattern.repeat_x());
new_pattern.set_repeat_y(pattern.repeat_y());
new_pattern.set_smoothing_enabled(pattern.smoothing_enabled());
self.palette.push_paint(&Paint::Pattern(new_pattern))
self.palette.push_paint(&Paint::from_pattern(new_pattern))
}
_ => self.palette.push_paint(old_paint),
}
}
_ => self.palette.push_paint(old_paint),
}
}
paint => self.palette.push_paint(paint),
};
paint_mapping.insert(old_paint_id, new_paint_id);
}

View File

@ -11,7 +11,7 @@
//! Software occlusion culling.
use crate::builder::Occluder;
use crate::gpu_data::{Tile, TileBatch, TileBatchTexture};
use crate::gpu_data::{Tile, TileBatch};
use crate::paint::{PaintId, PaintMetadata};
use crate::tile_map::DenseTileMap;
use crate::tiles;
@ -77,23 +77,17 @@ impl ZBuffer {
let tile_position = tile_coords + self.buffer.rect.origin();
// Create a batch if necessary.
let paint_tile_batch_texture = paint_metadata.tile_batch_texture();
match solid_tiles.batches.last() {
Some(TileBatch {
color_texture_0: Some(TileBatchTexture { page, sampling_flags }),
..
}) if *page == paint_metadata.location.page &&
*sampling_flags == paint_metadata.sampling_flags => {}
Some(TileBatch { color_texture: tile_batch_texture, .. }) if
*tile_batch_texture == paint_tile_batch_texture => {}
_ => {
// Batch break.
//
// TODO(pcwalton): We could be more aggressive with batching here, since we
// know there are no overlaps.
solid_tiles.batches.push(TileBatch {
color_texture_0: Some(TileBatchTexture {
page: paint_metadata.location.page,
sampling_flags: paint_metadata.sampling_flags,
}),
color_texture_1: None,
color_texture: paint_tile_batch_texture,
tiles: vec![],
filter: Filter::None,
blend_mode: BlendMode::default(),

View File

@ -75,6 +75,8 @@ precision highp sampler2D;
@ -93,7 +95,7 @@ uniform int uCtrl;
in vec3 vMaskTexCoord0;
in vec3 vMaskTexCoord1;
in vec2 vColorTexCoord0;
in float vOpacity;
in vec4 vBaseColor;
out vec4 oFragColor;
@ -105,6 +107,18 @@ vec4 sampleColor(sampler2D colorTexture, vec2 colorTexCoord){
vec4 combineColor0(vec4 destColor, vec4 srcColor, int op){
switch(op){
case 0x1 :
return vec4(srcColor . rgb, srcColor . a * destColor . a);
case 0x2 :
return vec4(destColor . rgb, srcColor . a * destColor . a);
}
return destColor;
}
float filterTextSample1Tap(float offset, sampler2D colorTexture, vec2 colorTexCoord){
return texture(colorTexture, colorTexCoord + vec2(offset, 0.0)). r;
}
@ -558,11 +572,12 @@ void calculateColor(int ctrl){
maskAlpha = sampleMask(maskAlpha, uMaskTexture1, vMaskTexCoord1, maskCtrl1);
vec4 color = vec4(0.0);
if(((ctrl >> 6)& 0x1)!= 0){
int color0Filter =(ctrl >> 4)&
vec4 color = vBaseColor;
int color0Combine =(ctrl >> 6)&
0x3;
color += filterColor(vColorTexCoord0,
if(color0Combine != 0){
int color0Filter =(ctrl >> 4)& 0x3;
vec4 color0 = filterColor(vColorTexCoord0,
uColorTexture0,
uGammaLUT,
uColorTexture0Size,
@ -572,10 +587,11 @@ void calculateColor(int ctrl){
uFilterParams1,
uFilterParams2,
color0Filter);
color = combineColor0(color, color0, color0Combine);
}
color . a *= maskAlpha * vOpacity;
color . a *= maskAlpha;
int compositeOp =(ctrl >> 8)& 0xf;

View File

@ -30,7 +30,7 @@ in int aColor;
out vec3 vMaskTexCoord0;
out vec3 vMaskTexCoord1;
out vec2 vColorTexCoord0;
out float vOpacity;
out vec4 vBaseColor;
void main(){
vec2 tileOrigin = vec2(aTileOrigin), tileOffset = vec2(aTileOffset);
@ -40,16 +40,18 @@ void main(){
vec2 maskTexCoord1 =(vec2(aMaskTexCoord1)+ tileOffset)/ 256.0;
vec2 textureMetadataScale = vec2(1.0)/ vec2(uTextureMetadataSize);
vec2 metadataEntryCoord = ivec2(aColor % 256 * 2, aColor / 256);
vec2 metadataEntryCoord = ivec2(aColor % 128 * 4, aColor / 128);
vec2 colorTexMatrix0Coord =(metadataEntryCoord + vec2(0.5, 0.5))* textureMetadataScale;
vec2 colorTexOffsetsCoord =(metadataEntryCoord + vec2(1.5, 0.5))* textureMetadataScale;
vec2 baseColorCoord =(metadataEntryCoord + vec2(2.5, 0.5))* textureMetadataScale;
vec4 colorTexMatrix0 = texture(uTextureMetadata, colorTexMatrix0Coord);
vec4 colorTexOffsets = texture(uTextureMetadata, colorTexOffsetsCoord);
vec4 baseColor = texture(uTextureMetadata, baseColorCoord);
vColorTexCoord0 = mat2(colorTexMatrix0)* position + colorTexOffsets . xy;
vOpacity = colorTexOffsets . z;
vMaskTexCoord0 = vec3(maskTexCoord0, float(aMaskBackdrop . x));
vMaskTexCoord1 = vec3(maskTexCoord1, float(aMaskBackdrop . y));
vBaseColor = baseColor;
gl_Position = uTransform * vec4(position, 0.0, 1.0);
}

View File

@ -26,7 +26,7 @@ struct spvDescriptorSetBuffer0
constant int* uCtrl [[id(15)]];
};
constant float3 _1003 = {};
constant float3 _1041 = {};
struct main0_out
{
@ -38,7 +38,7 @@ struct main0_in
float3 vMaskTexCoord0 [[user(locn0)]];
float3 vMaskTexCoord1 [[user(locn1)]];
float2 vColorTexCoord0 [[user(locn2)]];
float vOpacity [[user(locn3)]];
float4 vBaseColor [[user(locn3)]];
};
// Implementation of the GLSL mod() function, which is slightly different than Metal fmod()
@ -84,16 +84,16 @@ float4 filterRadialGradient(thread const float2& colorTexCoord, thread const tex
{
float2 ts = float2((float2(1.0, -1.0) * sqrt(discrim)) + float2(b)) / float2(a);
float tMax = fast::max(ts.x, ts.y);
float _511;
float _549;
if (tMax <= 1.0)
{
_511 = tMax;
_549 = tMax;
}
else
{
_511 = fast::min(ts.x, ts.y);
_549 = fast::min(ts.x, ts.y);
}
float t = _511;
float t = _549;
if (t >= 0.0)
{
color = colorTexture.sample(colorTextureSmplr, (uvOrigin + float2(t, 0.0)));
@ -109,19 +109,19 @@ float4 filterBlur(thread const float2& colorTexCoord, thread const texture2d<flo
float3 gaussCoeff = filterParams1.xyz;
float gaussSum = gaussCoeff.x;
float4 color = colorTexture.sample(colorTextureSmplr, colorTexCoord) * gaussCoeff.x;
float2 _561 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_561.x, _561.y, gaussCoeff.z);
float2 _599 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_599.x, _599.y, gaussCoeff.z);
for (int i = 1; i <= support; i += 2)
{
float gaussPartialSum = gaussCoeff.x;
float2 _581 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_581.x, _581.y, gaussCoeff.z);
float2 _619 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_619.x, _619.y, gaussCoeff.z);
gaussPartialSum += gaussCoeff.x;
float2 srcOffset = srcOffsetScale * (float(i) + (gaussCoeff.x / gaussPartialSum));
color += ((colorTexture.sample(colorTextureSmplr, (colorTexCoord - srcOffset)) + colorTexture.sample(colorTextureSmplr, (colorTexCoord + srcOffset))) * gaussPartialSum);
gaussSum += (2.0 * gaussPartialSum);
float2 _621 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_621.x, _621.y, gaussCoeff.z);
float2 _659 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_659.x, _659.y, gaussCoeff.z);
}
return color / float4(gaussSum);
}
@ -134,16 +134,16 @@ float filterTextSample1Tap(thread const float& offset, thread const texture2d<fl
void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCenter, thread float4& outAlphaRight, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTexCoord, thread const float4& kernel0, thread const float& onePixel)
{
bool wide = kernel0.x > 0.0;
float _195;
float _234;
if (wide)
{
float param = (-4.0) * onePixel;
float2 param_1 = colorTexCoord;
_195 = filterTextSample1Tap(param, colorTexture, colorTextureSmplr, param_1);
_234 = filterTextSample1Tap(param, colorTexture, colorTextureSmplr, param_1);
}
else
{
_195 = 0.0;
_234 = 0.0;
}
float param_2 = (-3.0) * onePixel;
float2 param_3 = colorTexCoord;
@ -151,7 +151,7 @@ void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCen
float2 param_5 = colorTexCoord;
float param_6 = (-1.0) * onePixel;
float2 param_7 = colorTexCoord;
outAlphaLeft = float4(_195, filterTextSample1Tap(param_2, colorTexture, colorTextureSmplr, param_3), filterTextSample1Tap(param_4, colorTexture, colorTextureSmplr, param_5), filterTextSample1Tap(param_6, colorTexture, colorTextureSmplr, param_7));
outAlphaLeft = float4(_234, filterTextSample1Tap(param_2, colorTexture, colorTextureSmplr, param_3), filterTextSample1Tap(param_4, colorTexture, colorTextureSmplr, param_5), filterTextSample1Tap(param_6, colorTexture, colorTextureSmplr, param_7));
float param_8 = 0.0;
float2 param_9 = colorTexCoord;
outAlphaCenter = filterTextSample1Tap(param_8, colorTexture, colorTextureSmplr, param_9);
@ -161,18 +161,18 @@ void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCen
float2 param_13 = colorTexCoord;
float param_14 = 3.0 * onePixel;
float2 param_15 = colorTexCoord;
float _255;
float _294;
if (wide)
{
float param_16 = 4.0 * onePixel;
float2 param_17 = colorTexCoord;
_255 = filterTextSample1Tap(param_16, colorTexture, colorTextureSmplr, param_17);
_294 = filterTextSample1Tap(param_16, colorTexture, colorTextureSmplr, param_17);
}
else
{
_255 = 0.0;
_294 = 0.0;
}
outAlphaRight = float4(filterTextSample1Tap(param_10, colorTexture, colorTextureSmplr, param_11), filterTextSample1Tap(param_12, colorTexture, colorTextureSmplr, param_13), filterTextSample1Tap(param_14, colorTexture, colorTextureSmplr, param_15), _255);
outAlphaRight = float4(filterTextSample1Tap(param_10, colorTexture, colorTextureSmplr, param_11), filterTextSample1Tap(param_12, colorTexture, colorTextureSmplr, param_13), filterTextSample1Tap(param_14, colorTexture, colorTextureSmplr, param_15), _294);
}
float filterTextConvolve7Tap(thread const float4& alpha0, thread const float3& alpha1, thread const float4& kernel0)
@ -289,6 +289,22 @@ float4 filterColor(thread const float2& colorTexCoord, thread const texture2d<fl
return filterNone(param_15, colorTexture, colorTextureSmplr);
}
float4 combineColor0(thread const float4& destColor, thread const float4& srcColor, thread const int& op)
{
switch (op)
{
case 1:
{
return float4(srcColor.xyz, srcColor.w * destColor.w);
}
case 2:
{
return float4(destColor.xyz, srcColor.w * destColor.w);
}
}
return destColor;
}
float3 compositeScreen(thread const float3& destColor, thread const float3& srcColor)
{
return (destColor + srcColor) - (destColor * srcColor);
@ -296,34 +312,34 @@ float3 compositeScreen(thread const float3& destColor, thread const float3& srcC
float3 compositeSelect(thread const bool3& cond, thread const float3& ifTrue, thread const float3& ifFalse)
{
float _687;
float _725;
if (cond.x)
{
_687 = ifTrue.x;
_725 = ifTrue.x;
}
else
{
_687 = ifFalse.x;
_725 = ifFalse.x;
}
float _698;
float _736;
if (cond.y)
{
_698 = ifTrue.y;
_736 = ifTrue.y;
}
else
{
_698 = ifFalse.y;
_736 = ifFalse.y;
}
float _709;
float _747;
if (cond.z)
{
_709 = ifTrue.z;
_747 = ifTrue.z;
}
else
{
_709 = ifFalse.z;
_747 = ifFalse.z;
}
return float3(_687, _698, _709);
return float3(_725, _736, _747);
}
float3 compositeHardLight(thread const float3& destColor, thread const float3& srcColor)
@ -364,16 +380,16 @@ float3 compositeSoftLight(thread const float3& destColor, thread const float3& s
float compositeDivide(thread const float& num, thread const float& denom)
{
float _723;
float _761;
if (denom != 0.0)
{
_723 = num / denom;
_761 = num / denom;
}
else
{
_723 = 0.0;
_761 = 0.0;
}
return _723;
return _761;
}
float3 compositeRGBToHSL(thread const float3& rgb)
@ -382,25 +398,25 @@ float3 compositeRGBToHSL(thread const float3& rgb)
float xMin = fast::min(fast::min(rgb.x, rgb.y), rgb.z);
float c = v - xMin;
float l = mix(xMin, v, 0.5);
float3 _829;
float3 _867;
if (rgb.x == v)
{
_829 = float3(0.0, rgb.yz);
_867 = float3(0.0, rgb.yz);
}
else
{
float3 _842;
float3 _880;
if (rgb.y == v)
{
_842 = float3(2.0, rgb.zx);
_880 = float3(2.0, rgb.zx);
}
else
{
_842 = float3(4.0, rgb.xy);
_880 = float3(4.0, rgb.xy);
}
_829 = _842;
_867 = _880;
}
float3 terms = _829;
float3 terms = _867;
float param = ((terms.x * c) + terms.y) - terms.z;
float param_1 = c;
float h = 1.0471975803375244140625 * compositeDivide(param, param_1);
@ -532,7 +548,7 @@ float4 composite(thread const float4& srcColor, thread const texture2d<float> de
return float4(((srcColor.xyz * (srcColor.w * (1.0 - destColor.w))) + (blendedRGB * (srcColor.w * destColor.w))) + (destColor.xyz * (1.0 - srcColor.w)), 1.0);
}
void calculateColor(thread const int& ctrl, thread texture2d<float> uMaskTexture0, thread const sampler uMaskTexture0Smplr, thread float3& vMaskTexCoord0, thread texture2d<float> uMaskTexture1, thread const sampler uMaskTexture1Smplr, thread float3& vMaskTexCoord1, thread float2& vColorTexCoord0, thread texture2d<float> uColorTexture0, thread const sampler uColorTexture0Smplr, thread texture2d<float> uGammaLUT, thread const sampler uGammaLUTSmplr, thread float2 uColorTexture0Size, thread float4& gl_FragCoord, thread float2 uFramebufferSize, thread float4 uFilterParams0, thread float4 uFilterParams1, thread float4 uFilterParams2, thread float& vOpacity, thread texture2d<float> uDestTexture, thread const sampler uDestTextureSmplr, thread float4& oFragColor)
void calculateColor(thread const int& ctrl, thread texture2d<float> uMaskTexture0, thread const sampler uMaskTexture0Smplr, thread float3& vMaskTexCoord0, thread texture2d<float> uMaskTexture1, thread const sampler uMaskTexture1Smplr, thread float3& vMaskTexCoord1, thread float4& vBaseColor, thread float2& vColorTexCoord0, thread texture2d<float> uColorTexture0, thread const sampler uColorTexture0Smplr, thread texture2d<float> uGammaLUT, thread const sampler uGammaLUTSmplr, thread float2 uColorTexture0Size, thread float4& gl_FragCoord, thread float2 uFramebufferSize, thread float4 uFilterParams0, thread float4 uFilterParams1, thread float4 uFilterParams2, thread texture2d<float> uDestTexture, thread const sampler uDestTextureSmplr, thread float4& oFragColor)
{
int maskCtrl0 = (ctrl >> 0) & 3;
int maskCtrl1 = (ctrl >> 2) & 3;
@ -545,8 +561,9 @@ void calculateColor(thread const int& ctrl, thread texture2d<float> uMaskTexture
float3 param_4 = vMaskTexCoord1;
int param_5 = maskCtrl1;
maskAlpha = sampleMask(param_3, uMaskTexture1, uMaskTexture1Smplr, param_4, param_5);
float4 color = float4(0.0);
if (((ctrl >> 6) & 1) != 0)
float4 color = vBaseColor;
int color0Combine = (ctrl >> 6) & 3;
if (color0Combine != 0)
{
int color0Filter = (ctrl >> 4) & 3;
float2 param_6 = vColorTexCoord0;
@ -557,17 +574,21 @@ void calculateColor(thread const int& ctrl, thread texture2d<float> uMaskTexture
float4 param_11 = uFilterParams1;
float4 param_12 = uFilterParams2;
int param_13 = color0Filter;
color += filterColor(param_6, uColorTexture0, uColorTexture0Smplr, uGammaLUT, uGammaLUTSmplr, param_7, param_8, param_9, param_10, param_11, param_12, param_13);
}
color.w *= (maskAlpha * vOpacity);
int compositeOp = (ctrl >> 8) & 15;
float4 color0 = filterColor(param_6, uColorTexture0, uColorTexture0Smplr, uGammaLUT, uGammaLUTSmplr, param_7, param_8, param_9, param_10, param_11, param_12, param_13);
float4 param_14 = color;
float2 param_15 = uFramebufferSize;
float2 param_16 = gl_FragCoord.xy;
int param_17 = compositeOp;
color = composite(param_14, uDestTexture, uDestTextureSmplr, param_15, param_16, param_17);
float3 _1294 = color.xyz * color.w;
color = float4(_1294.x, _1294.y, _1294.z, color.w);
float4 param_15 = color0;
int param_16 = color0Combine;
color = combineColor0(param_14, param_15, param_16);
}
color.w *= maskAlpha;
int compositeOp = (ctrl >> 8) & 15;
float4 param_17 = color;
float2 param_18 = uFramebufferSize;
float2 param_19 = gl_FragCoord.xy;
int param_20 = compositeOp;
color = composite(param_17, uDestTexture, uDestTextureSmplr, param_18, param_19, param_20);
float3 _1338 = color.xyz * color.w;
color = float4(_1338.x, _1338.y, _1338.z, color.w);
oFragColor = color;
}
@ -575,7 +596,7 @@ fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuff
{
main0_out out = {};
int param = (*spvDescriptorSet0.uCtrl);
calculateColor(param, spvDescriptorSet0.uMaskTexture0, spvDescriptorSet0.uMaskTexture0Smplr, in.vMaskTexCoord0, spvDescriptorSet0.uMaskTexture1, spvDescriptorSet0.uMaskTexture1Smplr, in.vMaskTexCoord1, in.vColorTexCoord0, spvDescriptorSet0.uColorTexture0, spvDescriptorSet0.uColorTexture0Smplr, spvDescriptorSet0.uGammaLUT, spvDescriptorSet0.uGammaLUTSmplr, (*spvDescriptorSet0.uColorTexture0Size), gl_FragCoord, (*spvDescriptorSet0.uFramebufferSize), (*spvDescriptorSet0.uFilterParams0), (*spvDescriptorSet0.uFilterParams1), (*spvDescriptorSet0.uFilterParams2), in.vOpacity, spvDescriptorSet0.uDestTexture, spvDescriptorSet0.uDestTextureSmplr, out.oFragColor);
calculateColor(param, spvDescriptorSet0.uMaskTexture0, spvDescriptorSet0.uMaskTexture0Smplr, in.vMaskTexCoord0, spvDescriptorSet0.uMaskTexture1, spvDescriptorSet0.uMaskTexture1Smplr, in.vMaskTexCoord1, in.vBaseColor, in.vColorTexCoord0, spvDescriptorSet0.uColorTexture0, spvDescriptorSet0.uColorTexture0Smplr, spvDescriptorSet0.uGammaLUT, spvDescriptorSet0.uGammaLUTSmplr, (*spvDescriptorSet0.uColorTexture0Size), gl_FragCoord, (*spvDescriptorSet0.uFramebufferSize), (*spvDescriptorSet0.uFilterParams0), (*spvDescriptorSet0.uFilterParams1), (*spvDescriptorSet0.uFilterParams2), spvDescriptorSet0.uDestTexture, spvDescriptorSet0.uDestTextureSmplr, out.oFragColor);
return out;
}

View File

@ -18,7 +18,7 @@ struct main0_out
float3 vMaskTexCoord0 [[user(locn0)]];
float3 vMaskTexCoord1 [[user(locn1)]];
float2 vColorTexCoord0 [[user(locn2)]];
float vOpacity [[user(locn3)]];
float4 vBaseColor [[user(locn3)]];
float4 gl_Position [[position]];
};
@ -41,15 +41,17 @@ vertex main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer
float2 maskTexCoord0 = (float2(in.aMaskTexCoord0) + tileOffset) / float2(256.0);
float2 maskTexCoord1 = (float2(in.aMaskTexCoord1) + tileOffset) / float2(256.0);
float2 textureMetadataScale = float2(1.0) / float2((*spvDescriptorSet0.uTextureMetadataSize));
float2 metadataEntryCoord = float2(int2((in.aColor % 256) * 2, in.aColor / 256));
float2 metadataEntryCoord = float2(int2((in.aColor % 128) * 4, in.aColor / 128));
float2 colorTexMatrix0Coord = (metadataEntryCoord + float2(0.5)) * textureMetadataScale;
float2 colorTexOffsetsCoord = (metadataEntryCoord + float2(1.5, 0.5)) * textureMetadataScale;
float2 baseColorCoord = (metadataEntryCoord + float2(2.5, 0.5)) * textureMetadataScale;
float4 colorTexMatrix0 = spvDescriptorSet0.uTextureMetadata.sample(spvDescriptorSet0.uTextureMetadataSmplr, colorTexMatrix0Coord, level(0.0));
float4 colorTexOffsets = spvDescriptorSet0.uTextureMetadata.sample(spvDescriptorSet0.uTextureMetadataSmplr, colorTexOffsetsCoord, level(0.0));
float4 baseColor = spvDescriptorSet0.uTextureMetadata.sample(spvDescriptorSet0.uTextureMetadataSmplr, baseColorCoord, level(0.0));
out.vColorTexCoord0 = (float2x2(float2(colorTexMatrix0.xy), float2(colorTexMatrix0.zw)) * position) + colorTexOffsets.xy;
out.vOpacity = colorTexOffsets.z;
out.vMaskTexCoord0 = float3(maskTexCoord0, float(in.aMaskBackdrop.x));
out.vMaskTexCoord1 = float3(maskTexCoord1, float(in.aMaskBackdrop.y));
out.vBaseColor = baseColor;
out.gl_Position = (*spvDescriptorSet0.uTransform) * float4(position, 0.0, 1.0);
return out;
}

View File

@ -45,7 +45,9 @@ precision highp sampler2D;
#define COMBINER_CTRL_MASK_WINDING 0x1
#define COMBINER_CTRL_MASK_EVEN_ODD 0x2
#define COMBINER_CTRL_COLOR_ENABLE_MASK 0x1
#define COMBINER_CTRL_COLOR_COMBINE_MASK 0x3
#define COMBINER_CTRL_COLOR_COMBINE_SRC_IN 0x1
#define COMBINER_CTRL_COLOR_COMBINE_DEST_IN 0x2
#define COMBINER_CTRL_FILTER_MASK 0x3
#define COMBINER_CTRL_FILTER_RADIAL_GRADIENT 0x1
@ -72,8 +74,8 @@ precision highp sampler2D;
#define COMBINER_CTRL_MASK_0_SHIFT 0
#define COMBINER_CTRL_MASK_1_SHIFT 2
#define COMBINER_CTRL_COLOR_0_FILTER_SHIFT 4
#define COMBINER_CTRL_COLOR_0_ENABLE_SHIFT 6
#define COMBINER_CTRL_COLOR_FILTER_SHIFT 4
#define COMBINER_CTRL_COLOR_COMBINE_SHIFT 6
#define COMBINER_CTRL_COMPOSITE_SHIFT 8
uniform sampler2D uColorTexture0;
@ -91,7 +93,7 @@ uniform int uCtrl;
in vec3 vMaskTexCoord0;
in vec3 vMaskTexCoord1;
in vec2 vColorTexCoord0;
in float vOpacity;
in vec4 vBaseColor;
out vec4 oFragColor;
@ -101,6 +103,18 @@ vec4 sampleColor(sampler2D colorTexture, vec2 colorTexCoord) {
return texture(colorTexture, colorTexCoord);
}
// Color combining
vec4 combineColor0(vec4 destColor, vec4 srcColor, int op) {
switch (op) {
case COMBINER_CTRL_COLOR_COMBINE_SRC_IN:
return vec4(srcColor.rgb, srcColor.a * destColor.a);
case COMBINER_CTRL_COLOR_COMBINE_DEST_IN:
return vec4(destColor.rgb, srcColor.a * destColor.a);
}
return destColor;
}
// Text filter
float filterTextSample1Tap(float offset, sampler2D colorTexture, vec2 colorTexCoord) {
@ -556,11 +570,12 @@ void calculateColor(int ctrl) {
maskAlpha = sampleMask(maskAlpha, uMaskTexture1, vMaskTexCoord1, maskCtrl1);
// Sample color.
vec4 color = vec4(0.0);
if (((ctrl >> COMBINER_CTRL_COLOR_0_ENABLE_SHIFT) & COMBINER_CTRL_COLOR_ENABLE_MASK) != 0) {
int color0Filter = (ctrl >> COMBINER_CTRL_COLOR_0_FILTER_SHIFT) &
COMBINER_CTRL_FILTER_MASK;
color += filterColor(vColorTexCoord0,
vec4 color = vBaseColor;
int color0Combine = (ctrl >> COMBINER_CTRL_COLOR_COMBINE_SHIFT) &
COMBINER_CTRL_COLOR_COMBINE_MASK;
if (color0Combine != 0) {
int color0Filter = (ctrl >> COMBINER_CTRL_COLOR_FILTER_SHIFT) & COMBINER_CTRL_FILTER_MASK;
vec4 color0 = filterColor(vColorTexCoord0,
uColorTexture0,
uGammaLUT,
uColorTexture0Size,
@ -570,10 +585,11 @@ void calculateColor(int ctrl) {
uFilterParams1,
uFilterParams2,
color0Filter);
color = combineColor0(color, color0, color0Combine);
}
// Apply mask and opacity.
color.a *= maskAlpha * vOpacity;
// Apply mask.
color.a *= maskAlpha;
// Apply composite.
int compositeOp = (ctrl >> COMBINER_CTRL_COMPOSITE_SHIFT) & COMBINER_CTRL_COMPOSITE_MASK;

View File

@ -28,7 +28,7 @@ in int aColor;
out vec3 vMaskTexCoord0;
out vec3 vMaskTexCoord1;
out vec2 vColorTexCoord0;
out float vOpacity;
out vec4 vBaseColor;
void main() {
vec2 tileOrigin = vec2(aTileOrigin), tileOffset = vec2(aTileOffset);
@ -38,15 +38,17 @@ void main() {
vec2 maskTexCoord1 = (vec2(aMaskTexCoord1) + tileOffset) / 256.0;
vec2 textureMetadataScale = vec2(1.0) / vec2(uTextureMetadataSize);
vec2 metadataEntryCoord = ivec2(aColor % 256 * 2, aColor / 256);
vec2 metadataEntryCoord = ivec2(aColor % 128 * 4, aColor / 128);
vec2 colorTexMatrix0Coord = (metadataEntryCoord + vec2(0.5, 0.5)) * textureMetadataScale;
vec2 colorTexOffsetsCoord = (metadataEntryCoord + vec2(1.5, 0.5)) * textureMetadataScale;
vec2 baseColorCoord = (metadataEntryCoord + vec2(2.5, 0.5)) * textureMetadataScale;
vec4 colorTexMatrix0 = texture(uTextureMetadata, colorTexMatrix0Coord);
vec4 colorTexOffsets = texture(uTextureMetadata, colorTexOffsetsCoord);
vec4 baseColor = texture(uTextureMetadata, baseColorCoord);
vColorTexCoord0 = mat2(colorTexMatrix0) * position + colorTexOffsets.xy;
vOpacity = colorTexOffsets.z;
vMaskTexCoord0 = vec3(maskTexCoord0, float(aMaskBackdrop.x));
vMaskTexCoord1 = vec3(maskTexCoord1, float(aMaskBackdrop.y));
vBaseColor = baseColor;
gl_Position = uTransform * vec4(position, 0.0, 1.0);
}

View File

@ -302,7 +302,7 @@ impl PaintExt for Paint {
result_flags: &mut BuildResultFlags)
-> Paint {
// TODO(pcwalton): Support gradients.
let mut paint = Paint::Color(match *svg_paint {
let mut paint = Paint::from_color(match *svg_paint {
UsvgPaint::Color(color) => ColorU::from_svg_color(color),
UsvgPaint::Link(_) => {
// TODO(pcwalton)
@ -311,7 +311,11 @@ impl PaintExt for Paint {
}
});
paint.apply_transform(transform);
paint.apply_opacity(opacity.value() as f32);
let mut base_color = paint.base_color().to_f32();
base_color.set_a(base_color.a() * opacity.value() as f32);
paint.set_base_color(base_color.to_u8());
paint
}
}

View File

@ -253,7 +253,9 @@ fn get_new_styles<'a>(
a
}
}
) => Some(PaintOrLine::Paint(Paint::Color(ColorU { r: *r, g: *g, b: *b, a: *a }))),
) => {
Some(PaintOrLine::Paint(Paint::from_color(ColorU { r: *r, g: *g, b: *b, a: *a })))
}
_ => unimplemented!("Unimplemented fill style")
}
}).chain(
@ -286,7 +288,7 @@ fn get_new_styles<'a>(
// assert_eq!(start_cap, end_cap);
Some(PaintOrLine::Line(SwfLineStyle {
width: Twips(*width as i32),
color: Paint::Color(ColorU { r: *r, g: *g, b: *b, a: *a }),
color: Paint::from_color(ColorU { r: *r, g: *g, b: *b, a: *a }),
join: match join {
JoinStyle::Bevel => LineJoin::Bevel,
JoinStyle::Round => LineJoin::Round,