Skip to content

Commit a683e7b

Browse files
Align GPU ImageBrush with CPU
1 parent 005ffa4 commit a683e7b

3 files changed

Lines changed: 27 additions & 83 deletions

File tree

src/ImageSharp.Drawing.WebGPU/Shaders/WgslSource/Shared/ptcl.wgsl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ struct CmdImage {
106106
format: u32,
107107
x_extend_mode: u32,
108108
y_extend_mode: u32,
109-
quality: u32,
110109
alpha: f32,
111110
alpha_type: u32,
112111
}

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

Lines changed: 27 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ var<storage> segments: array<Segment>;
2323

2424
const GRADIENT_WIDTH = 512;
2525

26-
const IMAGE_QUALITY_LOW = 0u;
27-
const IMAGE_QUALITY_MEDIUM = 1u;
28-
const IMAGE_QUALITY_HIGH = 2u;
29-
3026
const LUMINANCE_MASK_LAYER = 0x10000u;
3127

3228
@group(0) @binding(2)
@@ -153,15 +149,14 @@ fn read_image(cmd_ix: u32) -> CmdImage {
153149
let alpha = f32(sample_alpha & 0xFFu) / 255.0;
154150
let format = sample_alpha >> 15u;
155151
let alpha_type = (sample_alpha >> 14u) & 0x1u;
156-
let quality = (sample_alpha >> 12u) & 0x3u;
157152
let x_extend = (sample_alpha >> 10u) & 0x3u;
158153
let y_extend = (sample_alpha >> 8u) & 0x3u;
159154
// The following are not intended to be bitcasts
160155
let x = f32(xy >> 16u);
161156
let y = f32(xy & 0xffffu);
162157
let width = f32(width_height >> 16u);
163158
let height = f32(width_height & 0xffffu);
164-
return CmdImage(matrx, xlat, vec2(x, y), vec2(width, height), format, x_extend, y_extend, quality, alpha, alpha_type);
159+
return CmdImage(matrx, xlat, vec2(x, y), vec2(width, height), format, x_extend, y_extend, alpha, alpha_type);
165160
}
166161

167162
fn read_end_clip(cmd_ix: u32) -> CmdEndClip {
@@ -338,33 +333,20 @@ fn extend_mode(t: f32, mode: u32, max: f32) -> f32 {
338333
}
339334
}
340335

