gterm+bmp+image: Improve performance on TCG
diff --git a/stage23/lib/bmp.c b/stage23/lib/bmp.c
index fb90149a..a299caac 100644
--- a/stage23/lib/bmp.c
+++ b/stage23/lib/bmp.c
@@ -29,44 +29,9 @@ struct bmp_header {
} __attribute__((packed));
struct bmp_local {
- uint8_t *image;
- uint32_t pitch;
struct bmp_header header;
};
-static uint32_t get_pixel(struct image *this, int x, int y) {
- struct bmp_local *local = this->local;
- struct bmp_header *header = &local->header;
-
- switch (this->type) {
- case IMAGE_TILED: {
- x %= header->bi_width;
- y %= header->bi_height;
- break;
- }
- case IMAGE_CENTERED: {
- x -= this->x_displacement;
- y -= this->y_displacement;
- if (x < 0 || y < 0 || x >= this->x_size || y >= this->y_size)
- return this->back_colour;
- break;
- }
- case IMAGE_STRETCHED: {
- x = (x * this->old_x_size) / this->x_size;
- y = (y * this->old_y_size) / this->y_size;
- break;
- }
- }
-
- size_t pixel_offset = local->pitch * (header->bi_height - y - 1) + x * (header->bi_bpp / 8);
-
- // TODO: Perhaps use masks here, they're there for a reason
- uint32_t composite = 0;
- for (int i = 0; i < header->bi_bpp / 8; i++)
- composite |= (uint32_t)local->image[pixel_offset + i] << (i * 8);
-
- return composite;
-}
int bmp_open_image(struct image *image, struct file_handle *file) {
struct bmp_header header;
@@ -75,22 +40,22 @@ int bmp_open_image(struct image *image, struct file_handle *file) {
if (memcmp(&header.bf_signature, "BM", 2) != 0)
return -1;
- // We don't support bpp lower than 8
- if (header.bi_bpp < 8)
+ if ((header.bi_bpp < 8) | ((header.bi_bpp % 8) != 0))
return -1;
struct bmp_local *local = ext_mem_alloc(sizeof(struct bmp_local));
- local->image = ext_mem_alloc(header.bf_size);
- fread(file, local->image, header.bf_offset, header.bf_size);
+ image->img = ext_mem_alloc(header.bf_size);
+ fread(file, image->img, header.bf_offset, header.bf_size);
- local->pitch = ALIGN_UP(header.bi_width * header.bi_bpp, 32) / 8;
local->header = header;
- image->x_size = header.bi_width;
- image->y_size = header.bi_height;
- image->get_pixel = get_pixel;
- image->local = local;
-
+ image->x_size = header.bi_width;
+ image->y_size = header.bi_height;
+ image->pitch = ALIGN_UP(header.bi_width * header.bi_bpp, 32) / 8;
+ image->local = local;
+ image->bpp = header.bi_bpp;
+ image->img_width = header.bi_width;
+ image->img_height = header.bi_height;
return 0;
}
diff --git a/stage23/lib/gterm.c b/stage23/lib/gterm.c
index 2fe93470..9c1e7655 100644
--- a/stage23/lib/gterm.c
+++ b/stage23/lib/gterm.c
@@ -74,17 +74,7 @@ void gterm_plot_px(int x, int y, uint32_t hex) {
gterm_framebuffer[fb_i] = hex;
}
-static uint32_t blend_gradient_from_box(int x, int y, uint32_t hex) {
- if (x >= frame_width && x < frame_width + VGA_FONT_WIDTH * cols
- && y >= frame_height && y < frame_height + VGA_FONT_HEIGHT * rows) {
- return colour_blend(hex, background->get_pixel(background, x, y));
- }
-
- uint32_t bg_px = background->get_pixel(background, x, y);
-
- if (margin_gradient == 0)
- return bg_px;
-
+static uint32_t blend_gradient_from_box(int x, int y, uint32_t bg_px, uint32_t hex) {
int distance, x_distance, y_distance;
if (x < frame_width)
@@ -115,14 +105,78 @@ static uint32_t blend_gradient_from_box(int x, int y, uint32_t hex) {
return colour_blend((hex & 0xffffff) | (new_alpha << 24), bg_px);
}
+// Draw rect at coordinates, copying from the image to the fb and canvas, applying fn on every pixel
+#define genloop(xstart, xend, ystart, yend, fn) \
+ switch (background->type) { \
+ case IMAGE_TILED: \
+ for (int y = (ystart); y < (yend); y++) { \
+ int yb = y % img_height, xb = (xstart) % img_width; \
+ const size_t off = img_pitch * (img_height - 1 - yb), coff = gterm_width * y, goff = gterm_pitch / 4 * y; \
+ for (int x = (xstart); x < (xend); x++) { \
+ uint32_t i = *(uint32_t*)(img + xb * colsize + off); i = fn; /* xb = (x % img_width) */ \
+ bg_canvas[coff + x] = i; gterm_framebuffer[goff + x] = i; \
+ if (xb++ == img_width) xb = 0; \
+ } \
+ } \
+ break; \
+ case IMAGE_CENTERED: \
+ for (int y = (ystart); y < (yend); y++) { \
+ int yb = y - background->y_displacement; \
+ const size_t off = img_pitch * (img_height - 1 - yb), coff = gterm_width * y, goff = gterm_pitch / 4 * y; \
+ if ((yb < 0) || (yb > background->y_size)) { /* external part */ \
+ for (int x = (xstart); x < (xend); x++) { \
+ uint32_t i = background->back_colour; i = fn; \
+ bg_canvas[coff + x] = i; gterm_framebuffer[goff + x] = i; \
+ } \
+ } \
+ else { /* internal part */ \
+ for (int x = (xstart); x < (xend); x++) { \
+ int xb = (x - background->x_displacement); \
+ uint32_t i = ((xb < 0) || (xb > background->x_size)) ? background->back_colour : *(uint32_t*)(img + xb * colsize + off); i = fn; \
+ bg_canvas[coff + x] = i; gterm_framebuffer[goff + x] = i; \
+ } \
+ } \
+ } \
+ break; \
+ case IMAGE_STRETCHED: \
+ for (int y = (ystart); y < (yend); y++) { \
+ int counter = x16_x_delta * (xstart); \
+ const size_t imgy = (y * img_height) / gterm_height, off = img_pitch * (img_height - 1 - imgy), coff = gterm_width * y, goff = gterm_pitch / 4 * y; \
+ for (int x = (xstart); x < (xend); x++) { \
+ uint32_t i = *(uint32_t*)(img + (counter / 16) * colsize + off); i = fn; /* counter/16 = (x * img_width) / gterm_width */ \
+ bg_canvas[coff + x] = i; gterm_framebuffer[goff + x] = i; \
+ counter += x16_x_delta; \
+ } \
+ } \
+ break; \
+ }
+
void gterm_generate_canvas(void) {
if (background) {
- for (int y = 0; y < gterm_height; y++) {
- for (int x = 0; x < gterm_width; x++) {
- bg_canvas[y * gterm_width + x] = blend_gradient_from_box(x, y, ansi_colours[8]);
- gterm_plot_px(x, y, bg_canvas[y * gterm_width + x]);
- }
+ // Instead of executing blend_gradient_from_box for every pixel in the fb, just run it for the margin
+ uint8_t *img = background->img;
+ const int img_width = background->img_width, x16_x_delta = (img_width * 16) / gterm_width,
+ img_height = background->img_height, img_pitch = background->pitch, colsize = background->bpp / 8;
+ const int frame_height_end = frame_height + VGA_FONT_HEIGHT * rows, frame_width_end = frame_width + VGA_FONT_WIDTH * cols;
+ const int fheight = frame_height - margin_gradient, fheight_end = frame_height_end + margin_gradient,
+ fwidth = frame_width - margin_gradient, fwidth_end = frame_width_end + margin_gradient;
+
+ // Draw the part of the image outside the margin
+ genloop(0, gterm_width, 0, fheight, i);
+ genloop(0, gterm_width, fheight_end, gterm_height, i);
+ genloop(0, fwidth, fheight, fheight_end, i);
+ genloop(fwidth_end, gterm_width, fheight, fheight_end, i);
+
+ // Draw margin
+ if (margin_gradient) {
+ genloop(fwidth, fwidth_end, fheight, frame_height, blend_gradient_from_box(x, y, i, ansi_colours[8]));
+ genloop(fwidth, fwidth_end, frame_height_end, fheight_end, blend_gradient_from_box(x, y, i, ansi_colours[8]));
+ genloop(fwidth, frame_width, frame_height, frame_height_end, blend_gradient_from_box(x, y, i, ansi_colours[8]));
+ genloop(frame_width_end, fwidth_end, frame_height, frame_height_end, blend_gradient_from_box(x, y, i, ansi_colours[8]));
}
+
+ // Draw inner frame
+ genloop(frame_width, frame_width_end, frame_height, frame_height_end, colour_blend(ansi_colours[8], i));
} else {
for (int y = 0; y < gterm_height; y++) {
for (int x = 0; x < gterm_width; x++) {
@@ -132,6 +186,7 @@ void gterm_generate_canvas(void) {
}
}
}
+#undef genloop
struct gterm_char {
uint32_t c;
@@ -199,10 +254,9 @@ static void plot_char_grid(struct gterm_char *c, int x, int y) {
if (!double_buffer_enabled) {
for (int i = 0; i < VGA_FONT_HEIGHT; i++) {
for (int j = 0; j < VGA_FONT_WIDTH; j++) {
- if (old_char[i * VGA_FONT_WIDTH + j] != new_char[i * VGA_FONT_WIDTH + j])
- gterm_plot_px(x * VGA_FONT_WIDTH + frame_width + j,
- y * VGA_FONT_HEIGHT + frame_height + i,
- new_char[i * VGA_FONT_WIDTH + j]);
+ gterm_plot_px(x * VGA_FONT_WIDTH + frame_width + j,
+ y * VGA_FONT_HEIGHT + frame_height + i,
+ new_char[i * VGA_FONT_WIDTH + j]);
}
}
}
@@ -254,14 +308,15 @@ static void scroll(void) {
void gterm_clear(bool move) {
clear_cursor();
+ if (!double_buffer_enabled)
+ for (int y = frame_height; y < frame_height + VGA_FONT_HEIGHT * rows; y++)
+ memcpy(gterm_framebuffer + y * (gterm_pitch / 4) + frame_width, bg_canvas + y * gterm_width + frame_width, VGA_FONT_WIDTH * cols * 4);
struct gterm_char empty;
empty.c = ' ';
empty.fg = 9;
empty.bg = 8;
- for (int i = 0; i < rows * cols; i++) {
- plot_char_grid(&empty, i % cols, i / cols);
- }
-
+ for (int i = 0; i < rows * cols; i++) grid[i] = empty;
+
if (move) {
cursor_x = 0;
cursor_y = 0;
diff --git a/stage23/lib/image.c b/stage23/lib/image.c
index fe0f08b4..a325cc70 100644
--- a/stage23/lib/image.c
+++ b/stage23/lib/image.c
@@ -17,9 +17,6 @@ void image_make_centered(struct image *image, int frame_x_size, int frame_y_size
void image_make_stretched(struct image *image, int new_x_size, int new_y_size) {
image->type = IMAGE_STRETCHED;
- image->old_x_size = image->x_size;
- image->old_y_size = image->y_size;
-
image->x_size = new_x_size;
image->y_size = new_y_size;
}
diff --git a/stage23/lib/image.h b/stage23/lib/image.h
index 7e9cbd8f..717e6138 100644
--- a/stage23/lib/image.h
+++ b/stage23/lib/image.h
@@ -9,6 +9,13 @@ struct image {
int x_size;
int y_size;
int type;
+
+
+ uint8_t *img;
+ int bpp;
+ int pitch;
+ int img_width; // x_size = scaled size, img_width = bitmap size
+ int img_height;
union {
struct {
int x_displacement;
@@ -20,7 +27,6 @@ struct image {
};
};
uint32_t back_colour;
- uint32_t (*get_pixel)(struct image *this, int x, int y);
void *local;
};
