diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index dafa8f6f..dbe02544 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -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,61 +360,64 @@ 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) - -> Option { - if self.current_state.shadow_blur == 0.0 { - return None; + fn push_shadow_blur_render_targets_if_needed(scene: &mut Scene, + current_state: &State, + outline_bounds: RectF) + -> Option { + if current_state.shadow_blur == 0.0 { + return None; + } + + 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 = scene.push_render_target(render_target_y); + let render_target_x = RenderTarget::new(bounds.size(), String::new()); + let render_target_id_x = scene.push_render_target(render_target_x); + + Some(ShadowBlurRenderTargetInfo { + id_x: render_target_id_x, + id_y: render_target_id_y, + bounds, + sigma, + }) } - let sigma = self.current_state.shadow_blur * 0.5; - let bounds = outline_bounds.dilate(sigma * 3.0).round_out().to_i32(); + fn composite_shadow_blur_render_targets_if_needed(scene: &mut Scene, + info: Option, + clip_path: Option) { + let info = match info { + None => return, + Some(info) => info, + }; - 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_x = RenderTarget::new(bounds.size(), String::new()); - let render_target_id_x = self.canvas.scene.push_render_target(render_target_x); + let mut paint_x = Pattern::from_render_target(info.id_x, info.bounds.size()); + let mut paint_y = Pattern::from_render_target(info.id_y, info.bounds.size()); + paint_y.apply_transform(Transform2F::from_translation(info.bounds.origin().to_f32())); - Some(ShadowBlurRenderTargetInfo { - id_x: render_target_id_x, - id_y: render_target_id_y, - bounds, - sigma, - }) - } + let sigma = info.sigma; + paint_x.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::X, sigma })); + paint_y.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::Y, sigma })); - fn composite_shadow_blur_render_targets_if_needed(&mut self, - info: Option, - clip_path: Option) { - let info = match info { - None => return, - Some(info) => info, - }; + let paint_id_x = scene.push_paint(&Paint::from_pattern(paint_x)); + let paint_id_y = scene.push_paint(&Paint::from_pattern(paint_y)); - let mut paint_x = Pattern::from_render_target(info.id_x, info.bounds.size()); - let mut paint_y = Pattern::from_render_target(info.id_y, info.bounds.size()); - paint_y.apply_transform(Transform2F::from_translation(info.bounds.origin().to_f32())); + // TODO(pcwalton): Apply clip as necessary. + let outline_x = Outline::from_rect(RectF::new(vec2f(0.0, 0.0), + info.bounds.size().to_f32())); + let path_x = DrawPath::new(outline_x, paint_id_x); + let outline_y = Outline::from_rect(info.bounds.to_f32()); + let mut path_y = DrawPath::new(outline_y, paint_id_y); + path_y.set_clip_path(clip_path); - let sigma = info.sigma; - paint_x.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::X, sigma })); - paint_y.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::Y, sigma })); + scene.pop_render_target(); + scene.push_path(path_x); + scene.pop_render_target(); + scene.push_path(path_y); + } - 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)); - - // TODO(pcwalton): Apply clip as necessary. - let outline_x = Outline::from_rect(RectF::new(vec2f(0.0, 0.0), - info.bounds.size().to_f32())); - let path_x = DrawPath::new(outline_x, paint_id_x); - let outline_y = Outline::from_rect(info.bounds.to_f32()); - 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); } // 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, +} diff --git a/color/src/lib.rs b/color/src/lib.rs index 8bafd8f7..2a2486b9 100644 --- a/color/src/lib.rs +++ b/color/src/lib.rs @@ -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 { diff --git a/content/src/gradient.rs b/content/src/gradient.rs index c6460213..ecae5ac2 100644 --- a/content/src/gradient.rs +++ b/content/src/gradient.rs @@ -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 { diff --git a/content/src/pattern.rs b/content/src/pattern.rs index 6d3945d1..cdd34205 100644 --- a/content/src/pattern.rs +++ b/content/src/pattern.rs @@ -31,7 +31,6 @@ pub struct Pattern { transform: Transform2F, filter: Option, 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] diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index 9a6ec7b4..9d6c35b0 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -766,9 +766,9 @@ fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, filter: Option(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,16 +179,10 @@ fn export_ps(scene: &Scene, writer: &mut W) -> io::Result<()> { } } - match paint { - Paint::Color(color) => { - writeln!(writer, "{} {} {} setrgbcolor", color.r, color.g, color.b)?; - } - Paint::Gradient(_) => { - // TODO(pcwalton): Gradients. - } - Paint::Pattern(_) => { - // TODO(pcwalton): Patterns. - } + // TODO(pcwalton): Gradients and patterns. + if paint.is_color() { + let color = paint.base_color(); + writeln!(writer, "{} {} {} setrgbcolor", color.r, color.g, color.b)?; } writeln!(writer, "fill")?; diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index 264d35dd..cbb9570a 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -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, sampling_flags_1: TextureSamplingFlags, mask_0_fill_rule: FillRule, mask_1_fill_rule: Option, @@ -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, - color_texture_1: Option, + color_texture: Option, blend_mode: BlendMode, filter: Filter, mask_0_fill_rule: Option, @@ -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], } diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index a4c43803..21b46d29 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -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,46 +56,46 @@ 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! const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * MASK_TILES_ACROSS as i32; 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_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; -const COMBINER_CTRL_FILTER_BLUR: i32 = 0x3; +const COMBINER_CTRL_FILTER_RADIAL_GRADIENT: i32 = 0x1; +const COMBINER_CTRL_FILTER_TEXT: i32 = 0x2; +const COMBINER_CTRL_FILTER_BLUR: i32 = 0x3; -const COMBINER_CTRL_COMPOSITE_NORMAL: i32 = 0x0; -const COMBINER_CTRL_COMPOSITE_MULTIPLY: i32 = 0x1; -const COMBINER_CTRL_COMPOSITE_SCREEN: i32 = 0x2; -const COMBINER_CTRL_COMPOSITE_OVERLAY: i32 = 0x3; -const COMBINER_CTRL_COMPOSITE_DARKEN: i32 = 0x4; -const COMBINER_CTRL_COMPOSITE_LIGHTEN: i32 = 0x5; -const COMBINER_CTRL_COMPOSITE_COLOR_DODGE: i32 = 0x6; -const COMBINER_CTRL_COMPOSITE_COLOR_BURN: i32 = 0x7; -const COMBINER_CTRL_COMPOSITE_HARD_LIGHT: i32 = 0x8; -const COMBINER_CTRL_COMPOSITE_SOFT_LIGHT: i32 = 0x9; -const COMBINER_CTRL_COMPOSITE_DIFFERENCE: i32 = 0xa; -const COMBINER_CTRL_COMPOSITE_EXCLUSION: i32 = 0xb; -const COMBINER_CTRL_COMPOSITE_HUE: i32 = 0xc; -const COMBINER_CTRL_COMPOSITE_SATURATION: i32 = 0xd; -const COMBINER_CTRL_COMPOSITE_COLOR: i32 = 0xe; -const COMBINER_CTRL_COMPOSITE_LUMINOSITY: i32 = 0xf; +const COMBINER_CTRL_COMPOSITE_NORMAL: i32 = 0x0; +const COMBINER_CTRL_COMPOSITE_MULTIPLY: i32 = 0x1; +const COMBINER_CTRL_COMPOSITE_SCREEN: i32 = 0x2; +const COMBINER_CTRL_COMPOSITE_OVERLAY: i32 = 0x3; +const COMBINER_CTRL_COMPOSITE_DARKEN: i32 = 0x4; +const COMBINER_CTRL_COMPOSITE_LIGHTEN: i32 = 0x5; +const COMBINER_CTRL_COMPOSITE_COLOR_DODGE: i32 = 0x6; +const COMBINER_CTRL_COMPOSITE_COLOR_BURN: i32 = 0x7; +const COMBINER_CTRL_COMPOSITE_HARD_LIGHT: i32 = 0x8; +const COMBINER_CTRL_COMPOSITE_SOFT_LIGHT: i32 = 0x9; +const COMBINER_CTRL_COMPOSITE_DIFFERENCE: i32 = 0xa; +const COMBINER_CTRL_COMPOSITE_EXCLUSION: i32 = 0xb; +const COMBINER_CTRL_COMPOSITE_HUE: i32 = 0xc; +const COMBINER_CTRL_COMPOSITE_SATURATION: i32 = 0xd; +const COMBINER_CTRL_COMPOSITE_COLOR: i32 = 0xe; +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_COMPOSITE_SHIFT: i32 = 8; +const COMBINER_CTRL_MASK_0_SHIFT: i32 = 0; +const COMBINER_CTRL_MASK_1_SHIFT: i32 = 2; +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 where @@ -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, - color_texture_1: Option, mask_0_fill_rule: Option, mask_1_fill_rule: Option, 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, + } + } +} diff --git a/renderer/src/gpu_data.rs b/renderer/src/gpu_data.rs index de0ea9d2..c897a2ad 100644 --- a/renderer/src/gpu_data.rs +++ b/renderer/src/gpu_data.rs @@ -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, - pub color_texture_0: Option, - pub color_texture_1: Option, + pub color_texture: Option, pub mask_0_fill_rule: Option, pub mask_1_fill_rule: Option, 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) } diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index 9bfbc965..78f663a1 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -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, @@ -43,9 +39,20 @@ pub struct Palette { cache: HashMap, } +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct Paint { + base_color: ColorU, + overlay: Option, +} + +#[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()) - } - Paint::Pattern(_) => { - // TODO(pcwalton): Should we support this? - false + 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, + } } } } #[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,42 +169,96 @@ impl Paint { return; } - match *self { - Paint::Color(_) => {} - Paint::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)))); + 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 * transform.extract_scale().0)); + } } + PaintContents::Pattern(ref mut pattern) => pattern.apply_transform(*transform), } - Paint::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 { + &self.overlay + } + + #[inline] + pub fn overlay_mut(&mut self) -> &mut Option { + &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 + } +} + pub struct PaintInfo { /// The render commands needed to prepare the textures. pub render_commands: Vec, @@ -170,26 +270,30 @@ pub struct PaintInfo { /// /// The indices of this vector are render target IDs. pub render_target_metadata: Vec, - /// 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, + /// 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,116 +353,130 @@ 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) => { - // 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() { - None => PaintFilter::None, - Some(radii) => { - PaintFilter::RadialGradient { line: gradient.line(), radii } - } - }; - } - Paint::Pattern(ref pattern) => { - match *pattern.source() { - PatternSource::RenderTarget { id: render_target_id, .. } => { - texture_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. - let allocation_mode = AllocationMode::OwnPage; - texture_location = allocator.allocate(image.size(), allocation_mode); - image_texel_info.push(ImageTexelInfo { - location: texture_location, - texels: (*image.pixels()).clone(), - }); + 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. + 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(), } } + PaintContents::Pattern(ref pattern) => { + let location; + match *pattern.source() { + PatternSource::RenderTarget { id: render_target_id, .. } => { + 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. + let allocation_mode = AllocationMode::OwnPage; + location = allocator.allocate(image.size(), allocation_mode); + image_texel_info.push(ImageTexelInfo { + location, + texels: (*image.pixels()).clone(), + }); + } + } - sampling_flags = TextureSamplingFlags::empty(); - if pattern.repeat_x() { - sampling_flags.insert(TextureSamplingFlags::REPEAT_U); - } - if pattern.repeat_y() { - sampling_flags.insert(TextureSamplingFlags::REPEAT_V); - } - if !pattern.smoothing_enabled() { - sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN | - TextureSamplingFlags::NEAREST_MAG); - } + let mut sampling_flags = TextureSamplingFlags::empty(); + if pattern.repeat_x() { + sampling_flags.insert(TextureSamplingFlags::REPEAT_U); + } + if pattern.repeat_y() { + sampling_flags.insert(TextureSamplingFlags::REPEAT_V); + } + if !pattern.smoothing_enabled() { + sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN | + TextureSamplingFlags::NEAREST_MAG); + } - filter = match pattern.filter() { - None => PaintFilter::None, - Some(pattern_filter) => PaintFilter::PatternFilter(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 { - PaintFilter::None => Filter::None, - PaintFilter::RadialGradient { line, radii } => { - let uv_origin = self.texture_transform.vector; - Filter::RadialGradient { line, radii, uv_origin } + 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 = 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 { + 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 { - 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) { - render_commands.push(RenderCommand::UploadTexelData { - texels: Arc::new(self.create_texels()), - location: self.tile_location, - }); - } -} - -// Solid color allocation - -struct SolidColorTileBuilder { - tiles: Vec, -} - -struct SolidColorTile { - texels: Vec, - 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) { - 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>, } + +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, + } + } +} diff --git a/renderer/src/scene.rs b/renderer/src/scene.rs index 028173df..be194e69 100644 --- a/renderer/src/scene.rs +++ b/renderer/src/scene.rs @@ -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,23 +103,28 @@ 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) => { - match pattern.source() { - PatternSource::RenderTarget { id: old_render_target_id, size } => { - let mut new_pattern = - Pattern::from_render_target(*old_render_target_id, *size); - new_pattern.set_filter(pattern.filter()); - new_pattern.apply_transform(pattern.transform()); - 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)) + 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 = + Pattern::from_render_target(*old_render_target_id, *size); + new_pattern.set_filter(pattern.filter()); + new_pattern.apply_transform(pattern.transform()); + 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::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); } diff --git a/renderer/src/z_buffer.rs b/renderer/src/z_buffer.rs index 4df9991a..6b2afefe 100644 --- a/renderer/src/z_buffer.rs +++ b/renderer/src/z_buffer.rs @@ -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(), diff --git a/resources/shaders/gl3/tile.fs.glsl b/resources/shaders/gl3/tile.fs.glsl index 93d36805..5e673e61 100644 --- a/resources/shaders/gl3/tile.fs.glsl +++ b/resources/shaders/gl3/tile.fs.glsl @@ -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,24 +572,26 @@ void calculateColor(int ctrl){ maskAlpha = sampleMask(maskAlpha, uMaskTexture1, vMaskTexCoord1, maskCtrl1); - vec4 color = vec4(0.0); - if(((ctrl >> 6)& 0x1)!= 0){ - int color0Filter =(ctrl >> 4)& - 0x3; - color += filterColor(vColorTexCoord0, - uColorTexture0, - uGammaLUT, - uColorTexture0Size, - gl_FragCoord . xy, - uFramebufferSize, - uFilterParams0, - uFilterParams1, - uFilterParams2, - color0Filter); + vec4 color = vBaseColor; + int color0Combine =(ctrl >> 6)& + 0x3; + if(color0Combine != 0){ + int color0Filter =(ctrl >> 4)& 0x3; + vec4 color0 = filterColor(vColorTexCoord0, + uColorTexture0, + uGammaLUT, + uColorTexture0Size, + gl_FragCoord . xy, + uFramebufferSize, + uFilterParams0, + uFilterParams1, + uFilterParams2, + color0Filter); + color = combineColor0(color, color0, color0Combine); } - color . a *= maskAlpha * vOpacity; + color . a *= maskAlpha; int compositeOp =(ctrl >> 8)& 0xf; diff --git a/resources/shaders/gl3/tile.vs.glsl b/resources/shaders/gl3/tile.vs.glsl index 3ed8f3e5..babe8578 100644 --- a/resources/shaders/gl3/tile.vs.glsl +++ b/resources/shaders/gl3/tile.vs.glsl @@ -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); } diff --git a/resources/shaders/metal/tile.fs.metal b/resources/shaders/metal/tile.fs.metal index 79e5ea71..67565b7f 100644 --- a/resources/shaders/metal/tile.fs.metal +++ b/resources/shaders/metal/tile.fs.metal @@ -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 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 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 uMaskTexture0, thread const sampler uMaskTexture0Smplr, thread float3& vMaskTexCoord0, thread texture2d uMaskTexture1, thread const sampler uMaskTexture1Smplr, thread float3& vMaskTexCoord1, thread float2& vColorTexCoord0, thread texture2d uColorTexture0, thread const sampler uColorTexture0Smplr, thread texture2d 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 uDestTexture, thread const sampler uDestTextureSmplr, thread float4& oFragColor) +void calculateColor(thread const int& ctrl, thread texture2d uMaskTexture0, thread const sampler uMaskTexture0Smplr, thread float3& vMaskTexCoord0, thread texture2d uMaskTexture1, thread const sampler uMaskTexture1Smplr, thread float3& vMaskTexCoord1, thread float4& vBaseColor, thread float2& vColorTexCoord0, thread texture2d uColorTexture0, thread const sampler uColorTexture0Smplr, thread texture2d 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 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 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 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); + 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; + float4 param_15 = color0; + int param_16 = color0Combine; + color = combineColor0(param_14, param_15, param_16); } - color.w *= (maskAlpha * vOpacity); + color.w *= maskAlpha; int compositeOp = (ctrl >> 8) & 15; - 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_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; } diff --git a/resources/shaders/metal/tile.vs.metal b/resources/shaders/metal/tile.vs.metal index d1c868af..25b0756d 100644 --- a/resources/shaders/metal/tile.vs.metal +++ b/resources/shaders/metal/tile.vs.metal @@ -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; } diff --git a/shaders/tile.fs.glsl b/shaders/tile.fs.glsl index c09e3d31..e0056462 100644 --- a/shaders/tile.fs.glsl +++ b/shaders/tile.fs.glsl @@ -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,24 +570,26 @@ 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, - uColorTexture0, - uGammaLUT, - uColorTexture0Size, - gl_FragCoord.xy, - uFramebufferSize, - uFilterParams0, - uFilterParams1, - uFilterParams2, - color0Filter); + 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, + gl_FragCoord.xy, + uFramebufferSize, + uFilterParams0, + 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; diff --git a/shaders/tile.vs.glsl b/shaders/tile.vs.glsl index 2c7bc8c4..049f2bf6 100644 --- a/shaders/tile.vs.glsl +++ b/shaders/tile.vs.glsl @@ -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); } diff --git a/svg/src/lib.rs b/svg/src/lib.rs index 24840488..d66aba02 100644 --- a/svg/src/lib.rs +++ b/svg/src/lib.rs @@ -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 } } diff --git a/swf/src/shapes.rs b/swf/src/shapes.rs index aefafe37..88b45178 100644 --- a/swf/src/shapes.rs +++ b/swf/src/shapes.rs @@ -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,