use premultiplied alpha to avoid black fringes on edges
This commit is contained in:
parent
e317e21cfe
commit
1b20016642
@ -334,6 +334,7 @@ pub fn frame(self: *App, plt: Platform.Frame) !void {
|
||||
|
||||
const clear_color = Color.rgb(40, 40, 40);
|
||||
Gfx.setClearColor(clear_color);
|
||||
Gfx.setDefaultSampler(.linear);
|
||||
|
||||
const tile_size = Vec2.init(48, 48);
|
||||
var canvas = CanvasScaler{
|
||||
|
||||
@ -96,9 +96,11 @@ pub fn init(gpa: std.mem.Allocator, logger: sg.Logger) !void {
|
||||
s[0] = .{
|
||||
.blend = .{
|
||||
.enabled = true,
|
||||
.src_factor_rgb = .SRC_ALPHA,
|
||||
|
||||
.src_factor_rgb = .ONE,
|
||||
.dst_factor_rgb = .ONE_MINUS_SRC_ALPHA,
|
||||
.op_rgb = .ADD,
|
||||
|
||||
.src_factor_alpha = .ONE,
|
||||
.dst_factor_alpha = .ONE_MINUS_SRC_ALPHA,
|
||||
.op_alpha = .ADD,
|
||||
@ -646,13 +648,19 @@ pub fn deinitTexture(id: Texture.Id) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setTexture(id: Texture.Id, texture_data: ImageData) void {
|
||||
const SetTextureOptions = struct {
|
||||
data: ImageData,
|
||||
premultiplied: bool = false
|
||||
};
|
||||
|
||||
pub fn setTexture(id: Texture.Id, opts: SetTextureOptions) void {
|
||||
const self = &g_state;
|
||||
const texture = self.textures.get(id) orelse {
|
||||
log.warn("Attempt to set texture that doesn't exist: {}", .{id});
|
||||
return;
|
||||
};
|
||||
|
||||
const texture_data = opts.data;
|
||||
const pixel_count = texture_data.width*texture_data.height;
|
||||
|
||||
var image_data: sg.ImageData = .{};
|
||||
@ -671,6 +679,9 @@ pub fn setTexture(id: Texture.Id, texture_data: ImageData) void {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Apply premultiply, will need scratch buffer
|
||||
assert(opts.premultiplied);
|
||||
|
||||
var new_image_desc = sg.ImageDesc{
|
||||
.type = ._2D,
|
||||
.width = @intCast(texture_data.width),
|
||||
@ -874,7 +885,8 @@ pub fn deinitSprite(id: Sprite.Id) void {
|
||||
|
||||
const SetSpriteOptions = struct {
|
||||
image: ImageData,
|
||||
rect: ?Rect = null
|
||||
rect: ?Rect = null,
|
||||
premultiplied: bool = false
|
||||
};
|
||||
|
||||
pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void {
|
||||
@ -925,6 +937,10 @@ pub fn setSprite(id: Sprite.Id, opts: SetSpriteOptions) void {
|
||||
};
|
||||
}
|
||||
|
||||
if (!opts.premultiplied) {
|
||||
sprite_data.premultiplyAlpha();
|
||||
}
|
||||
|
||||
var old_size: ?Vec2 = null;
|
||||
if (sprite.data) |old_sprite_data| {
|
||||
old_size = Vec2.initFromInt(u32, old_sprite_data.width, old_sprite_data.height);
|
||||
@ -1026,6 +1042,8 @@ fn repackSpritesheetIfNeeded(id: Spritesheet.Id) void {
|
||||
repackSpritesheet(id);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
const self = &g_state;
|
||||
const spritesheet = self.spritesheets.get(id) orelse return;
|
||||
@ -1072,9 +1090,43 @@ fn rebuildSpritesheetTextureIfNeeded(id: Spritesheet.Id) void {
|
||||
sprite_data.width,
|
||||
sprite_data.height
|
||||
);
|
||||
if (sprite.padding > 0) {
|
||||
for (0..sprite_data.width) |ox| {
|
||||
const pixel = image_data.getPixel(x+ox, y);
|
||||
for (0..sprite.padding) |i| {
|
||||
@memcpy(image_data.getPixel(x+ox, y-(i+1)), pixel);
|
||||
}
|
||||
}
|
||||
|
||||
setTexture(spritesheet.texture, image_data);
|
||||
for (0..sprite_data.width) |ox| {
|
||||
const pixel = image_data.getPixel(x+ox, y+sprite_data.height-1);
|
||||
for (0..sprite.padding) |i| {
|
||||
@memcpy(image_data.getPixel(x+ox, y+sprite_data.height+i), pixel);
|
||||
}
|
||||
}
|
||||
|
||||
for (0..sprite_data.height) |oy| {
|
||||
const pixel = image_data.getPixel(x, y+oy);
|
||||
for (0..sprite.padding) |i| {
|
||||
@memcpy(image_data.getPixel(x-(i+1), y+oy), pixel);
|
||||
}
|
||||
}
|
||||
|
||||
for (0..sprite_data.height) |oy| {
|
||||
const pixel = image_data.getPixel(x+sprite_data.width-1, y+oy);
|
||||
for (0..sprite.padding) |i| {
|
||||
@memcpy(image_data.getPixel(x+sprite_data.width+i, y+oy), pixel);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add padding to corners.
|
||||
}
|
||||
}
|
||||
|
||||
setTexture(spritesheet.texture, .{
|
||||
.data = image_data,
|
||||
.premultiplied = true
|
||||
});
|
||||
|
||||
spritesheet.needs_texture_rebuild = false;
|
||||
}
|
||||
@ -1125,17 +1177,11 @@ pub fn getSpriteUVRect(id: Sprite.Id) ?Rect {
|
||||
|
||||
assert(sprite.position != null);
|
||||
|
||||
|
||||
const sprite_size = Vec2.initFromInt(u32, sprite_data.width, sprite_data.height);
|
||||
var uv_rect = Rect{
|
||||
return Rect{
|
||||
.pos = sprite.position.?.divide(spritesheet.size),
|
||||
.size = sprite_size.divide(spritesheet.size),
|
||||
};
|
||||
|
||||
const texel_size = Vec2.init(1, 1).divide(spritesheet.size);
|
||||
uv_rect = uv_rect.shrink(texel_size.divideScalar(8));
|
||||
|
||||
return uv_rect;
|
||||
}
|
||||
|
||||
pub fn drawSpriteQuad(id: Sprite.Id, points: Quad, color: Color) void {
|
||||
@ -1283,7 +1329,7 @@ pub const ImageData = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPixel(self: ImageData, x: u32, y: u32) []u8 {
|
||||
pub fn getPixel(self: ImageData, x: usize, y: usize) []u8 {
|
||||
const pixel_index = y * self.width + x;
|
||||
if (self.pixels) |pixels| {
|
||||
return switch (pixels) {
|
||||
@ -1330,6 +1376,23 @@ pub const ImageData = struct {
|
||||
self.setRect(x, y, width, height, pixel);
|
||||
}
|
||||
|
||||
pub fn premultiplyAlpha(self: ImageData) void {
|
||||
const pixels = self.pixels orelse return;
|
||||
if (pixels != .rgba8) return;
|
||||
const rgba8_pixels = pixels.rgba8;
|
||||
|
||||
for (0..self.height) |y| {
|
||||
for (0..self.width) |x| {
|
||||
const pixel_index = y * self.width + x;
|
||||
const rgba8_pixel = rgba8_pixels[(4*pixel_index)..][0..4];
|
||||
|
||||
inline for (0..3) |i| {
|
||||
rgba8_pixel[i] = @intCast(@as(u16, rgba8_pixel[i]) * rgba8_pixel[3] / 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copyFrom(
|
||||
self: ImageData,
|
||||
source: ImageData,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user