chore: reimplement alpha blending
This commit is contained in:
parent
2c6a6a2ebb
commit
27dd49d12f
323
src/core/ppu.zig
323
src/core/ppu.zig
|
@ -398,7 +398,7 @@ pub const Ppu = struct {
|
||||||
// Sprite Palette starts at 0x0500_0200
|
// Sprite Palette starts at 0x0500_0200
|
||||||
if (pal_id != 0) {
|
if (pal_id != 0) {
|
||||||
const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
|
const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
|
||||||
copyToSpriteBuffer(self.bld.cnt, &self.scanline, x, bgr555);
|
drawSpritePixel(self.bld.cnt, &self.scanline, x, bgr555);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -448,7 +448,7 @@ pub const Ppu = struct {
|
||||||
// Sprite Palette starts at 0x0500_0200
|
// Sprite Palette starts at 0x0500_0200
|
||||||
if (pal_id != 0) {
|
if (pal_id != 0) {
|
||||||
const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
|
const bgr555 = self.palette.read(u16, 0x200 + pal_id * 2);
|
||||||
copyToSpriteBuffer(self.bld.cnt, &self.scanline, x, bgr555);
|
drawSpritePixel(self.bld.cnt, &self.scanline, x, bgr555);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,10 +493,7 @@ pub const Ppu = struct {
|
||||||
const tile_addr = char_base + (tile_id * 0x40) + (row * 0x8) + col;
|
const tile_addr = char_base + (tile_id * 0x40) + (row * 0x8) + col;
|
||||||
const pal_id: u16 = self.vram.buf[tile_addr];
|
const pal_id: u16 = self.vram.buf[tile_addr];
|
||||||
|
|
||||||
if (pal_id != 0) {
|
if (pal_id != 0) self.drawBackgroundPixel(n, i, self.palette.read(u16, pal_id * 2));
|
||||||
const bgr555 = self.palette.read(u16, pal_id * 2);
|
|
||||||
self.copyToBackgroundBuffer(n, win_bounds, i, bgr555);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update BGxX and BGxY
|
// Update BGxX and BGxY
|
||||||
|
@ -551,10 +548,7 @@ pub const Ppu = struct {
|
||||||
// and then we can index the palette
|
// and then we can index the palette
|
||||||
const pal_id: u16 = if (!is_8bpp) get4bppTilePalette(entry.pal_bank.read(), col, tile) else tile;
|
const pal_id: u16 = if (!is_8bpp) get4bppTilePalette(entry.pal_bank.read(), col, tile) else tile;
|
||||||
|
|
||||||
if (pal_id != 0) {
|
if (pal_id != 0) self.drawBackgroundPixel(n, i, self.palette.read(u16, pal_id * 2));
|
||||||
const bgr555 = self.palette.read(u16, pal_id * 2);
|
|
||||||
self.copyToBackgroundBuffer(n, win_bounds, i, bgr555);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,8 +663,7 @@ pub const Ppu = struct {
|
||||||
// FIXME: @ptrCast between slices changing the length isn't implemented yet
|
// FIXME: @ptrCast between slices changing the length isn't implemented yet
|
||||||
const framebuf = @ptrCast([*]u32, @alignCast(@alignOf(u32), self.framebuf.get(.Emulator)));
|
const framebuf = @ptrCast([*]u32, @alignCast(@alignOf(u32), self.framebuf.get(.Emulator)));
|
||||||
|
|
||||||
for (self.scanline.top()) |maybe_px, i| {
|
for (self.scanline.top()) |maybe_top, i| {
|
||||||
const maybe_top = maybe_px;
|
|
||||||
const maybe_btm = self.scanline.btm()[i];
|
const maybe_btm = self.scanline.btm()[i];
|
||||||
|
|
||||||
const bgr555 = self.getBgr555(maybe_top, maybe_btm);
|
const bgr555 = self.getBgr555(maybe_top, maybe_btm);
|
||||||
|
@ -683,12 +676,25 @@ pub const Ppu = struct {
|
||||||
std.mem.set(?Sprite, self.scanline_sprites, null);
|
std.mem.set(?Sprite, self.scanline_sprites, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getBgr555(self: *Self, maybe_top: ?u16, maybe_btm: ?u16) u16 {
|
fn getBgr555(self: *Self, maybe_top: Scanline.Pixel, maybe_btm: Scanline.Pixel) u16 {
|
||||||
if (maybe_btm) |btm| {
|
|
||||||
return switch (self.bld.cnt.mode.read()) {
|
return switch (self.bld.cnt.mode.read()) {
|
||||||
0b00 => if (maybe_top) |top| top else btm,
|
0b00 => switch (maybe_top) {
|
||||||
0b01 => if (maybe_top) |top| alphaBlend(btm, top, self.bld.alpha) else btm,
|
.set => |top| top,
|
||||||
0b10 => blk: {
|
else => self.palette.backdrop(),
|
||||||
|
},
|
||||||
|
0b01 => switch (maybe_top) {
|
||||||
|
.set => |top| switch (maybe_btm) {
|
||||||
|
.set => |btm| alphaBlend(top, btm, self.bld.alpha), // ALPHA_BLEND
|
||||||
|
else => top,
|
||||||
|
},
|
||||||
|
else => switch (maybe_btm) {
|
||||||
|
.set => |btm| btm,
|
||||||
|
else => self.palette.backdrop(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0b10 => switch (maybe_btm) {
|
||||||
|
.set => |btm| blk: {
|
||||||
|
// BLD_WHITE
|
||||||
const evy: u16 = self.bld.y.evy.read();
|
const evy: u16 = self.bld.y.evy.read();
|
||||||
|
|
||||||
const r = btm & 0x1F;
|
const r = btm & 0x1F;
|
||||||
|
@ -701,51 +707,87 @@ pub const Ppu = struct {
|
||||||
|
|
||||||
break :blk (bld_b << 10) | (bld_g << 5) | bld_r;
|
break :blk (bld_b << 10) | (bld_g << 5) | bld_r;
|
||||||
},
|
},
|
||||||
0b11 => blk: {
|
else => switch (maybe_top) {
|
||||||
|
.set => |top| top,
|
||||||
|
else => self.palette.backdrop(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
0b11 => switch (maybe_btm) {
|
||||||
|
.set => |btm| blk: {
|
||||||
|
// BLD_BLACK
|
||||||
const evy: u16 = self.bld.y.evy.read();
|
const evy: u16 = self.bld.y.evy.read();
|
||||||
|
|
||||||
const btm_r = btm & 0x1F;
|
const r = btm & 0x1F;
|
||||||
const btm_g = (btm >> 5) & 0x1F;
|
const g = (btm >> 5) & 0x1F;
|
||||||
const btm_b = (btm >> 10) & 0x1F;
|
const b = (btm >> 10) & 0x1F;
|
||||||
|
|
||||||
const bld_r = btm_r - ((btm_r * evy) >> 4);
|
const bld_r = r - ((r * evy) >> 4);
|
||||||
const bld_g = btm_g - ((btm_g * evy) >> 4);
|
const bld_g = g - ((g * evy) >> 4);
|
||||||
const bld_b = btm_b - ((btm_b * evy) >> 4);
|
const bld_b = b - ((b * evy) >> 4);
|
||||||
|
|
||||||
break :blk (bld_b << 10) | (bld_g << 5) | bld_r;
|
break :blk (bld_b << 10) | (bld_g << 5) | bld_r;
|
||||||
},
|
},
|
||||||
|
else => switch (maybe_top) {
|
||||||
|
.set => |top| top,
|
||||||
|
else => self.palette.backdrop(),
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maybe_top) |top| return top;
|
fn drawBackgroundPixel(self: *Self, comptime layer: u2, i: usize, bgr555: u16) void {
|
||||||
return self.palette.backdrop();
|
// When writing to the scanline buffer, we want to be aware of a top and bottom layer. Some preconditions were
|
||||||
}
|
// already determined by shouldDrawBackground, so we should be aware of what we can assume to be true or false
|
||||||
|
|
||||||
fn copyToBackgroundBuffer(self: *Self, comptime n: u2, bounds: ?WindowBounds, i: usize, bgr555: u16) void {
|
switch (self.bld.cnt.mode.read()) {
|
||||||
if (self.bld.cnt.mode.read() != 0b00) {
|
0b00 => {}, // pass through
|
||||||
// Standard Alpha Blending
|
0b01 => {
|
||||||
const a_layers = self.bld.cnt.layer_a.read();
|
// We are to alpha blend here so we should pay attention to which layer ths pixel should be written to
|
||||||
const is_blend_enabled = (a_layers >> n) & 1 == 1;
|
// FIXME: We redo work here that we've already figured out. Is this worth refactorning?
|
||||||
|
|
||||||
// If Alpha Blending is enabled and we've found an eligible layer for
|
// If the current layer is makred as Layer A, write to top buffer
|
||||||
// Pixel A, store the pixel in the bottom pixel buffer
|
const top_layer = self.bld.cnt.layer_a.read();
|
||||||
|
const is_top_layer = (top_layer >> layer) & 1 == 1;
|
||||||
|
|
||||||
const win_part = if (bounds) |win| blk: {
|
if (is_top_layer) {
|
||||||
// Window Enabled
|
self.scanline.top()[i] = Scanline.Pixel.from(bgr555);
|
||||||
break :blk switch (win) {
|
|
||||||
.win0 => self.win.in.w0_bld.read(),
|
|
||||||
.win1 => self.win.in.w1_bld.read(),
|
|
||||||
.out => self.win.out.out_bld.read(),
|
|
||||||
};
|
|
||||||
} else true;
|
|
||||||
|
|
||||||
if (win_part and is_blend_enabled) {
|
|
||||||
self.scanline.btm()[i] = bgr555;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the current layer is marked as Layer B, we want to continue if there's an available space on that buffer
|
||||||
|
const btm_layer = self.bld.cnt.layer_b.read();
|
||||||
|
const is_btm_layer = (btm_layer >> layer) & 1 == 1;
|
||||||
|
|
||||||
|
if (is_btm_layer) {
|
||||||
|
self.scanline.btm()[i] = Scanline.Pixel.from(bgr555);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scanline.top()[i] = bgr555;
|
// The code we're about to fall-through to assumes that alpha blending takes place. In order to withold all invariants
|
||||||
|
// we need to discard anything that might be in the bottom buffer.
|
||||||
|
self.scanline.btm()[i] = .hidden;
|
||||||
|
},
|
||||||
|
0b10, 0b11 => {
|
||||||
|
// BLD_WHITE, BLD_BLACK
|
||||||
|
// Weare to blend with White or black here. By convention we store regular ol' pixels in the top layer, which means that if we want to
|
||||||
|
// treat some pixels (in this case the ones relegated to blending) we need to keep them separate as we can't apply the blending to the top layer.
|
||||||
|
|
||||||
|
// While in these modes, (and since this is a scanline renderer), the bottom layer will be completely unused. While it's a bit unintuitive, since we'll
|
||||||
|
// be moving layer A pixels there, we will repurpose the bottom layer as the "to blend", layer
|
||||||
|
|
||||||
|
// If the current layer is makred as Layer A, write to top buffer
|
||||||
|
const top_layer = self.bld.cnt.layer_a.read();
|
||||||
|
const is_top_layer = (top_layer >> layer) & 1 == 1;
|
||||||
|
|
||||||
|
if (is_top_layer) {
|
||||||
|
self.scanline.btm()[i] = Scanline.Pixel.from(bgr555); // this is intentional
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we aren't blending here at all, just add the pixel to the top layer
|
||||||
|
self.scanline.top()[i] = Scanline.Pixel.from(bgr555);
|
||||||
}
|
}
|
||||||
|
|
||||||
const WindowBounds = enum { win0, win1, out };
|
const WindowBounds = enum { win0, win1, out };
|
||||||
|
@ -763,48 +805,79 @@ pub const Ppu = struct {
|
||||||
return .out;
|
return .out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shouldDrawBackground(self: *Self, comptime n: u2, bounds: ?WindowBounds, i: usize) bool {
|
fn shouldDrawBackground(self: *Self, comptime layer: u2, bounds: ?WindowBounds, i: usize) bool {
|
||||||
// If a pixel has been drawn on the top layer, it's because:
|
// First and foremost, we should recognize that there are two scanline buffers, named Top and Bottom.
|
||||||
// 1. The pixel is to be blended with a pixel on the bottom layer
|
|
||||||
// 2. The pixel is not to be blended at all
|
switch (self.bld.cnt.mode.read()) {
|
||||||
// Also, if we find a pixel on the top layer we don't need to bother with this I think?
|
0b00 => if (self.scanline.top()[i] == .set) return false, // Exit early if we've already drawn pixel here
|
||||||
if (self.scanline.top()[i] != null) return false;
|
0b01 => {
|
||||||
|
// BLD_ALPHA
|
||||||
|
const top_layers = self.bld.cnt.layer_a.read();
|
||||||
|
const is_top_layer = (top_layers >> layer) & 1 == 1;
|
||||||
|
|
||||||
|
if (is_top_layer and self.scanline.top()[i] == .set) return false;
|
||||||
|
|
||||||
|
// If the current layer is marked as Layer B, we want to continue if there's an available space on that buffer
|
||||||
|
const btm_layer = self.bld.cnt.layer_b.read();
|
||||||
|
const is_btm_layer = (btm_layer >> layer) & 1 == 1;
|
||||||
|
|
||||||
|
if (is_btm_layer) {
|
||||||
|
if (self.scanline.btm()[i] == .set) return false;
|
||||||
|
|
||||||
|
// In some previous iteration we have determined that an opaque pixel was drawn at this position
|
||||||
|
// therefore there's no reason to draw anything here
|
||||||
|
if (self.scanline.btm()[i] == .hidden) return false;
|
||||||
|
|
||||||
|
// We have a pixel and we know it to be a part of hte bottom layer.
|
||||||
|
// when getBgr555 sees that thre's a pixel in the top and bottom layer it chooses to blend the two
|
||||||
|
// Meaning that if we want to prevent Alpha Blending from happening (like for example if a window is preventing it)
|
||||||
|
// we need to make that happen now.
|
||||||
|
|
||||||
|
// We can do this by not drawing the bottom pixel, since with alpha blending disabled it wouldn't be visible anyways
|
||||||
|
|
||||||
if (bounds) |win| {
|
if (bounds) |win| {
|
||||||
switch (win) {
|
switch (win) {
|
||||||
.win0 => if ((self.win.in.w0_bg.read() >> n) & 1 == 0) return false,
|
.win0 => if (!self.win.in.w0_bld.read()) return false,
|
||||||
.win1 => if ((self.win.in.w1_bg.read() >> n) & 1 == 0) return false,
|
.win1 => if (!self.win.in.w1_bld.read()) return false,
|
||||||
.out => if ((self.win.out.out_bg.read() >> n) & 1 == 0) return false,
|
.out => if (!self.win.out.out_bld.read()) return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
0b10, 0b11 => {
|
||||||
|
// BLD_WHITE and BLD_BLACK
|
||||||
|
|
||||||
|
// We should treat the top and bottom layers the same in this mode. That means that we should exit early if a pixel has been drawn
|
||||||
|
// to either the top or bottom layers
|
||||||
|
|
||||||
|
if (self.scanline.top()[i] == .set) return false; // Check the regular pixels
|
||||||
|
if (self.scanline.btm()[i] == .set) return false; // Check the pixels that will later be blended WHITE or BLACK
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we will have exited early if we determined that we'd be overwriting a pixel
|
||||||
|
// with a higher priority. We can now move own to determining whether the pixel is visible or not
|
||||||
|
|
||||||
|
// The first thing that may or may not affect visibility is windowing. We should check to see if ths pixel is in bounds
|
||||||
|
// of of the background Window if it is enabled
|
||||||
|
// TODO: Do Window Bounds checking here instead of outside this function?
|
||||||
|
|
||||||
|
if (bounds) |window| {
|
||||||
|
// If this parameter is non-null, we know that:
|
||||||
|
// 1. Win0, Win1 or WinObj are enabled
|
||||||
|
// 2. This specific pixel exists within the range of a window
|
||||||
|
|
||||||
|
// Here, we check to see if the Window for this background is enabled. If not, we won't render the pixel
|
||||||
|
// FIXME: We perform needless computations on Window Bounds by checking for enable here after we've already computed this information
|
||||||
|
switch (window) {
|
||||||
|
.win0 => if ((self.win.in.w0_bg.read() >> layer) & 1 == 0) return false,
|
||||||
|
.win1 => if ((self.win.in.w1_bg.read() >> layer) & 1 == 0) return false,
|
||||||
|
.out => if ((self.win.out.out_bg.read() >> layer) & 1 == 0) return false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.scanline.btm()[i] != null) {
|
// Otherwise, return true
|
||||||
// The pixel found in the bottom layer is:
|
|
||||||
// 1. From a higher priority background
|
|
||||||
// 2. From a background that is marked for blending (Pixel A)
|
|
||||||
|
|
||||||
// If Alpha Blending isn't enabled, then we've already found a higher prio
|
|
||||||
// pixel, we can return early
|
|
||||||
if (self.bld.cnt.mode.read() != 0b01) return false;
|
|
||||||
|
|
||||||
const b_layers = self.bld.cnt.layer_b.read();
|
|
||||||
|
|
||||||
const win_part = if (bounds) |win| blk: {
|
|
||||||
// Window Enabled
|
|
||||||
break :blk switch (win) {
|
|
||||||
.win0 => self.win.in.w0_bld.read(),
|
|
||||||
.win1 => self.win.in.w1_bld.read(),
|
|
||||||
.out => self.win.out.out_bld.read(),
|
|
||||||
};
|
|
||||||
} else true;
|
|
||||||
|
|
||||||
// If the Background is not marked for blending, we've already found
|
|
||||||
// a higher priority pixel, move on.
|
|
||||||
|
|
||||||
const is_blend_enabled = win_part and ((b_layers >> n) & 1 == 1);
|
|
||||||
if (!is_blend_enabled) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1287,56 +1360,98 @@ fn alphaBlend(top: u16, btm: u16, bldalpha: io.BldAlpha) u16 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shouldDrawSprite(bldcnt: io.BldCnt, scanline: *Scanline, x: u9) bool {
|
fn shouldDrawSprite(bldcnt: io.BldCnt, scanline: *Scanline, x: u9) bool {
|
||||||
if (scanline.top()[x] != null) return false;
|
if (bldcnt.mode.read() == 0b01) {
|
||||||
|
const top_layers = bldcnt.layer_a.read();
|
||||||
|
const is_top_layer = (top_layers >> 4) & 1 == 1;
|
||||||
|
|
||||||
if (scanline.btm()[x] != null) {
|
if (is_top_layer and scanline.top()[x] == .set) return false;
|
||||||
if (bldcnt.mode.read() != 0b01) return false;
|
|
||||||
|
|
||||||
const b_layers = bldcnt.layer_b.read();
|
const btm_layers = bldcnt.layer_b.read();
|
||||||
const is_blend_enabled = (b_layers >> 4) & 1 == 1;
|
const is_btm_layer = (btm_layers >> 4) & 1 == 1;
|
||||||
if (!is_blend_enabled) return false;
|
|
||||||
|
if (is_btm_layer and scanline.btm()[x] == .set) return false;
|
||||||
|
} else {
|
||||||
|
if (scanline.top()[x] == .set) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copyToSpriteBuffer(bldcnt: io.BldCnt, scanline: *Scanline, x: u9, bgr555: u16) void {
|
fn drawSpritePixel(bldcnt: io.BldCnt, scanline: *Scanline, x: u9, bgr555: u16) void {
|
||||||
if (bldcnt.mode.read() != 0b00) {
|
switch (bldcnt.mode.read()) {
|
||||||
// Alpha Blending
|
0b00 => {}, // pass through
|
||||||
const a_layers = bldcnt.layer_a.read();
|
0b01 => {
|
||||||
const is_blend_enabled = (a_layers >> 4) & 1 == 1;
|
// BLD_ALPHA
|
||||||
|
const top_layers = bldcnt.layer_a.read();
|
||||||
|
const is_top_layer = (top_layers >> 4) & 1 == 1;
|
||||||
|
|
||||||
if (is_blend_enabled) {
|
if (is_top_layer) {
|
||||||
scanline.btm()[x] = bgr555;
|
scanline.top()[x] = Scanline.Pixel.from(bgr555);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const btm_layers = bldcnt.layer_b.read();
|
||||||
|
const is_btm_layer = (btm_layers >> 4) & 1 == 1;
|
||||||
|
|
||||||
|
if (is_btm_layer) {
|
||||||
|
scanline.btm()[x] = Scanline.Pixel.from(bgr555);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scanline.top()[x] = bgr555;
|
// We're rendering a normal pixel that isn't alpha blended
|
||||||
|
// we can mark the pixel on the bottom layer as hidden
|
||||||
|
scanline.btm()[x] = .hidden;
|
||||||
|
},
|
||||||
|
|
||||||
|
0b10, 0b11 => {
|
||||||
|
// This is explained in drawBackgroundPixel, we're reusing the bottom layer to draw layer A pixels we will want to
|
||||||
|
// later blend with WHITE or BLACK
|
||||||
|
|
||||||
|
const top_layers = bldcnt.layer_a.read();
|
||||||
|
const is_top_layer = (top_layers >> 4) & 1 == 1;
|
||||||
|
|
||||||
|
if (is_top_layer) {
|
||||||
|
scanline.btm()[x] = Scanline.Pixel.from(bgr555); // This is intentional
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
scanline.top()[x] = Scanline.Pixel.from(bgr555);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Scanline = struct {
|
const Scanline = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
layers: [2][]?u16,
|
const Pixel = union(enum) {
|
||||||
buf: []?u16,
|
set: u16,
|
||||||
|
unset: void,
|
||||||
|
hidden: void,
|
||||||
|
|
||||||
|
fn from(bgr555: u16) Pixel {
|
||||||
|
return .{ .set = bgr555 };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
layers: [2][]Pixel,
|
||||||
|
buf: []Pixel,
|
||||||
|
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
fn init(allocator: Allocator) !Self {
|
fn init(allocator: Allocator) !Self {
|
||||||
const buf = try allocator.alloc(?u16, width * 2); // Top & Bottom Scanline
|
const buf = try allocator.alloc(Pixel, width * 2); // Top & Bottom Scanline
|
||||||
std.mem.set(?u16, buf, null);
|
std.mem.set(Pixel, buf, .unset);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
// Top & Bototm Layers
|
// Top & Bototm Layers
|
||||||
.layers = [_][]?u16{ buf[0..][0..width], buf[width..][0..width] },
|
.layers = [_][]Pixel{ buf[0..][0..width], buf[width..][0..width] },
|
||||||
.buf = buf,
|
.buf = buf,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(self: *Self) void {
|
fn reset(self: *Self) void {
|
||||||
std.mem.set(?u16, self.buf, null);
|
std.mem.set(Pixel, self.buf, .unset);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
fn deinit(self: *Self) void {
|
||||||
|
@ -1344,11 +1459,11 @@ const Scanline = struct {
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn top(self: *Self) []?u16 {
|
fn top(self: *Self) []Pixel {
|
||||||
return self.layers[0];
|
return self.layers[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn btm(self: *Self) []?u16 {
|
fn btm(self: *Self) []Pixel {
|
||||||
return self.layers[1];
|
return self.layers[1];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue