Skip to content

Commit

Permalink
16 Bit Blit: Implement OP_OVER
Browse files Browse the repository at this point in the history
  • Loading branch information
Ghabry committed Aug 30, 2024
1 parent 59f184f commit 74fee9e
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 48 deletions.
165 changes: 117 additions & 48 deletions src/bitmap_blit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
*/

#include "bitmap_blit.h"
#include "pixel_format.h"
#include <pixman.h>

namespace {

bool adjust_rects(Bitmap const& dest, Rect& dst_rect, Bitmap const& src, Rect& src_rect, Opacity const& opacity) {
bool AdjustRects(Bitmap const& dest, Rect& dst_rect, Bitmap const& src, Rect& src_rect, Opacity const& opacity) {
if (!Rect::AdjustRectangles(src_rect, dst_rect, src.GetRect()))
return false;

Expand All @@ -30,8 +31,18 @@ bool adjust_rects(Bitmap const& dest, Rect& dst_rect, Bitmap const& src, Rect& s
return true;
}

int GetMaskValue(Opacity const& opacity) {
if (opacity.IsOpaque() || opacity.IsTransparent()) {
return -1;
}

assert(!opacity.IsSplit());

return opacity.Value();
}

} // anonymous namespace

namespace BitmapBlit {

bool Blit(Bitmap& dest, int x, int y, Bitmap const& src, Rect src_rect,
Expand All @@ -45,7 +56,7 @@ bool Blit(Bitmap& dest, int x, int y, Bitmap const& src, Rect src_rect,
return true;
}

if (dest.format != src.format) {
if (!format_A1R5G5B5_a().Match(src.format) && !format_A1R5G5B5_n().Match(src.format)) {
return false;
}

Expand All @@ -54,12 +65,21 @@ bool Blit(Bitmap& dest, int x, int y, Bitmap const& src, Rect src_rect,
return false;
}

if (opacity.IsSplit()) {
// Only used by events on bushes, not implemented here
return false;
}

Rect dst_rect = {x, y, 0, 0};

if (!adjust_rects(dest, dst_rect, src, src_rect, opacity)) {
if (!AdjustRects(dest, dst_rect, src, src_rect, opacity)) {
return true;
}

// The code only works for 16 bit

int mask = GetMaskValue(opacity);

int bpp = src.bpp();
int src_pitch = src.pitch();
int dst_pitch = dest.pitch();
Expand All @@ -68,64 +88,113 @@ bool Blit(Bitmap& dest, int x, int y, Bitmap const& src, Rect src_rect,
uint8_t* dst_pixels = (uint8_t*)dest.pixels() + dst_rect.x * bpp + dst_rect.y * dst_pitch;
uint16_t src_pixel;

int to_copy = src_rect.width * bpp;
// TODO: Currently hardcoded to this format
auto format = format_A1R5G5B5_a();

int a_mask = src.format.a.mask;
int run_beg = 0;
int run_alpha = 0;
int pix_alpha = 0;
const uint16_t amask = format.a_mask();

for (y = 0; y < src_rect.height; ++y) {
run_beg = 0;
src_pixel = *(uint16_t*)(src_pixels + y * src_pitch + 0);
run_alpha = (src_pixel & a_mask);
if (mask >= 0) {
// Alpha blending required (slow)
const uint8_t rshift = format.r_shift();
const uint8_t gshift = format.g_shift();
const uint8_t bshift = format.b_shift();
const uint8_t ashift = format.a_shift();
const uint8_t pxmask = 0b11111;

for (x = 0; x < src_rect.width * bpp; x += bpp) {
src_pixel = *(uint16_t*)(src_pixels + y * src_pitch + x);
pix_alpha = (src_pixel & a_mask);
uint16_t* dst_pixel;
uint16_t dst_pixel_val;

if (pix_alpha != run_alpha) {
if (run_alpha != 0) {
memcpy(
dst_pixels + y * dst_pitch + run_beg,
src_pixels + y * src_pitch + run_beg,
x - run_beg);
}
uint8_t rs, gs, bs; // src colors
uint8_t rd, gd, bd; // dest colors

auto format = src.format;

run_beg = x;
run_alpha = pix_alpha;
mask /= 8; // Reduce range to [0 - 32] (5 bit)

for (int y = 0; y < src_rect.height; ++y) {
for (int x = 0; x < src_rect.width * bpp; x += bpp) {
src_pixel = *(uint16_t*)(src_pixels + y * src_pitch + x);

// Transparent pixels are skipped
if ((src_pixel & amask) != 0) {
dst_pixel = (uint16_t*)(dst_pixels + y * dst_pitch + x);
dst_pixel_val = *dst_pixel;

rs = (src_pixel >> rshift) & pxmask;
gs = (src_pixel >> gshift) & pxmask;
bs = (src_pixel >> bshift) & pxmask;

rd = (dst_pixel_val >> rshift) & pxmask;
gd = (dst_pixel_val >> gshift) & pxmask;
bd = (dst_pixel_val >> bshift) & pxmask;

rd = (rs * mask + ((64 - mask) * rd)) / 64;
gd = (gs * mask + ((64 - mask) * gd)) / 64;
bd = (bs * mask + ((64 - mask) * bd)) / 64;

*dst_pixel = (rd << rshift ) | (gd << gshift) | (bd << bshift) | (1 << ashift);
}
}
}
} else {
// We only have 1 bit of alpha so a pixel can be only full transparent or opaque
// The code scans for runs of transparent or opaque pixels and then ignores them (transparent) or memcpys them (opaque)
int run_beg = 0;
int run_alpha = 0;
int pix_alpha = 0;

for (y = 0; y < src_rect.height; ++y) {
run_beg = 0;
src_pixel = *(uint16_t*)(src_pixels + y * src_pitch + 0);
run_alpha = (src_pixel & amask);

for (x = 0; x < src_rect.width * bpp; x += bpp) {
src_pixel = *(uint16_t*)(src_pixels + y * src_pitch + x);
pix_alpha = (src_pixel & amask);

if (pix_alpha != run_alpha) {
if (run_alpha != 0) {
memcpy(
dst_pixels + y * dst_pitch + run_beg,
src_pixels + y * src_pitch + run_beg,
x - run_beg);
}

run_beg = x;
run_alpha = pix_alpha;
}
}

if (run_alpha != 0) {
if (run_beg == 0) {
// Copy entire line
memcpy(
dst_pixels + y * dst_pitch,
src_pixels + y * src_pitch,
src_rect.width * bpp);
} else {
// Copy remainder
memcpy(
dst_pixels + y * dst_pitch + run_beg,
src_pixels + y * src_pitch + run_beg,
x - bpp - run_beg);
if (run_alpha != 0) {
if (run_beg == 0) {
// Copy entire line
memcpy(
dst_pixels + y * dst_pitch,
src_pixels + y * src_pitch,
src_rect.width * bpp);
} else {
// Copy remainder
memcpy(
dst_pixels + y * dst_pitch + run_beg,
src_pixels + y * src_pitch + run_beg,
x - bpp - run_beg);
}
}
}
}

/*
// Naive implementation
for (int y = 0; y < src_rect.height; ++y) {
for (int x = 0; x < src_rect.width * bpp; x += bpp) {
src_pixel = *(uint16_t*)(src_pixels + y * src_pitch + x);
/*
// Naive implementation
for (int y = 0; y < src_rect.height; ++y) {
for (int x = 0; x < src_rect.width * bpp; x += bpp) {
src_pixel = *(uint16_t*)(src_pixels + y * src_pitch + x);
if ((src_pixel & a_mask) != 0) {
*(uint16_t*)(dst_pixels + y * dst_pitch + x) = src_pixel;
if ((src_pixel & a_mask) != 0) {
*(uint16_t*)(dst_pixels + y * dst_pitch + x) = src_pixel;
}
}
}
*/
}
*/

return true;
}
Expand All @@ -137,13 +206,13 @@ bool BlitFast(Bitmap& dest, int x, int y, Bitmap const& src, Rect src_rect,
return true;
}

if (dest.format != src.format) {
if (!src.format.rgb_equal(dest.format)) {
return false;
}

Rect dst_rect = {x, y, 0, 0};

if (!adjust_rects(dest, dst_rect, src, src_rect, opacity)) {
if (!AdjustRects(dest, dst_rect, src, src_rect, opacity)) {
return true;
}

Expand Down
4 changes: 4 additions & 0 deletions src/pixel_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ class DynamicFormat {
alpha_type = _alpha_type;
}

constexpr bool rgb_equal(const DynamicFormat& f) const {
return r == f.r && g == f.g && b == f.b;
}

constexpr int code(bool shifts) const {
int x = (int) alpha_type | ((bits - 1) << 2);
if (!shifts)
Expand Down

0 comments on commit 74fee9e

Please sign in to comment.