///|
priv struct SunLayerScope {
spec : @core.LayerSpec
transform : @sun_graphics.Transform
}
///|
fn SunRasterRenderer::push_transparent_offscreen_canvas(
self : SunRasterRenderer,
) -> Unit {
let parent_canvas = self.canvas
self.canvas_stack.push(parent_canvas)
let w = parent_canvas.get_pixmap().get_width()
let h = parent_canvas.get_pixmap().get_height()
let layer_canvas = @sun_graphics.Canvas::new(w, h)
layer_canvas.clear(@sun_graphics.Color::new(r=0, g=0, b=0, a=0))
layer_canvas.set_transform(parent_canvas.get_transform())
match parent_canvas.get_clip_rect() {
Some(rect) => layer_canvas.set_clip_rect(rect)
None => ()
}
self.canvas = layer_canvas
}
///|
fn sun_transformed_rect_bounds(
transform : @sun_graphics.Transform,
rect : @core.Rect,
) -> @sun_graphics.Rect {
let x = rect.origin.x
let y = rect.origin.y
let width = rect.size.width
let height = rect.size.height
let (x0, y0) = transform.apply(x, y)
let (x1, y1) = transform.apply(x + width, y)
let (x2, y2) = transform.apply(x, y + height)
let (x3, y3) = transform.apply(x + width, y + height)
let left = x0.min(x1).min(x2).min(x3)
let top = y0.min(y1).min(y2).min(y3)
let right = x0.max(x1).max(x2).max(x3)
let bottom = y0.max(y1).max(y2).max(y3)
let device_left = sun_floor_to_int(left).to_double()
let device_top = sun_floor_to_int(top).to_double()
let device_right = sun_ceil_to_int(right).to_double()
let device_bottom = sun_ceil_to_int(bottom).to_double()
@sun_graphics.Rect::new(
x=device_left,
y=device_top,
width=(device_right - device_left).max(0.0),
height=(device_bottom - device_top).max(0.0),
)
}
///|
fn sun_floor_to_int(value : Double) -> Int {
let truncated = value.to_int()
if truncated.to_double() > value {
truncated - 1
} else {
truncated
}
}
///|
fn sun_ceil_to_int(value : Double) -> Int {
-sun_floor_to_int(0.0 - value)
}
///|
fn SunRasterRenderer::composite_rounded_clip(
self : SunRasterRenderer,
layer_pixmap : @sun_graphics.Pixmap,
rounded : @core.RoundedRect,
transform : @sun_graphics.Transform,
opacity : Double,
) -> Unit {
let masked = sun_rounded_masked_pixmap(layer_pixmap, rounded, transform)
self.canvas.draw_pixmap(
masked,
0,
0,
@sun_graphics.Paint::new().set_opacity(opacity),
)
}
///|
fn SunRasterRenderer::composite_layer_pixmap(
self : SunRasterRenderer,
layer_pixmap : @sun_graphics.Pixmap,
scope : SunLayerScope,
opacity : Double,
blend_mode : @sun_graphics.BlendMode,
) -> Unit {
let spec = scope.spec
let paint = @sun_graphics.Paint::new()
.set_opacity(sun_clamp_unit(opacity * spec.opacity))
.set_blend_mode(blend_mode)
match spec.mask {
NoMask => self.canvas.draw_pixmap(layer_pixmap, 0, 0, paint)
RectMask(rect) =>
self.canvas.draw_pixmap(
sun_rect_masked_pixmap(layer_pixmap, rect, scope.transform),
0,
0,
paint,
)
RoundedMask(rounded) =>
self.canvas.draw_pixmap(
sun_rounded_masked_pixmap(layer_pixmap, rounded, scope.transform),
0,
0,
paint,
)
}
}
///|
fn sun_rounded_masked_pixmap(
layer_pixmap : @sun_graphics.Pixmap,
rounded : @core.RoundedRect,
transform : @sun_graphics.Transform,
) -> @sun_graphics.Pixmap {
sun_masked_pixmap(layer_pixmap, transform, (x, y) => {
sun_rounded_mask_coverage(rounded, x, y)
})
}
///|
fn sun_rect_masked_pixmap(
layer_pixmap : @sun_graphics.Pixmap,
rect : @core.Rect,
transform : @sun_graphics.Transform,
) -> @sun_graphics.Pixmap {
sun_masked_pixmap(layer_pixmap, transform, (x, y) => {
if sun_rect_contains(rect, x, y) {
1.0
} else {
0.0
}
})
}
///|
fn sun_masked_pixmap(
layer_pixmap : @sun_graphics.Pixmap,
transform : @sun_graphics.Transform,
coverage_at : (Double, Double) -> Double,
) -> @sun_graphics.Pixmap {
let masked = @sun_graphics.Pixmap::new(
layer_pixmap.get_width(),
layer_pixmap.get_height(),
)
let w = layer_pixmap.get_width()
let h = layer_pixmap.get_height()
match transform.inverse() {
Some(inverse) =>
for py in 0.. 0 {
let (local_x, local_y) = inverse.apply(
px.to_double() + 0.5,
py.to_double() + 0.5,
)
let coverage = coverage_at(local_x, local_y).max(0.0).min(1.0)
if coverage > 0.0 {
let masked_alpha = sun_channel(
src.a.to_int().to_double() / 255.0 * coverage,
)
if masked_alpha.to_int() > 0 {
masked.set_pixel(px, py, src.with_alpha(masked_alpha))
}
}
}
}
}
None => ()
}
masked
}
///|
fn sun_rect_contains(rect : @core.Rect, x : Double, y : Double) -> Bool {
rect.size.width > 0.0 &&
rect.size.height > 0.0 &&
x >= rect.origin.x &&
x < rect.origin.x + rect.size.width &&
y >= rect.origin.y &&
y < rect.origin.y + rect.size.height
}
///|
fn sun_rounded_mask_coverage(
rounded : @core.RoundedRect,
x : Double,
y : Double,
) -> Double {
let rect = rounded.rect
let radius = rounded.radius.max(0.0)
let cx = rect.origin.x + rect.size.width * 0.5
let cy = rect.origin.y + rect.size.height * 0.5
let hx = rect.size.width * 0.5
let hy = rect.size.height * 0.5
let r = radius.min(hx).min(hy)
let qx = (x - cx).abs() - (hx - r)
let qy = (y - cy).abs() - (hy - r)
let outside_dist = sun_rounded_outside_distance(qx, qy)
let d = outside_dist - r
(0.5 - d).max(0.0).min(1.0)
}
///|
fn sun_rounded_outside_distance(qx : Double, qy : Double) -> Double {
if qx > 0.0 && qy > 0.0 {
(qx * qx + qy * qy).sqrt()
} else if qx > 0.0 {
qx
} else if qy > 0.0 {
qy
} else {
0.0
}
}