Skip to content

Commit 557111d

Browse files
Fix GPU brush transforms
1 parent c6fc3ab commit 557111d

File tree

1 file changed

+72
-25
lines changed

1 file changed

+72
-25
lines changed

src/ImageSharp.Drawing.WebGPU/Shaders/WgslSource/draw_leaf.wgsl

Lines changed: 72 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,13 @@ fn main(
166166
var p1 = bitcast<vec2<f32>>(vec2(scene[dd + 3u], scene[dd + 4u]));
167167
var r0 = bitcast<f32>(scene[dd + 5u]);
168168
var r1 = bitcast<f32>(scene[dd + 6u]);
169-
let user_to_gradient = transform_inverse(transform);
169+
let scale_x = length(transform.matrx.xy);
170+
let scale_y = length(transform.matrx.zw);
171+
let average_scale = 0.5 * (scale_x + scale_y);
172+
p0 = transform_apply(transform, p0);
173+
p1 = transform_apply(transform, p1);
174+
r0 *= average_scale;
175+
r1 *= average_scale;
170176
// Output variables
171177
var xform = Transform();
172178
var focal_x = 0.0;
@@ -177,10 +183,7 @@ fn main(
177183
// When the radii are the same, emit a strip gradient
178184
kind = RAD_GRAD_KIND_STRIP;
179185
let scaled = r0 / distance(p0, p1);
180-
xform = transform_mul(
181-
two_point_to_unit_line(p0, p1),
182-
user_to_gradient
183-
);
186+
xform = two_point_to_unit_line(p0, p1);
184187
radius = scaled * scaled;
185188
} else {
186189
// Assume a two point conical gradient unless the centers
@@ -204,10 +207,7 @@ fn main(
204207
focal_x = r0 / (r0 - r1);
205208
let cf = (1.0 - focal_x) * p0 + focal_x * p1;
206209
radius = r1 / (distance(cf, p1));
207-
let user_to_unit_line = transform_mul(
208-
two_point_to_unit_line(cf, p1),
209-
user_to_gradient
210-
);
210+
let user_to_unit_line = two_point_to_unit_line(cf, p1);
211211
var user_to_scaled = user_to_unit_line;
212212
// When r == 1.0, focal point is on circle
213213
if abs(radius - 1.0) <= GRADIENT_EPSILON {
@@ -241,15 +241,24 @@ fn main(
241241
}
242242
case DRAWTAG_FILL_ELLIPTIC_GRADIENT: {
243243
info[di] = draw_flags;
244-
var center = bitcast<vec2<f32>>(vec2(scene[dd + 1u], scene[dd + 2u]));
245-
var axis_end = bitcast<vec2<f32>>(vec2(scene[dd + 3u], scene[dd + 4u]));
244+
let local_center = bitcast<vec2<f32>>(vec2(scene[dd + 1u], scene[dd + 2u]));
245+
let local_axis_end = bitcast<vec2<f32>>(vec2(scene[dd + 3u], scene[dd + 4u]));
246246
let axis_ratio = bitcast<f32>(scene[dd + 5u]);
247-
center = transform_apply(transform, center);
248-
axis_end = transform_apply(transform, axis_end);
247+
248+
var center = transform_apply(transform, local_center);
249+
var axis_end = transform_apply(transform, local_axis_end);
249250
let dxy = axis_end - center;
250251
let axis = length(dxy);
251252
let inv_axis = 1.0 / axis;
252-
let inv_second_axis = inv_axis / axis_ratio;
253+
let local_axis = local_axis_end - local_center;
254+
let local_axis_len = length(local_axis);
255+
let local_second_end = local_center + vec2(
256+
(-local_axis.y / local_axis_len) * (local_axis_len * axis_ratio),
257+
(local_axis.x / local_axis_len) * (local_axis_len * axis_ratio));
258+
let second_end = transform_apply(transform, local_second_end);
259+
let second_axis = length(second_end - center);
260+
let transformed_axis_ratio = select(axis_ratio, second_axis / axis, axis > 0.0);
261+
let inv_second_axis = inv_axis / transformed_axis_ratio;
253262
let cos_theta = dxy.x * inv_axis;
254263
let sin_theta = dxy.y * inv_axis;
255264
// Map the ellipse to the unit circle so the fill parameter is length(local_xy).
@@ -268,17 +277,55 @@ fn main(
268277
}
269278
case DRAWTAG_FILL_SWEEP_GRADIENT: {
270279
info[di] = draw_flags;
271-
let p0 = bitcast<vec2<f32>>(vec2(scene[dd + 1u], scene[dd + 2u]));
272-
let xform = transform_mul(transform, Transform(vec4(1.0, 0.0, 0.0, 1.0), p0, vec3(0.0, 0.0, 1.0)));
273-
let inv = transform_inverse(xform);
274-
info[di + 1u] = bitcast<u32>(inv.matrx.x);
275-
info[di + 2u] = bitcast<u32>(inv.matrx.y);
276-
info[di + 3u] = bitcast<u32>(inv.matrx.z);
277-
info[di + 4u] = bitcast<u32>(inv.matrx.w);
278-
info[di + 5u] = bitcast<u32>(inv.translate.x);
279-
info[di + 6u] = bitcast<u32>(inv.translate.y);
280-
info[di + 7u] = scene[dd + 3u];
281-
info[di + 8u] = scene[dd + 4u];
280+
let center = bitcast<vec2<f32>>(vec2(scene[dd + 1u], scene[dd + 2u]));
281+
let t0 = bitcast<f32>(scene[dd + 3u]);
282+
let t1 = bitcast<f32>(scene[dd + 4u]);
283+
let tau = 6.2831855;
284+
let transformed_center = transform_apply(transform, center);
285+
let start_dir = transform_apply(transform, center + vec2(cos(t0 * tau), -sin(t0 * tau)));
286+
let end_dir = transform_apply(transform, center + vec2(cos(t1 * tau), -sin(t1 * tau)));
287+
let start_xy = start_dir - transformed_center;
288+
let end_xy = end_dir - transformed_center;
289+
let transformed_t0 = fract((atan2(-start_xy.y, start_xy.x) / tau) + 1.0);
290+
let transformed_end = fract((atan2(-end_xy.y, end_xy.x) / tau) + 1.0);
291+
let minimum_magnitude = abs(t1 - t0);
292+
let determinant = (transform.matrx.x * transform.matrx.w) - (transform.matrx.y * transform.matrx.z);
293+
var direction_hint = sign(t1 - t0);
294+
if direction_hint == 0.0 {
295+
direction_hint = 1.0;
296+
}
297+
298+
if determinant < 0.0 {
299+
direction_hint = -direction_hint;
300+
}
301+
302+
var delta = transformed_end - transformed_t0;
303+
if direction_hint >= 0.0 {
304+
while delta < 0.0 {
305+
delta += 1.0;
306+
}
307+
308+
if abs(delta) < 1e-6 && minimum_magnitude >= 1.0 - 1e-6 {
309+
delta = 1.0;
310+
}
311+
} else {
312+
while delta > 0.0 {
313+
delta -= 1.0;
314+
}
315+
316+
if abs(delta) < 1e-6 && minimum_magnitude >= 1.0 - 1e-6 {
317+
delta = -1.0;
318+
}
319+
}
320+
321+
info[di + 1u] = bitcast<u32>(1.0);
322+
info[di + 2u] = bitcast<u32>(0.0);
323+
info[di + 3u] = bitcast<u32>(0.0);
324+
info[di + 4u] = bitcast<u32>(1.0);
325+
info[di + 5u] = bitcast<u32>(-transformed_center.x);
326+
info[di + 6u] = bitcast<u32>(-transformed_center.y);
327+
info[di + 7u] = bitcast<u32>(transformed_t0);
328+
info[di + 8u] = bitcast<u32>(transformed_t0 + delta);
282329
}
283330
case DRAWTAG_FILL_IMAGE: {
284331
info[di] = draw_flags;

0 commit comments

Comments
 (0)