Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize gradient drawing when only part of the gradient is visible. #83

Merged
merged 15 commits into from
Dec 14, 2023
Merged
2 changes: 1 addition & 1 deletion src/element.toit
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ abstract class CustomElement extends ClippingDiv_:
change-tracker.child-invalidated x y w h

draw canvas/Canvas -> none:
if not (x and y): return
if not (x and y and w and h): return
analysis := canvas.bounds-analysis x y w h
if analysis == Canvas.DISJOINT: return
autoclipped := analysis == Canvas.CANVAS-IN-AREA or analysis == Canvas.COINCIDENT
Expand Down
31 changes: 19 additions & 12 deletions src/gradient.toit
Original file line number Diff line number Diff line change
Expand Up @@ -243,14 +243,16 @@ class GradientRendering_:
// Use fixed point with 16 bits after the decimal point.
step := ((texture-length_ - h) << 16) / w
offset := 0
for i := 0; i < w; i += repeats:
lines := min repeats (w - i)
skew-adjusted-y := update-r-g-b-block.call y2 h lines orientation offset
if canvas.gray-scale:
canvas.pixmap (i + x2) skew-adjusted-y --pixels=b --source-width=source-width --orientation=orientation
else:
canvas.rgb-pixmap (i + x2) skew-adjusted-y --r=r --g=g --b=b --source-width=source-width --orientation=orientation
offset += step
canvas.visible-x-range x2 w: | x3 r3 |
offset += step * (x3 - x2)
for i := x3; i < r3; i += repeats:
lines := min repeats (r3 - i)
skew-adjusted-y := update-r-g-b-block.call y2 h lines orientation offset
if canvas.gray-scale:
canvas.pixmap i skew-adjusted-y --pixels=b --source-width=source-width --orientation=orientation
else:
canvas.rgb-pixmap i skew-adjusted-y --r=r --g=g --b=b --source-width=source-width --orientation=orientation
offset += step
else:
// The gradient goes broadly horizontally, and we draw in horizontal
// strips.
Expand All @@ -260,15 +262,20 @@ class GradientRendering_:
loop-body := : | i lines |
skew-adjusted-x := update-r-g-b-block.call x w lines ORIENTATION-0 offset
if canvas.gray-scale:
canvas.pixmap skew-adjusted-x (i + y) --pixels=b --source-width=source-width
canvas.pixmap skew-adjusted-x i --pixels=b --source-width=source-width
else:
canvas.rgb-pixmap skew-adjusted-x (i + y) --r=r --g=g --b=b --source-width=source-width
canvas.rgb-pixmap skew-adjusted-x i --r=r --g=g --b=b --source-width=source-width
offset += step
if up:
for i := 0; i < h; i += repeats: loop-body.call i (min repeats (h - i))
canvas.visible-y-range y (y + h): | y3 b3 |
offset += step * (y3 - y)
for i := y3; i < b3; i += repeats: loop-body.call i (min repeats (b3 - i))
else:
assert: repeats == 1
for i := h - 1; i >= 0; i--: loop-body.call i 1
b2 := y + h - 1
canvas.visible-y-range y b2: | y3 b3 |
offset += step * (b2 - b3)
for i := b3; i >= y3; i--: loop-body.call i 1

/// Returns a list of quadruples of the form starting-percent ending-percent start-color end-color.
static extract-ranges_ specifiers/List -> List:
Expand Down
26 changes: 26 additions & 0 deletions src/pixel-display.toit
Original file line number Diff line number Diff line change
Expand Up @@ -855,6 +855,32 @@ abstract class Canvas:
if x2 <= 0 and y2 <= 0 and right >= width_ and bottom >= height_: return CANVAS-IN-AREA
return OVERLAP

/**
Given an x position and a width in the coordinate system of the
canvas, calculates the leftmost and rightmost pixels that are visible, so
that superflous drawing operations can be optimized away
The block is called with the left and right pixel positions.
The function may conservatively add a pixel on one side.
*/
visible-x-range x/int w/int [block] -> none:
transform.invert.xywh 0 0 (width_ + 1) (height_ + 1): | x2 _ w2 _ |
block.call
max x x2
min (x + w) (x2 + w2)

/**
Given a y position and a height in the coordinate system of the
canvas, calculates the top and bottom pixels that are visible, so
that superflous drawing operations can be optimized away
The block is called with the top and bottom pixel positions.
The function may conservatively add a pixel on one side.
*/
visible-y-range y/int h/int [block] -> none:
transform.invert.xywh 0 0 (width_ + 1) (height_ + 1): | _ y2 _ h2 |
block.call
max y y2
min (y + h) (y2 + h2)

/**
Constant to indicate that all pixels are transparent.
For use with $composit.
Expand Down