diff --git a/src/platform/graphics.zig b/src/platform/graphics.zig index 3951463..c2e28a1 100644 --- a/src/platform/graphics.zig +++ b/src/platform/graphics.zig @@ -660,10 +660,19 @@ pub fn setTexture(id: Texture.Id, opts: SetTextureOptions) void { return; }; - // TODO: Apply premultiply, will need scratch buffer - assert(opts.premultiplied); + var scratch = std.heap.ArenaAllocator.init(self.gpa); var mipchain = opts.mipchain; + if (!opts.premultiplied) { + var new_mipchain = mipchain.clone(scratch.allocator()) catch |e| { + log.warn("Failed to clone mipmap: {}", .{e}); + return; + }; + + new_mipchain.premultiplyAlpha(); + + mipchain = new_mipchain; + } var image_data: sg.ImageData = .{}; assert(mipchain.length() <= image_data.mip_levels.len); @@ -917,12 +926,7 @@ pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void { return; }; - next_mipchain.copyFromRect( - opts.mipchain, - 0, 0, - @trunc(rect.pos.x), @trunc(rect.pos.y), - @trunc(rect.size.x), @trunc(rect.size.y) - ); + next_mipchain.copyFromRect(opts.mipchain, 0, 0, .initTrunc(rect)); } else { next_mipchain = opts.mipchain.clone(next_arena.allocator()) catch |e| { log.warn("Failed to clone mipchain: {}", .{e}); @@ -931,9 +935,7 @@ pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void { } if (!opts.premultiplied) { - for (0..next_mipchain.length()) |index| { - next_mipchain.getLevel(index).premultiplyAlpha(); - } + next_mipchain.premultiplyAlpha(); } var prev_size: ?Vec2 = null; @@ -1043,6 +1045,9 @@ fn repackSpritesheetIfNeeded(id: Spritesheet.Id) void { // TODO: This function is a bit large, there are some easy things that could be moved to separate function. // e.g. extruding padding. fn rebuildSpritesheetTextureIfNeeded(id: Spritesheet.Id) void { + // TODO: Allow generating mipmaps + // https://github.com/floooh/sokol/issues/102#issuecomment-2926566603 + const self = &g_state; const spritesheet = self.spritesheets.get(id) orelse return; if (!spritesheet.needs_texture_rebuild) { @@ -1095,36 +1100,22 @@ fn rebuildSpritesheetTextureIfNeeded(id: Spritesheet.Id) void { ); level.copyFrom(sprite_level, x, y); + // TODO: Make `edge_extrusion` amount configurable + var edge_extrusion: u32 = 0; if (padding > 0) { - for (0..sprite_level.width) |ox| { - const pixel = level.getPixel(x+ox, y); - for (0..padding) |i| { - @memcpy(level.getPixel(x+ox, y-(i+1)), pixel); - } - } + edge_extrusion = 1; + } - for (0..sprite_level.width) |ox| { - const pixel = level.getPixel(x+ox, y+sprite_level.height-1); - for (0..padding) |i| { - @memcpy(level.getPixel(x+ox, y+sprite_level.height+i), pixel); - } - } - - for (0..sprite_level.height) |oy| { - const pixel = level.getPixel(x, y+oy); - for (0..padding) |i| { - @memcpy(level.getPixel(x-(i+1), y+oy), pixel); - } - } - - for (0..sprite_level.height) |oy| { - const pixel = level.getPixel(x+sprite_level.width-1, y+oy); - for (0..padding) |i| { - @memcpy(level.getPixel(x+sprite_level.width+i, y+oy), pixel); - } - } - - // TODO: Add padding to corners. + if (edge_extrusion > 0) { + level.extrudeEdge( + .{ + .x = x, + .y = y, + .width = sprite_level.width, + .height = sprite_level.height, + }, + edge_extrusion + ); } } } @@ -1301,6 +1292,22 @@ pub const Image = struct { height: u32, }; + const Rect = struct { + x: u32, + y: u32, + width: u32, + height: u32, + + fn initTrunc(r: Math.Rect) Image.Rect { + return .{ + .x = @trunc(r.pos.x), + .y = @trunc(r.pos.y), + .width = @trunc(r.size.x), + .height = @trunc(r.size.y), + }; + } + }; + pub fn initAlloc(gpa: Allocator, opts: InitOptions) !Image { const pixel_count = opts.width * opts.height; const pixel_size = opts.format.pixelSize(); @@ -1385,15 +1392,12 @@ pub const Image = struct { source: Image, cx: u32, cy: u32, - source_cx: u32, - source_cy: u32, - source_width: u32, - source_height: u32, + source_rect: Image.Rect, ) void { assert(source.format == self.format); - for (0..source_height) |oy| { - const source_y = source_cy+oy; + for (0..source_rect.height) |oy| { + const source_y = source_rect.y+oy; const dest_y = cy+oy; if (dest_y >= self.height) { continue; @@ -1402,8 +1406,8 @@ pub const Image = struct { continue; } - for (0..source_width) |ox| { - const source_x = source_cx+ox; + for (0..source_rect.width) |ox| { + const source_x = source_rect.x+ox; const dest_x = cx+ox; if (dest_x >= self.width) { continue; @@ -1426,7 +1430,39 @@ pub const Image = struct { cx: u32, cy: u32, ) void { - self.copyFromRect(source, cx, cy, 0, 0, source.width, source.height); + self.copyFromRect(source, cx, cy, .{ .x = 0, .y = 0, .width = source.width, .height = source.height}); + } + + pub fn extrudeEdge(self: Image, rect: Image.Rect, size: u32) void { + for (0..rect.width) |ox| { + const pixel = self.getPixel(rect.x+ox, rect.y); + for (0..size) |i| { + @memcpy(self.getPixel(rect.x+ox, rect.y-(i+1)), pixel); + } + } + + for (0..rect.width) |ox| { + const pixel = self.getPixel(rect.x+ox, rect.y+rect.height-1); + for (0..size) |i| { + @memcpy(self.getPixel(rect.x+ox, rect.y+rect.height+i), pixel); + } + } + + for (0..rect.height) |oy| { + const pixel = self.getPixel(rect.x, rect.y+oy); + for (0..size) |i| { + @memcpy(self.getPixel(rect.x-(i+1), rect.y+oy), pixel); + } + } + + for (0..rect.height) |oy| { + const pixel = self.getPixel(rect.x+rect.width-1, rect.y+oy); + for (0..size) |i| { + @memcpy(self.getPixel(rect.x+rect.width+i, rect.y+oy), pixel); + } + } + + // TODO: Add padding to corners. } }; @@ -1562,10 +1598,7 @@ pub const Mipchain = struct { source: Mipchain, cx: u32, cy: u32, - source_cx: u32, - source_cy: u32, - source_width: u32, - source_height: u32, + source_rect: Image.Rect, ) void { assert(self.length() == source.length()); @@ -1576,13 +1609,21 @@ pub const Mipchain = struct { source_level, cx >> @intCast(index), cy >> @intCast(index), - source_cx >> @intCast(index), - source_cy >> @intCast(index), - source_width >> @intCast(index), - source_height >> @intCast(index), + .{ + .x = source_rect.x >> @intCast(index), + .y = source_rect.y >> @intCast(index), + .width = source_rect.width >> @intCast(index), + .height = source_rect.height >> @intCast(index), + } ); } } + + pub fn premultiplyAlpha(self: Mipchain) void { + for (0..self.length()) |index| { + self.getLevel(index).premultiplyAlpha(); + } + } }; pub const Texture = struct {