Skip to content

Commit

Permalink
Add transparent index and optimize font rendering with shadows.
Browse files Browse the repository at this point in the history
  • Loading branch information
nopjne committed Nov 7, 2024
1 parent dea58c0 commit 6c3261e
Show file tree
Hide file tree
Showing 24 changed files with 183 additions and 56 deletions.
1 change: 1 addition & 0 deletions src/formats/bk.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "formats/internal/writer.h"
#include "formats/palette.h"
#include "formats/pcx.h"
#include "formats/transparent.h"
#include "formats/vga_image.h"
#include "utils/allocator.h"

Expand Down
7 changes: 5 additions & 2 deletions src/formats/fonts.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "formats/fonts.h"
#include "formats/internal/reader.h"
#include "formats/internal/writer.h"
#include <assert.h>

int sd_font_create(sd_font *font) {
if(font == NULL) {
Expand Down Expand Up @@ -57,18 +58,20 @@ int sd_font_save(const sd_font *font, const char *file) {
return SD_SUCCESS;
}

int sd_font_decode(const sd_font *font, sd_vga_image *o, uint8_t ch, uint8_t color) {
int sd_font_decode(const sd_font *font, sd_vga_image *o, uint8_t ch, uint8_t color, int transparent) {
if(font == NULL || o == NULL || ch >= 224) {
return SD_INVALID_INPUT;
}

assert((transparent >= 0) && (transparent < 256));

int t = 0;
for(unsigned i = 0; i < font->h; i++) {
for(int k = font->h - 1; k >= 0; k--) {
if(font->chars[ch].data[i] & (1 << k)) {
o->data[t] = color;
} else {
o->data[t] = 0;
o->data[t] = (char)transparent;
}
t++;
}
Expand Down
2 changes: 1 addition & 1 deletion src/formats/fonts.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ int sd_font_save(const sd_font *font, const char *filename);
* \param ch Character to load. Must be 0 <= ch <= 224.
* \param color Color palette index (0 - 0xFF)
*/
int sd_font_decode(const sd_font *font, sd_vga_image *surface, uint8_t ch, uint8_t color);
int sd_font_decode(const sd_font *font, sd_vga_image *surface, uint8_t ch, uint8_t color, int transparent);

/**
* Same as sd_font_decode, but decodes to an RGBA surface.
Expand Down
10 changes: 8 additions & 2 deletions src/formats/pcx.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "formats/pcx.h"
#include "formats/error.h"
#include "formats/internal/reader.h"
#include "formats/transparent.h"
#include "utils/allocator.h"
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
Expand Down Expand Up @@ -73,6 +75,7 @@ int pcx_load(pcx_file *pcx, const char *filename) {
return ret;
}

pcx->image.transparent = FONT_TRANSPARENT_INDEX;
for(unsigned j = 0; j < 200; ++j) {
for(unsigned i = 0; i < 320;) {
i += decode_next_bytes(&(pcx->image.data)[(320 * j) + i], reader);
Expand Down Expand Up @@ -141,19 +144,22 @@ int pcx_load_font(pcx_font *font, const char *filename) {
return SD_SUCCESS;
}

int pcx_font_decode(const pcx_font *font, sd_vga_image *o, uint8_t ch, int8_t palette_offset) {
int pcx_font_decode(const pcx_font *font, sd_vga_image *o, uint8_t ch, int8_t palette_offset, int transparent) {
if(ch >= font->glyph_count || font == NULL || o == NULL) {
return SD_INVALID_INPUT;
}

// Fonts always need to have a transparent value.
assert((transparent >= 0) && (transparent < 256));

int k = 0;
for(int i = font->glyphs[ch].y; i < font->glyphs[ch].y + font->glyph_height; i++) {
int l = 0;
for(int j = font->glyphs[ch].x; j < font->glyphs[ch].x + font->glyphs[ch].width; j++) {
if(font->pcx.image.data[(i * 320) + j]) {
o->data[(k * font->glyphs[ch].width) + l] = palette_offset + (int)font->pcx.image.data[(i * 320) + j];
} else {
o->data[(k * font->glyphs[ch].width) + l] = 0;
o->data[(k * font->glyphs[ch].width) + l] = transparent;
}

l++;
Expand Down
2 changes: 1 addition & 1 deletion src/formats/pcx.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ typedef struct {

int pcx_load(pcx_file *pcx, const char *filename);
int pcx_load_font(pcx_font *font, const char *filename);
int pcx_font_decode(const pcx_font *font, sd_vga_image *o, uint8_t ch, int8_t palette_offset);
int pcx_font_decode(const pcx_font *font, sd_vga_image *o, uint8_t ch, int8_t palette_offset, int transparent);
void pcx_free(pcx_file *pcx);
void pcx_font_free(pcx_font *font);

Expand Down
7 changes: 7 additions & 0 deletions src/formats/sprite.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "formats/error.h"
#include "formats/palette.h"
#include "formats/sprite.h"
#include "formats/transparent.h"
#include "utils/allocator.h"

int sd_sprite_create(sd_sprite *sprite) {
Expand Down Expand Up @@ -271,6 +272,7 @@ int sd_sprite_vga_decode(sd_vga_image *dst, const sd_sprite *src) {
uint16_t c = 0;
uint16_t data = 0;
char op = 0;
int transparent = SPRITE_TRANSPARENT_INDEX;

// Make sure we aren't being fed BS
if(dst == NULL || src == NULL) {
Expand All @@ -284,6 +286,8 @@ int sd_sprite_vga_decode(sd_vga_image *dst, const sd_sprite *src) {
sd_vga_image_create(dst, 1, 1);
}

dst->transparent = SPRITE_TRANSPARENT_INDEX;

// XXX CREDITS.BK has a bunch of 0 width sprites, for some unknown reason
if(src->width == 0 || src->height == 0 || src->len == 0) {
return SD_SUCCESS;
Expand All @@ -310,6 +314,7 @@ int sd_sprite_vga_decode(sd_vga_image *dst, const sd_sprite *src) {
uint8_t b = src->data[i];
int pos = ((y * src->width) + x);
dst->data[pos] = b;
assert(b != transparent);
i++; // we read 1 byte
x++;
data--;
Expand All @@ -324,6 +329,8 @@ int sd_sprite_vga_decode(sd_vga_image *dst, const sd_sprite *src) {
}
}

dst->transparent = transparent;

// All done. dst should now contain a valid vga image.
return SD_SUCCESS;
}
Expand Down
44 changes: 44 additions & 0 deletions src/formats/transparent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*! \file
* \brief Transparent VGA pixel values
* \details Describes the transparancy index value for various engine components.
* \copyright MIT license.
* \date 2024
* \author Andrew Thompson
* \author Tuomas Virtanen
*/

#ifndef TRANSPARENT_H
#define TRANSPARENT_H

// Default's.
// Please do not use the default definitions in the code directly. Define a new value below instead.
// This way all different transparency values are stored in one place.
#define DEFAULT_TRANSPARENT_INDEX 0
#define DEFAULT_NOT_TRANSPARENT -1

// All sprites have holes, these holes need to be transparent.
#define SPRITE_TRANSPARENT_INDEX DEFAULT_TRANSPARENT_INDEX

// Backgrounds have no transparent pixels and need all pixels to be rendered.
#define BACKGROUND_TRANSPARENT_INDEX DEFAULT_NOT_TRANSPARENT

// Force an opaque menu for now.
#define MENU_TRANSPARENT_INDEX DEFAULT_NOT_TRANSPARENT

// Portraits have a transparancy index that needs to offset past other items in the global palette.
// TODO: Should really become default.
#define PORTRAIT_TRANSPARENT_INDEX 0xD0

// Portrait highlights need a special offset for transparency to not conflict with other entries in the global palette.
// TODO: Should really become default.
#define HIGHLIGHT_TRANSPARENT_INDEX 90

// Fonts need a transparent index that does not conflict with the lower 8 colors on the N64 otherwise there will be a
// combinatorial explosion.
#define FONT_TRANSPARENT_INDEX DEFAULT_TRANSPARENT_INDEX
#define PCX_FONT_TRANSPARENT_INDEX DEFAULT_TRANSPARENT_INDEX

// Gauges in the player select have an opaque background and are not transparent.
#define GAUGE_TRANSPARENT_INDEX DEFAULT_NOT_TRANSPARENT

#endif
29 changes: 29 additions & 0 deletions src/formats/vga_image.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
#if !defined(N64_BUILD) && !defined(MIN_BUILD)
#include <png.h>
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef N64_BUILD
#include <libdragon.h>
#else
#include <assert.h>
#endif

#include "formats/error.h"
#include "formats/transparent.h"
#include "formats/vga_image.h"
#include "utils/allocator.h"
#include "utils/png_writer.h"
Expand All @@ -16,7 +24,15 @@ int sd_vga_image_create(sd_vga_image *img, unsigned int w, unsigned int h) {
img->w = w;
img->h = h;
img->len = w * h;
img->transparent = BACKGROUND_TRANSPARENT_INDEX;
#ifdef N64_BUILD
assertf(w <= 320 && h <= 240, "w:%X h:%X", w, h);
img->data = malloc_uncached_aligned(64, w * h);
assert(img->data != NULL);
memset(img->data, 0, w * h);
#else
img->data = omf_calloc(1, w * h);
#endif
return SD_SUCCESS;
}

Expand All @@ -28,6 +44,7 @@ int sd_vga_image_copy(sd_vga_image *dst, const sd_vga_image *src) {
dst->h = src->h;
dst->len = src->len;
dst->data = omf_calloc(src->len, 1);
dst->transparent = src->transparent;
memcpy(dst->data, src->data, src->len);
return SD_SUCCESS;
}
Expand All @@ -36,7 +53,11 @@ void sd_vga_image_free(sd_vga_image *img) {
if(img == NULL) {
return;
}
#ifdef N64_BUILD
free_uncached(img->data);
#else
omf_free(img->data);
#endif
}

int sd_vga_image_decode(sd_rgba_image *dst, const sd_vga_image *src, const vga_palette *pal) {
Expand All @@ -62,6 +83,7 @@ int sd_vga_image_decode(sd_rgba_image *dst, const sd_vga_image *src, const vga_p
}

int sd_vga_image_from_png(sd_vga_image *img, const char *filename) {
#if !defined(N64_BUILD) && !defined(MIN_BUILD)
png_structp png_ptr;
png_infop info_ptr;
int ret = SD_SUCCESS;
Expand Down Expand Up @@ -159,14 +181,21 @@ int sd_vga_image_from_png(sd_vga_image *img, const char *filename) {
fclose(handle);
error_0:
return ret;
#else
return 0;
#endif
}

int sd_vga_image_to_png(const sd_vga_image *img, const vga_palette *pal, const char *filename) {
#if !defined(N64_BUILD) && !defined(MIN_BUILD)
if(img == NULL || filename == NULL) {
return SD_INVALID_INPUT;
}
if(!png_write_paletted(filename, img->w, img->h, pal, (unsigned char *)img->data)) {
return SD_FILE_OPEN_ERROR;
}
return SD_SUCCESS;
#else
return 0;
#endif
}
1 change: 1 addition & 0 deletions src/formats/vga_image.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ typedef struct {
unsigned int h; ///< Pixel height
unsigned int len; ///< Byte length
char *data; ///< Palette representation of image data
int transparent;
} sd_vga_image;

/*! \brief Initialize VGA image structure
Expand Down
3 changes: 2 additions & 1 deletion src/game/gui/gauge.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "game/gui/gauge.h"
#include "formats/transparent.h"
#include "game/gui/widget.h"
#include "utils/allocator.h"
#include "utils/miscmath.h"
Expand Down Expand Up @@ -74,7 +75,7 @@ typedef struct {
} gauge;

static void surface_from_pix_img(surface *sur, const pixel_image *pix) {
surface_create_from_data(sur, pix->width, pix->height, pix->data);
surface_create_from_data(sur, pix->width, pix->height, pix->data, GAUGE_TRANSPARENT_INDEX);
}

static void gauge_render(component *c) {
Expand Down
14 changes: 4 additions & 10 deletions src/game/gui/menu_background.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "game/gui/menu_background.h"
#include "formats/transparent.h"
#include "video/image.h"

#define COLOR_MENU_LINE 252
Expand All @@ -8,11 +9,6 @@
#define COLOR_MENU_BORDER2 162
#define COLOR_MENU_BG 0

void menu_transparent_bg_create(surface *s, int w, int h) {
surface_create(s, w, h);
surface_set_transparency(s, -1);
}

void menu_background_create(surface *s, int w, int h) {
image img;
image_create(&img, w, h);
Expand All @@ -24,8 +20,7 @@ void menu_background_create(surface *s, int w, int h) {
image_line(&img, 0, y, w - 1, y, COLOR_MENU_LINE);
}
image_rect(&img, 0, 0, w - 1, h - 1, COLOR_MENU_BORDER);
surface_create_from_image(s, &img);
surface_set_transparency(s, COLOR_MENU_BG);
surface_create_from_image(s, &img, MENU_TRANSPARENT_INDEX);
image_free(&img);
}

Expand All @@ -42,8 +37,7 @@ void menu_background2_create(surface *s, int w, int h) {
}
image_rect(&img, 1, 1, w - 2, h - 2, COLOR_MENU_BORDER2);
image_rect(&img, 0, 0, w - 2, h - 2, COLOR_MENU_BORDER1);
surface_create_from_image(s, &img);
surface_set_transparency(s, COLOR_MENU_BG);
surface_create_from_image(s, &img, MENU_TRANSPARENT_INDEX);
image_free(&img);
}

Expand All @@ -53,6 +47,6 @@ void menu_background_border_create(surface *s, int w, int h) {
image_create(&img, w, h);
image_clear(&img, 0);
image_rect(&img, 0, 0, w - 1, h - 1, COLOR_MENU_BORDER);
surface_create_from_image(s, &img);
surface_create_from_image(s, &img, MENU_TRANSPARENT_INDEX);
image_free(&img);
}
7 changes: 4 additions & 3 deletions src/game/gui/progressbar.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <stdlib.h>

#include "formats/transparent.h"
#include "game/gui/progressbar.h"
#include "game/gui/widget.h"
#include "utils/allocator.h"
Expand Down Expand Up @@ -95,7 +96,7 @@ static void progressbar_render(component *c) {
if(bar->block == NULL) {
bar->block = omf_calloc(1, sizeof(surface));
}
surface_create_from_image(bar->block, &tmp);
surface_create_from_image(bar->block, &tmp, MENU_TRANSPARENT_INDEX);
image_free(&tmp);
} else {
omf_free(bar->block);
Expand Down Expand Up @@ -153,14 +154,14 @@ static void progressbar_layout(component *c, int x, int y, int w, int h) {
image_clear(&tmp, bar->theme.bg_color);
image_rect_bevel(&tmp, 0, 0, w - 1, h - 1, bar->theme.border_topleft_color, bar->theme.border_bottomright_color,
bar->theme.border_bottomright_color, bar->theme.border_topleft_color);
surface_create_from_image(bar->background, &tmp);
surface_create_from_image(bar->background, &tmp, MENU_TRANSPARENT_INDEX);
image_free(&tmp);

image_create(&tmp, w, h);
image_clear(&tmp, bar->theme.bg_color_alt);
image_rect_bevel(&tmp, 0, 0, w - 1, h - 1, bar->theme.border_topleft_color, bar->theme.border_bottomright_color,
bar->theme.border_bottomright_color, bar->theme.border_topleft_color);
surface_create_from_image(bar->background_alt, &tmp);
surface_create_from_image(bar->background_alt, &tmp, MENU_TRANSPARENT_INDEX);
image_free(&tmp);
}

Expand Down
Loading

0 comments on commit 6c3261e

Please sign in to comment.