341-
fn image_extend_mode_normalized(t: f32, mode: u32) -> f32 {
342-
switch mode {
343-
case EXTEND_PAD: {
344-
return clamp(t, 0.0, 1.0);
345-
}
346-
case EXTEND_REPEAT: {
347-
return fract(t);
348-
}
349-
case EXTEND_REFLECT, default: {
350-
let reflected = fract(0.5 * t) * 2.0;
351-
return 1.0 - abs(reflected - 1.0);
352-
}
353-
}
336+
fn image_repeat_mode_i32(t: f32, max: i32) -> i32 {
337+
let value = i32(t);
338+
let magnitude = select(value, -value, value < 0);
339+
let remainder = magnitude % max;
340+
let signed_remainder = select(remainder, -remainder, value < 0);
341+
342+
// Match the CPU ImageBrush index calculation: ((value % max) + max) % max.
343+
return (signed_remainder + max) % max;
354344
}
355345

356-
fn image_extend_mode(t: f32, mode: u32, max: f32) -> f32 {
357-
switch mode {
358-
case EXTEND_PAD: {
359-
return clamp(t, 0.0, max);
360-
}
361-
case EXTEND_REPEAT: {
362-
return image_extend_mode_normalized(t / max, mode) * max;
363-
}
364-
case EXTEND_REFLECT, default: {
365-
return image_extend_mode_normalized(t / max, mode) * max;
366-
}
367-
}
346+
fn image_reflect_mode_i32(t: f32, max: i32) -> i32 {
347+
let reflected = fract(0.5 * t) * 2.0;
348+
let wrapped = (1.0 - abs(reflected - 1.0)) * f32(max);
349+
return i32(clamp(wrapped, 0.0, f32(max - 1)));
368350
}
369351

370352
fn image_extend_mode_i32(t: f32, mode: u32, max: i32) -> i32 {
@@ -373,12 +355,10 @@ fn image_extend_mode_i32(t: f32, mode: u32, max: i32) -> i32 {
373355
return clamp(i32(t), 0, max - 1);
374356
}
375357
case EXTEND_REPEAT: {
376-
let wrapped = i32(t) % max;
377-
return select(wrapped + max, wrapped, wrapped >= 0);
358+
return image_repeat_mode_i32(t, max);
378359
}
379360
case EXTEND_REFLECT, default: {
380-
let reflected = clamp(image_extend_mode(t, mode, f32(max)), 0.0, f32(max - 1));
381-
return i32(reflected);
361+
return image_reflect_mode_i32(t, max);
382362
}
383363
}
384364
}
@@ -842,50 +822,17 @@ fn main(
842822
case CMD_IMAGE: {
843823
let image = read_image(cmd_ix);
844824
let draw_flags = info[ptcl[cmd_ix + 1u] - 1u];
845-
let atlas_max = image.atlas_offset + image.extents - vec2(1.0);
846-
switch image.quality {
847-
case IMAGE_QUALITY_LOW: {
848-
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
849-
// We only need to load from the textures if the value will be used.
850-
if area[i] != 0.0 {
851-
let my_xy = vec2(xy.x + f32(i), xy.y);
852-
var atlas_uv = image.matrx.xy * my_xy.x + image.matrx.zw * my_xy.y + image.xlat;
853-
let atlas_ix = image_extend_mode_i32(atlas_uv.x, image.x_extend_mode, i32(image.extents.x));
854-
let atlas_iy = image_extend_mode_i32(atlas_uv.y, image.y_extend_mode, i32(image.extents.y));
855-
let atlas_uv_clamped = vec2<i32>(i32(image.atlas_offset.x) + atlas_ix, i32(image.atlas_offset.y) + atlas_iy);
856-
// Nearest neighbor sampling
857-
let fg_rgba = maybe_premul_alpha(textureLoad(image_atlas, atlas_uv_clamped, 0), image.alpha_type);
858-
let fg_i = pixel_format(fg_rgba * area[i] * image.alpha, image.format);
859-
rgba[i] = compose_draw(rgba[i], fg_i, draw_flags);
860-
}
861-
}
862-
}
863-
case IMAGE_QUALITY_MEDIUM, default: {
864-
// We don't have an implementation for `IMAGE_QUALITY_HIGH` yet, just use the same as medium
865-
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
866-
// We only need to load from the textures if the value will be used.
867-
if area[i] != 0.0 {
868-
let my_xy = vec2(xy.x + f32(i), xy.y);
869-
var atlas_uv = image.matrx.xy * my_xy.x + image.matrx.zw * my_xy.y + image.xlat;
870-
atlas_uv.x = image_extend_mode(atlas_uv.x, image.x_extend_mode, image.extents.x);
871-
atlas_uv.y = image_extend_mode(atlas_uv.y, image.y_extend_mode, image.extents.y);
872-
atlas_uv = atlas_uv + image.atlas_offset - vec2(0.5);
873-
// TODO: If the image couldn't be added to the atlas (i.e. was too big), this isn't robust
874-
let atlas_uv_clamped = clamp(atlas_uv, image.atlas_offset, atlas_max);
875-
// We know that the floor and ceil are within the atlas area because atlas_max and
876-
// atlas_offset are integers
877-
let uv_quad = vec4(floor(atlas_uv_clamped), ceil(atlas_uv_clamped));
878-
let uv_frac = fract(atlas_uv);
879-
let a = maybe_premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.xy), 0), image.alpha_type);
880-
let b = maybe_premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.xw), 0), image.alpha_type);
881-
let c = maybe_premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.zy), 0), image.alpha_type);
882-
let d = maybe_premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.zw), 0), image.alpha_type);
883-
// Bilinear sampling
884-
let fg_rgba = mix(mix(a, b, uv_frac.y), mix(c, d, uv_frac.y), uv_frac.x);
885-
let fg_i = pixel_format(fg_rgba * area[i] * image.alpha, image.format);
886-
rgba[i] = compose_draw(rgba[i], fg_i, draw_flags);
887-
}
888-
}
825+
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
826+
// We only need to load from the textures if the value will be used.
827+
if area[i] != 0.0 {
828+
let my_xy = vec2(xy.x + f32(i), xy.y);
829+
var atlas_uv = image.matrx.xy * my_xy.x + image.matrx.zw * my_xy.y + image.xlat;
830+
let atlas_ix = image_extend_mode_i32(atlas_uv.x, image.x_extend_mode, i32(image.extents.x));
831+
let atlas_iy = image_extend_mode_i32(atlas_uv.y, image.y_extend_mode, i32(image.extents.y));
832+
let atlas_uv_clamped = vec2<i32>(i32(image.atlas_offset.x) + atlas_ix, i32(image.atlas_offset.y) + atlas_iy);
833+
let fg_rgba = maybe_premul_alpha(textureLoad(image_atlas, atlas_uv_clamped, 0), image.alpha_type);
834+
let fg_i = pixel_format(fg_rgba * area[i] * image.alpha, image.format);
835+
rgba[i] = compose_draw(rgba[i], fg_i, draw_flags);
889836
}
890837
}
891838
cmd_ix += 2u;
@@ -903,7 +850,7 @@ fn main(
903850
let rgba_sep = vec4(fg.rgb * a_inv, fg.a);
904851
textureStore(output, vec2<i32>(coords), rgba_sep);
905852
}
906-
}
853+
}
907854
}
908855

909856
fn premul_alpha(rgba: vec4<f32>) -> vec4<f32> {

src/ImageSharp.Drawing.WebGPU/WebGPUSceneResources.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,13 +713,11 @@ private static uint PackImageExtents(int width, int height)
713713
private static uint PackImageSampleInfo(TextureFormat textureFormat, uint xExtendMode, uint yExtendMode)
714714
{
715715
const uint alpha = 0xFFU;
716-
const uint qualityLow = 0U;
717716
const uint alphaTypeStraight = 0U;
718717
uint format = textureFormat == TextureFormat.Bgra8Unorm ? 1U : 0U;
719718
return alpha
720719
| (yExtendMode << 8)
721720
| (xExtendMode << 10)
722-
| (qualityLow << 12)
723721
| (alphaTypeStraight << 14)
724722
| (format << 15);
725723
}

0 commit comments

Comments
 (0)