Add hi-color conversion utility and example

This commit is contained in:
bbbbbr
2023-08-07 11:19:21 -07:00
parent a714de237e
commit 2defbebd46
46 changed files with 13105 additions and 4 deletions

View File

@@ -43,3 +43,7 @@ MIT License. @Zal0 is the original author + additional contributors
LCC License, traditionally named as the "CPYRIGHT" file
### png2hicolorgb:
Public Domain. Glen Cook and others are the original authors, @bbbbbr authored the cross platform console port

View File

@@ -151,6 +151,8 @@ endif
@$(MAKE) -C $(GBDKSUPPORTDIR)/makecom TOOLSPREFIX=$(TOOLSPREFIX) TARGETDIR=$(TARGETDIR)/ --no-print-directory
@echo Building makebin
@$(MAKE) -C $(GBDKSUPPORTDIR)/makebin TOOLSPREFIX=$(TOOLSPREFIX) TARGETDIR=$(TARGETDIR)/ --no-print-directory
@echo Building png2hicolorgb
@$(MAKE) -C $(GBDKSUPPORTDIR)/png2hicolorgb TOOLSPREFIX=$(TOOLSPREFIX) TARGETDIR=$(TARGETDIR)/ --no-print-directory
@echo
gbdk-support-install: gbdk-support-build $(BUILDDIR)/bin
@@ -184,6 +186,9 @@ gbdk-support-install: gbdk-support-build $(BUILDDIR)/bin
@echo Installing makebin
@cp $(GBDKSUPPORTDIR)/makebin/makebin$(EXEEXTENSION) $(BUILDDIR)/bin/makebin$(EXEEXTENSION)
@$(TARGETSTRIP) $(BUILDDIR)/bin/makebin$(EXEEXTENSION)
@echo Installing png2hicolorgb
@cp $(GBDKSUPPORTDIR)/png2hicolorgb/png2hicolorgb$(EXEEXTENSION) $(BUILDDIR)/bin/png2hicolorgb$(EXEEXTENSION)
@$(TARGETSTRIP) $(BUILDDIR)/bin/png2hicolorgb$(EXEEXTENSION)
@echo
gbdk-support-clean:
@@ -202,6 +207,8 @@ gbdk-support-clean:
@$(MAKE) -C $(GBDKSUPPORTDIR)/makecom clean
@echo Cleaning makebin
@$(MAKE) -C $(GBDKSUPPORTDIR)/makebin clean
@echo Cleaning png2hicolorgb
@$(MAKE) -C $(GBDKSUPPORTDIR)/png2hicolorgb clean
@echo
# Rules for gbdk-lib
@@ -468,6 +475,12 @@ ifneq (,$(wildcard $(BUILDDIR)/bin/))
echo \`\`\` >> $(TOOLCHAIN_DOCS_FILE);
$(BUILDDIR)/bin/png2asset >> $(TOOLCHAIN_DOCS_FILE) 2>&1
echo \`\`\` >> $(TOOLCHAIN_DOCS_FILE)
# png2hicolorgb
echo \@anchor png2hicolorgb-settings >> $(TOOLCHAIN_DOCS_FILE);
echo \# png2hicolorgb settings >> $(TOOLCHAIN_DOCS_FILE);
echo \`\`\` >> $(TOOLCHAIN_DOCS_FILE);
$(BUILDDIR)/bin/png2hicolorgb -h >> $(TOOLCHAIN_DOCS_FILE) 2>&1
echo \`\`\` >> $(TOOLCHAIN_DOCS_FILE)
endif

View File

@@ -1,6 +1,6 @@
gbdk-4.2.0
- Library fixes/improvements
- Added: set_bkg_attributes(), set_bkg_submap_attributes(), set_bkg_attribute_xy()
- Added set_bkg_attributes(), set_bkg_submap_attributes(), set_bkg_attribute_xy()
- Some functions renamed (old names will work for now)
- vsync(): replaces wait_vbl_done()
- set_default_palette(): replaces cgb_compatibility()
@@ -21,17 +21,19 @@ gbdk-4.2.0
- Refactored interrupts to use less space
- lcc "lcc"
- Fixed `--sdccbindir`
- utility_png2asset "png2asset"
- png2asset
- Added `-repair_index_pal`: Tries to repair tile palettes
- Added `-no_palettes`: Do not export palette data
- Fixed support for indexed color pngs with less than 8 bits color depth
- Fixed incorrect palettes when different colors have same luma value
- Added png2hicolorgb
- Added sdld6808 (for NES)
- Examples
- Wav Playback: Improved support on AGB/AGS hardware
- Large Map: Added color for supported platforms
- Added GB-Type example
- Updated documentation
- Added Game Boy Color Hi-Color example using png2hicolorgb
- Updated documentation and improved search
gbdk-4.1.1
- Library fixes/improvements

View File

@@ -308,3 +308,30 @@ Converts a binary .rom file to .msxdos com format, including splitting the banks
- For detailed settings see @ref makecom-settings
@anchor utility_png2hicolorgb
## png2hicolorgb
An updated version of Glen Cook's Windows GUI "hicolour.exe" 1.2 conversion tool for the Game Boy Color. The starting code base was the 1.2 release.
- For detailed settings see @ref png2hicolorgb-settings
"Hi Color" on the Game Boy Color is a technique for displaying backgrounds with thousands of colors instead being limtied to 32 colors for the entire screen background. It achieves this by changing ~16 colors of the background palette per scanline. The main tradeoffs are that it uses much of the Game Boy's available cpu processing per frame and requires more ROM space. The tile patterns, map, attributes and per-scanline palettes are pre-calculated using the PC based conversion tool.
Example: `png2hicolorgb myimage.png --csource -o=my_output_filename`
Example with higher quality (slower conversion): `png2hicolorgb myimage.png --csource -o=my_output_filename --type=3 -L=2 -R=2`
```
Historical credits and info:
Original Concept : Icarus Productions
Original Code : Jeff Frohwein
Full Screen Modification : Anon
Adaptive Code : Glen Cook
Windows Interface : Glen Cook
Additional Windows Programming : Rob Jones
Original Quantiser Code : Benny
Quantiser Conversion : Glen Cook
```
### Additional Details
For technical details about the conversion process and rendering, see:
https://github.com/bbbbbr/png2hicolorgb

View File

@@ -38,6 +38,7 @@ https://github.com/gbdk-2020/gbdk-2020/releases
- Workaround for possible HALT bug in Crash Handler
- Refactored interrupts to use less space
- Toolchain / Utilities
- Added @ref utility_png2hiclorgb "png2hicolorgb"
- @ref lcc "lcc"
- Fixed `--sdccbindir`
- Removed the unused `-DINT_16_BITS` from the default SDCC compiler and preprocessor arguments
@@ -59,7 +60,8 @@ https://github.com/gbdk-2020/gbdk-2020/releases
- Metasprites: Added sub-palette switching for GBC and NES, software metasprite flipping for sms/gg
- Large Map: Added color for supported platforms
- LCD ISR Wobble: Improved interrupt flag settings
- Added GB-Type example:
- Added GB-Type example
- Added Game Boy Color Hi-Color example using @ref utility_png2hiclorgb "png2hicolorgb"
- Docs:
- Improved search to do partial matches instead of matching start of string only
- Added SDAS assembler manual (asmlnk_manual.txt)

View File

@@ -598,3 +598,42 @@ usage: png2asset <file>.png [options]
-bin export to binary format
-transposed export transposed (column-by-column instead of row-by-row)
```
@anchor png2hicolorgb-settings
# png2hicolorgb settings
```
png2hicolorgb input_image.png [options]
version 1.4.1: bbbbbr. Based on Glen Cook's Windows GUI "hicolour.exe" 1.2
Convert an image to Game Boy Hi-Color format
Options
-h : Show this help
-v* : Set log level: "-v" verbose, "-vQ" quiet, "-vE" only errors, "-vD" debug
-o <file> : Set base output filename (otherwise from input image)
--csource : Export C source format with incbins for data files
--bank=N : Set bank number for C source output where N is decimal bank number 1-511
--type=N : Set conversion type where N is one of below
1: Median Cut - No Dither (*Default*)
2: Median Cut - With Dither
3: Wu Quantiser
-p : Show screen attribute pattern options (no processing)
-L=N : Set Left screen attribute pattern where N is decimal entry (-p to show patterns)
-R=N : Set Right screen attribute pattern where N is decimal entry (-p to show patterns)
--vaddrid : Map uses vram id (128->255->0->127) instead of (*Default*) sequential tile order (0->255)
--nodedupe : Turn off tile pattern deduplication
Example 1: "png2hicolorgb myimage.png"
Example 2: "png2hicolorgb myimage.png --csource -o=my_output_filename"
* Default settings provide good results. Better quality but slower: "--type=3 -L=2 -R=2"
Historical credits and info:
Original Concept : Icarus Productions
Original Code : Jeff Frohwein
Full Screen Modification : Anon
Adaptive Code : Glen Cook
Windows Interface : Glen Cook
Additional Windows Programming : Rob Jones
Original Quantiser Code : Benny
Quantiser Conversion : Glen Cook
```

View File

@@ -0,0 +1,109 @@
# If you move this project you can change the directory
# to match your GBDK root directory (ex: GBDK_HOME = "C:/GBDK/"
ifndef GBDK_HOME
GBDK_HOME = ../../../
endif
LCC = $(GBDK_HOME)bin/lcc
LCC = $(GBDK_HOME)bin/lcc
# You can set flags for LCC here
LCCFLAGS += -debug # Uncomment to enable debug output
# LCCFLAGS += -v # Uncomment for lcc verbose output
LCCFLAGS += -Wm-yC # GB Color required for Hi Color
LCCFLAGS += -Wf-MMD -Wf-Wp-MP # Header file dependency output (-MMD) for Makefile use + per-header Phoney rules (-MP)
LCCFLAGS += -Wl-yt0x19 # MBC5
LCCFLAGS += -autobank -Wb-v -Wb-ext=.rel # Auto-bank packing
# For testing only: randomize autobank assignment
# LCCFLAGS += -Wb-random -Wb-max=3 -Wb-v
# For Hi Color:
PNG2HICOLORGB = $(GBDK_HOME)bin/png2hicolorgb
# Use conversion method type 1, Faster Adaptive attribute sizing for Left / Right side of screen, C source output
# Bank 255 for auto-banking
HICOLOR_FLAGS = --type=1 -L=1 -R=1 --csource --bank=255
# Add the object dir as an include dir since that's
# where the converted png source files will get generated
LCCFLAGS += -I$(OBJDIR) -I$(SRCDIR)
# You can set the name of the .gb ROM file here
PROJECTNAME = hicolor_pic
BINDIR = bin
SRCDIR = src
OBJDIR = obj
RESDIR = res
HICOLORDIR = $(RESDIR)/hicolor
MKDIRS = $(OBJDIR) $(BINDIR) # See bottom of Makefile for directory auto-creation
BINS = $(BINDIR)/$(PROJECTNAME).gb
CSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.c))) $(foreach dir,$(RESDIR),$(notdir $(wildcard $(dir)/*.c)))
ASMSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.s)))
# For Hi Color:
# The HICOLORS entries should be first in the OBJS list so they get generated before anything which might depend on them
HICOLORS = $(foreach dir,$(HICOLORDIR),$(notdir $(wildcard $(dir)/*.png)))
HICOLOR_SRCS = $(HICOLORS:%.png=$(OBJDIR)/%.c) $(HICOLORS:%.png=$(OBJDIR)/%_pal.c)
OBJS = $(HICOLOR_SRCS:%.c=%.o) $(CSOURCES:%.c=$(OBJDIR)/%.o) $(ASMSOURCES:%.s=$(OBJDIR)/%.o)
all: $(BINS)
# Dependencies (using output from -Wf-MMD -Wf-Wp-MP)
DEPS = $(OBJS:%.o=%.d)
-include $(DEPS)
# == Start Hi Color conversion ==
# Convert png images in res/hicolor to .c files (which incbin the generated .til/.map/.pal/.atr files)
# The resulting C files will get compiled to object files afterward
.SECONDEXPANSION:
$(OBJDIR)/%.c: $(HICOLORDIR)/%.png
$(PNG2HICOLORGB) $< $(HICOLOR_FLAGS) -o $@
# Prevent make from deleting intermediary generated hi-color C source files
.SECONDARY: $(HICOLOR_SRCS)
# Compile hicolor .c files in "obj/" to .o object files
$(OBJDIR)/%.o: $(OBJDIR)/%.c
$(LCC) $(LCCFLAGS) -c -o $@ $<
# == End Hi Color conversion ==
# Compile .c files in "src/" to .o object files
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(LCC) $(LCCFLAGS) -c -o $@ $<
# Compile .c files in "res/" to .o object files
$(OBJDIR)/%.o: $(RESDIR)/%.c
$(LCC) $(LCCFLAGS) -c -o $@ $<
# Compile .s assembly files in "src/" to .o object files
$(OBJDIR)/%.o: $(SRCDIR)/%.s
$(LCC) $(LCCFLAGS) -c -o $@ $<
# If needed, compile .c files in "src/" to .s assembly files
# (not required if .c is compiled directly to .o)
$(OBJDIR)/%.s: $(SRCDIR)/%.c
$(LCC) $(LCCFLAGS) -S -o $@ $<
# Link the compiled object files into a .gb ROM file
$(BINS): $(OBJS)
$(LCC) $(LCCFLAGS) -o $(BINS) $(OBJS)
clean:
rm -f $(OBJDIR)/*.* $(BINDIR)/*.*
compile.bat: Makefile
@echo "REM Automatically generated from Makefile" > compile.bat
@make -sn | sed y/\\//\\\\/ | sed s/mkdir\ -p\/mkdir\/ | grep -v make >> compile.bat
# create necessary directories after Makefile is parsed but before build
# info prevents the command from being pasted into the makefile
$(info $(shell mkdir -p $(MKDIRS)))

View File

@@ -0,0 +1,16 @@
# GBDK Example
This example Requires GBDK-2020 4.2.0 or higher (due to using `vsync()`)
PNG images placed in the res/hicolor folder will get automatically converted and compiled.
To use them, include the automatically generated header file (based on the png filename) and then use the `hicolor_start()` and `hicolor_stop()` functions.
# Pallete ISR
The new palette update ISR is contributed by Toxa
https://github.com/untoxa
# Example image
Example image Pixel art originally by RodrixAP under Creative Commons Attribution 2.0 Generic (CC BY 2.0)
https://www.flickr.com/photos/rodrixap/10591266994/in/album-72157637154901153/

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1,164 @@
// TODO: Permissive License
#include <gbdk/platform.h>
#include <gbdk/incbin.h>
#include <gb/isr.h>
#include <gbc_hicolor.h>
#define MIN(A,B) ((A)<(B)?(A):(B))
static uint16_t SP_SAVE;
static uint8_t STAT_SAVE;
static uint8_t * p_hicolor_palettes;
static uint8_t hicolor_palettes_bank;
static uint8_t hicolor_last_scanline;
// ISR function which updates 4 CGB palettes per scanline
// alternating between Palettes 0-3 and 4-7
void hicolor_palette_isr(void) NONBANKED {
__asm
ldh a, (__current_bank) ; switch ROM bank
push af
ld a, (_hicolor_palettes_bank)
or a
jr z, 3$
ldh (__current_bank), a
ld (_rROMB0), a
3$:
ld (_SP_SAVE), sp ; save SP
ld hl, #_p_hicolor_palettes ; load address of picture palettes buffer
ld a, (hl+)
ld d, (hl)
ld e, a
ldh a, (_SCY_REG)
swap a
ld l, a
and #0x0f
ld h, a
ld a, #0xf0
and l
ld l, a
add hl, hl
add hl, de ; offset address by SCY * (4 * 4 * 2)
ld sp, hl
rlca ; compensate odd/even line
and #0x20 ; if odd then start from 4-th palette; offset == 32
or #BCPSF_AUTOINC ; set auto-increment
ld hl, #_BCPS_REG
ld (hl+), a ; HL now points to BCPD
ld c, #(8 * 4) ; read and set the the colors that come from previous lines
2$:
pop de
ld (hl), e
ld (hl), d
dec c
jr nz, 2$
0$:
ldh a, (_STAT_REG)
and #STATF_BUSY
jr z, 0$ ; wait for mode 3
ldh a, (_STAT_REG)
ld (_STAT_SAVE), a
ld a, #STATF_MODE00
ldh (_STAT_REG), a
xor a
ldh (_IF_REG), a
1$:
pop bc ; preload the first two colors
pop de
xor a
ldh (_IF_REG), a
halt ; wait for mode 0
ld (hl), c ; set the first two colors
ld (hl), b
ld (hl), e
ld (hl), d
.rept (4*4)-2 ; read and set four palettes except the two previously set colors
pop de
ld (hl), e
ld (hl), d
.endm
ld a, (_hicolor_last_scanline)
ld c, a
ldh a, (_LY_REG)
cp c
jr c, 1$ ; load the next 4 palettes
ld a, (_STAT_SAVE)
ldh (_STAT_REG), a
xor a
ldh (_IF_REG), a
ld sp, #_SP_SAVE
pop hl
ld sp, hl ; restore SP
pop af
ldh (__current_bank), a
ld (_rROMB0), a
ret
__endasm;
}
// Loads Tile Patterns, Map and Map Attributes for the HiColor image,
// then installs the HiColor ISR handler which updates palettes per scanline.
//
// The intput argument should be a pointer to the struct generated by the
// png2hicolorgb program with C source output mode enabled
void hicolor_start(const hicolor_data * p_hicolor, const uint8_t hicolor_bank, const uint8_t hicolor_bank_pal) NONBANKED {
// prevent installing HiColor ISR twice
CRITICAL {
remove_LCD(hicolor_palette_isr);
}
if (!p_hicolor) return;
hicolor_palettes_bank = hicolor_bank_pal;
uint8_t bank_save = _current_bank;
if (hicolor_bank) SWITCH_ROM(hicolor_bank);
// Copy address of palette into local var used by HiColor ISR
p_hicolor_palettes = p_hicolor->p_palette;
// TODO: if less than screen height, then converter must emit tail palettes and the cutting scanline must be moved accordingly
hicolor_last_scanline = (p_hicolor->height_in_tiles > DEVICE_SCREEN_HEIGHT) ? (DEVICE_SCREEN_PX_HEIGHT - 1) : ((p_hicolor->height_in_tiles << 3) - 1);
// Load the first 256 tiles or less and set BG Map
VBK_REG = VBK_BANK_0;
set_bkg_data(0u, MIN(p_hicolor->tile_count, 256), p_hicolor->p_tiles);
set_bkg_tiles(0u, 0u, DEVICE_SCREEN_WIDTH, p_hicolor->height_in_tiles, p_hicolor->p_map);
// Load remaining 256 tiles and set Attribute Map into alternate bank
VBK_REG = VBK_BANK_1;
if (p_hicolor->tile_count > 256) set_bkg_data(0u, (p_hicolor->tile_count - 256), p_hicolor->p_tiles + (256 * 16));
set_bkg_tiles(0, 0, DEVICE_SCREEN_WIDTH, p_hicolor->height_in_tiles, p_hicolor->p_attribute_map);
VBK_REG = VBK_BANK_0;
SWITCH_ROM(bank_save);
// Set up and install the HiColor ISR
CRITICAL {
LYC_REG = 152;
STAT_REG = STATF_LYC;
// install the HiColor ISR
add_LCD(hicolor_palette_isr);
}
set_interrupts(IE_REG | LCD_IFLAG);
}

View File

@@ -0,0 +1,27 @@
// TODO: Permissive License
#ifndef GBC_HICOLOR_H
#define GBC_HICOLOR_H
#define HICOLOR_VAR(varname) varname ## _data
typedef struct hicolor_data {
uint16_t tile_count;
uint8_t height_in_tiles;
uint8_t * p_tiles;
uint8_t * p_map;
uint8_t * p_attribute_map;
uint8_t * p_palette;
} hicolor_data;
// Loads Tile Patterns, Map and Map Attributes for the HiColor image,
// then installs the HiColor ISR handler which updates palettes per scanline.
void hicolor_start(const hicolor_data * p_hicolor, const uint8_t hicolor_bank, const uint8_t hicolor_bank_pal) NONBANKED;
// De-installs the HiColor ISR handler
inline void hicolor_stop(void) {
hicolor_start(NULL, 0, 0);
}
#endif

View File

@@ -0,0 +1,114 @@
#include <gbdk/platform.h>
#include <stdint.h>
#include <stdbool.h>
#include <gbc_hicolor.h>
// GBC HiColor images; header file names align with png file names
#include "example_image.h"
#include "test_pattern_tall.h"
#include "test_pattern_short.h"
#define BG_LAST_TILE 255u
const uint8_t blank_tile[] = {0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
#define ARRAY_LEN(A) sizeof(A) / sizeof(A[0])
uint8_t buttons, buttons_prev;
#define UPDATE_BUTTONS() (buttons_prev = buttons, buttons = joypad())
#define BUTTON_TOGGLED(BUTTON_MASK) ((buttons & (~buttons_prev)) & (BUTTON_MASK))
#define BUTTON_PRESSED(BUTTON_MASK) (buttons & (BUTTON_MASK))
typedef struct hicolor_rec_t {
uint8_t bank;
uint8_t bank_pal;
const void * ptr;
} hicolor_rec_t;
// Array of pointers to the generated hicolor data structures
const hicolor_rec_t hicolors[] = {
{ BANK(test_pattern_tall), BANK(test_pattern_tall_pal), &HICOLOR_VAR(test_pattern_tall) },
{ BANK(example_image), BANK(example_image_pal), &HICOLOR_VAR(example_image) },
{ BANK(test_pattern_short), BANK(test_pattern_short_pal), &HICOLOR_VAR(test_pattern_short) }
};
void main(void) {
// Image toggling variable, by default show the "example_image"
uint8_t img_select = 0;
bool first_pass = true;
uint8_t scroll_limit = 0;
const hicolor_data * p_hicolor;
uint8_t hicolor_bank;
uint8_t hicolor_bank_pal;
SHOW_BKG;
// Require Game Boy Color
if (_cpu == CGB_TYPE) {
// CGB running in the double speed mode is required
cpu_fast();
while(true) {
vsync();
UPDATE_BUTTONS();
// Change displayed Hi Color image when pressing A or B
if (BUTTON_TOGGLED(J_A | J_B) || first_pass) {
vsync();
DISPLAY_OFF;
// Set current image to show
hicolor_bank = hicolors[img_select].bank;
hicolor_bank_pal = hicolors[img_select].bank_pal;
p_hicolor = (const hicolor_data *)hicolors[img_select].ptr;
uint8_t bank_save = _current_bank;
if (hicolor_bank) SWITCH_ROM(hicolor_bank);
// Reset Y scroll and set scroll range based on converted image height
SCY_REG = 0u;
if ((p_hicolor->height_in_tiles * 8u) > DEVICE_SCREEN_PX_HEIGHT)
scroll_limit = ((p_hicolor->height_in_tiles * 8u) - DEVICE_SCREEN_PX_HEIGHT);
else scroll_limit = 0;
// Optional:
// If the Hi Color image is shorter than screen height
// then fill the remaining screen area with a tile.
//
// Put the tile at the end of CGB tile pattern vram since
// the short Hi Color image will be too small to use all of it.
if ((p_hicolor->height_in_tiles * 8u) < DEVICE_SCREEN_PX_HEIGHT) {
VBK_REG = VBK_BANK_1;
set_bkg_data(BG_LAST_TILE, 1u, blank_tile);
fill_bkg_rect(0u, (p_hicolor->height_in_tiles), DEVICE_SCREEN_WIDTH, DEVICE_SCREEN_HEIGHT, BKGF_BANK1);
VBK_REG = VBK_BANK_0;
fill_bkg_rect(0u, (p_hicolor->height_in_tiles), DEVICE_SCREEN_WIDTH, DEVICE_SCREEN_HEIGHT, BG_LAST_TILE);
}
SWITCH_ROM(bank_save);
// Load and display the HiColor image
hicolor_start(p_hicolor, hicolor_bank, hicolor_bank_pal);
DISPLAY_ON;
// Cycle through which image to show next
img_select++;
if (img_select == ARRAY_LEN(hicolors)) img_select = 0;
first_pass = false;
}
// Scroll Up/Down if available
else if (BUTTON_PRESSED(J_UP)) {
if (SCY_REG) SCY_REG--;
} else if (BUTTON_PRESSED(J_DOWN)) {
if (SCY_REG < scroll_limit) SCY_REG++;
}
}
}
}

View File

@@ -0,0 +1,45 @@
ifndef TARGETDIR
TARGETDIR = /opt/gbdk
endif
ifeq ($(OS),Windows_NT)
BUILD_OS := Windows_NT
LDFLAGS = -s -static
else
BUILD_OS := $(shell uname -s)
LDFLAGS = -s
endif
# Target older macOS version than whatever build OS is for better compatibility
ifeq ($(BUILD_OS),Darwin)
export MACOSX_DEPLOYMENT_TARGET=10.10
endif
CC = $(TOOLSPREFIX)gcc
CFLAGS = -O -Wno-incompatible-pointer-types -DGBDKLIBDIR=\"$(TARGETDIR)\"
CFLAGS += -Isrc -Isrc/lodepng -Isrc/hicolor
OBJ = src/common.o \
src/tile_dedupe.o \
src/lodepng/lodepng.o \
src/hicolor/Wu.o \
src/hicolor/median.o \
src/hicolor/hicolour.o \
src/c_source.o \
src/logging.o \
src/files.o \
src/options.o \
src/main.o \
src/path_ops.o \
src/image_load.o
BIN = png2hicolorgb
all: $(BIN)
$(BIN): $(OBJ)
$(CC) -o $(BIN) $^ $(CFLAGS) $(LDFLAGS)
clean:
rm -f *.o $(BIN) *~ src/*.o src/lodepng/*.o src/hicolor/*.o
rm -f *.exe

Binary file not shown.

View File

@@ -0,0 +1,169 @@
// See LICENSE file for license details
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include "common.h"
#include "image_info.h"
#include "path_ops.h"
#include "logging.h"
#include "options.h"
// Writes a buffer to a file in C source format
// Adds a matching .h if possible
//
bool file_c_output_write(const char * fname_base, int bank_num, int tile_count, int height_in_tiles) {
char varname_withpath[MAX_PATH * 2];
strcpy(varname_withpath, fname_base);
// Main C output (Tiles, Map, Attributes)
char filename_c[MAX_PATH * 2];
strcpy(filename_c, fname_base);
strcat(filename_c, ".c");
// Secondary C output (Palette)
// Split from the others for more efficient bank packing
char filename_c_pal[MAX_PATH * 2];
strcpy(filename_c_pal, fname_base);
strcat(filename_c_pal, "_pal.c");
// Header File
char filename_h[MAX_PATH * 2];
strcpy(filename_h, fname_base);
strcat(filename_h, ".h");
FILE * file_out;
// Fix up filename to be usable in the C source file
char * varname = (char *)get_filename_from_path(varname_withpath);
char * ch = varname;
while (*ch != '\0') {
if ((*ch == ' ') || (*ch == '-')) *ch = '_';
ch++;
}
log_verbose("Writing C format to: %s, %s, %s\n", filename_c, filename_c_pal, filename_h);
// === C Source output ===
file_out = fopen(filename_c, "w");
if (file_out) {
fprintf(file_out,
"#include <gbdk/platform.h>\n"
"#include <gbdk/incbin.h>\n\n");
// If Bank Num is set add a .h bank ref
if (bank_num != BANK_NUM_UNSET) {
fprintf(file_out, "#pragma bank %d\n", bank_num);
fprintf(file_out, "BANKREF(%s)\n\n", varname);
}
fprintf(file_out,
"#include <gbc_hicolor.h>\n"
"\n"
"// Generated by png2hicolorgb\n"
"\n"
"// Note: filepath is relative to the working directory of the tool that is calling it (often a makefile's working directory), NOT to the file it's being included into.\n"
"\n"
"INCBIN(%s_tiles, \"%s.til\")\n"
"INCBIN(%s_map, \"%s.map\")\n"
"INCBIN(%s_attr, \"%s.atr\")\n"
"\n"
"INCBIN_EXTERN(%s_tiles)\n"
"INCBIN_EXTERN(%s_map)\n"
"INCBIN_EXTERN(%s_attr)\n"
"INCBIN_EXTERN(%s_palette)\n"
"\n"
"const hicolor_data %s_data = {\n"
" .tile_count = %du,\n"
" .height_in_tiles = %du,\n"
" .p_tiles = %s_tiles,\n"
" .p_map = %s_map,\n"
" .p_attribute_map = %s_attr,\n"
" .p_palette = %s_palette\n"
"};\n"
"\n"
,varname, fname_base,
varname, fname_base,
varname, fname_base,
varname, varname, varname, varname,
varname,
tile_count, height_in_tiles,
varname, varname, varname, varname
);
fclose(file_out);
} else {
printf("Error: Failed to open output file: %s\n", filename_c);
return false;
}
// === C Source output ===
file_out = fopen(filename_c_pal, "w");
if (file_out) {
fprintf(file_out,
"#include <gbdk/platform.h>\n"
"#include <gbdk/incbin.h>\n\n");
// If Bank Num is set add a .h bank ref
if (bank_num != BANK_NUM_UNSET) {
fprintf(file_out, "#pragma bank %d\n", bank_num);
fprintf(file_out, "BANKREF(%s_pal)\n\n", varname);
}
fprintf(file_out,
"#include <gbc_hicolor.h>\n"
"\n"
"// Generated by png2hicolorgb\n"
"\n"
"// Note: filepath is relative to the working directory of the tool that is calling it (often a makefile's working directory), NOT to the file it's being included into.\n"
"\n"
"INCBIN(%s_palette, \"%s.pal\")\n"
"\n"
,varname, fname_base
);
fclose(file_out);
} else {
printf("Error: Failed to open output file: %s\n", filename_c);
return false;
}
// === Header output ===
file_out = fopen(filename_h, "w");
if (file_out) {
// If Bank Num is set add a .h bank ref
if (bank_num != BANK_NUM_UNSET) {
fprintf(file_out, "#include <gbdk/platform.h>\n\n");
fprintf(file_out, "BANKREF_EXTERN(%s)\n\n", varname);
fprintf(file_out, "BANKREF_EXTERN(%s_pal)\n\n", varname);
}
fprintf(file_out,
"#ifndef __%s_h_\n"
"#define __%s_h_\n"
"\n"
"#include <gbc_hicolor.h>\n"
"\n"
"// Generated by png2hicolorgb\n"
"\n"
"extern const hicolor_data %s_data;\n"
"\n"
"#endif\n"
"\n"
,varname, varname,
varname
);
fclose(file_out);
} else {
printf("Error: Failed to open output file: %s\n", filename_h);
return false;
}
return true;
}

View File

@@ -0,0 +1,9 @@
// "c_source.h"
#ifndef C_SOURCE_H
#define C_SOURCE_H
bool file_c_output_write(const char * fname_base, int bank_num, int tile_count, int height_in_tiles);
#endif

View File

@@ -0,0 +1,17 @@
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include "common.h"
bool exit_error = false;
void set_exit_error(void) {
exit_error = true;
}
bool get_exit_error(void) {
return exit_error;
}

View File

@@ -0,0 +1,23 @@
// See LICENSE file for license details
#ifndef _COMMON_H
#define _COMMON_H
#define TILE_HEIGHT_PX 8
#define TILE_WIDTH_PX 8
#define MAX_STR_LEN 4096
#define DEFAULT_STR_LEN 100
#define ARRAY_LEN(A) sizeof(A) / sizeof(A[0])
#define MAX_PATH (MAX_STR_LEN)
enum {
IMG_TYPE_PNG
};
void set_exit_error(void);
bool get_exit_error(void);
#endif // _COMMON_H

View File

@@ -0,0 +1,118 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
// Read from a file into a buffer (will allocate needed memory)
// Returns NULL if reading file didn't succeed
uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size) {
long fsize;
FILE * file_in = fopen(filename, "rb");
uint8_t * filedata = NULL;
if (file_in) {
// Get file size
fseek(file_in, 0, SEEK_END);
fsize = ftell(file_in);
if (fsize != -1L) {
fseek(file_in, 0, SEEK_SET);
filedata = (uint8_t *)malloc(fsize);
if (filedata) {
if (fsize != fread(filedata, 1, fsize, file_in)) {
printf("Warning: File read size didn't match expected for %s\n", filename);
filedata = NULL;
}
// Read was successful, set return size
*ret_size = fsize;
} else printf("ERROR: Failed to allocate memory to read file %s\n", filename);
} else printf("ERROR: Failed to read size of file %s\n", filename);
fclose(file_in);
} else printf("ERROR: Failed to open input file %s\n", filename);
return filedata;
}
// Writes a buffer to a file
bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len) {
bool status = false;
size_t wrote_bytes;
FILE * file_out = fopen(filename, "wb");
if (file_out) {
if (data_len == fwrite(p_buf, 1, data_len, file_out))
status = true;
else
printf("Warning: File write size didn't match expected for %s\n", filename);
fclose(file_out);
} else
printf("Warning: Unable to open file: %s\n", filename);
return status;
}
// Read from a file into a buffer (will allocate needed memory)
// Returns NULL if reading file didn't succeed
char * file_read_into_buffer_char(char * filename, uint32_t *ret_size) {
long fsize;
// On windows windows fread() will auto-translate CRLF to just LF and
// report it as one byte read, messing up the size check
FILE * file_in = fopen(filename, "rb");
char * filedata = NULL;
if (file_in) {
// Get file size
fseek(file_in, 0, SEEK_END);
fsize = ftell(file_in);
if (fsize != -1L) {
fseek(file_in, 0, SEEK_SET);
filedata = (char *)malloc(fsize);
if (filedata) {
if (fsize != fread(filedata, 1, fsize, file_in)) {
printf("Warning: File read size didn't match expected for %s\n", filename);
filedata = NULL;
}
// Read was successful, set return size
*ret_size = fsize;
} else printf("ERROR: Failed to allocate memory to read file %s\n", filename);
} else printf("ERROR: Failed to read size of file %s\n", filename);
fclose(file_in);
} else printf("ERROR: Failed to open input file %s\n", filename);
return filedata;
}
// Writes a buffer to a file
bool file_write_from_buffer_char(char * filename, char * p_buf, uint32_t data_len) {
bool status = false;
size_t wrote_bytes;
FILE * file_out = fopen(filename, "w");
if (file_out) {
if (data_len == fwrite(p_buf, 1, data_len, file_out))
status = true;
else
printf("Warning: File write size didn't match expected for %s\n", filename);
fclose(file_out);
}
return status;
}

View File

@@ -0,0 +1,11 @@
#ifndef _FILES_H
#define _FILES_H
uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size);
bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len);
char * file_read_into_buffer_char(char * filename, uint32_t *ret_size);
bool file_write_from_buffer_char(char * filename, char * p_buf, uint32_t data_len);
#endif // _FILES_H

View File

@@ -0,0 +1,475 @@
/*
C Implementation of Wu's Color Quantizer (v2.0)
-- Xiaolin Wu.
From the Graphics Gems.
Memory hungry.
Some fixes and additional code by Benny.
*/
/**********************************************************************
C Implementation of Wu's Color Quantizer (v. 2)
(see Graphics Gems vol. II, pp. 126-133)
Author: Xiaolin Wu
Dept. of Computer Science
Univ. of Western Ontario
London, Ontario N6A 5B7
wu@csd.uwo.ca
Algorithm: Greedy orthogonal bipartition of RGB space for variance
minimization aided by inclusion-exclusion tricks.
For speed no nearest neighbor search is done. Slightly
better performance can be expected by more sophisticated
but more expensive versions.
The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of
additional documentation and a cure to a previous bug.
Free to distribute, comments and suggestions are appreciated.
**********************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "defines.h"
#include "hicolour.h"
#include "median.h"
#include "wu.h"
#define NOINVERT // RGB or BGR
//void error(char *s);
#define MAXCOLOR 256
#define RED 2
#define GREEN 1
#define BLUE 0
/* Histogram is in elements 1..HISTSIZE along each axis,
element 0 is for base or marginal value.
NB: these must start out 0! */
s32 wt[BOX][BOX][BOX],mr[BOX][BOX][BOX],mg[BOX][BOX][BOX],mb[BOX][BOX][BOX];
float m2[BOX][BOX][BOX];
s32 ImageSize; /* image size */
s32 PalSize; /* color look-up table size */
u16 *Qadd; // *must* be unsigned?
u8 *TrueColorPic;
// s16 AQadd[7*8*3*2]; // Signed in original code, type clash with QAdd and bit shifting
u16 AQadd[7*8*3*2];
u8 Atag[BOX * BOX * BOX];
/* build 3-D color histogram of counts, r/g/b, c^2 */
void Hist3d(s32 *vwt,s32 *vmr,s32 *vmg,s32 *vmb, float *m_2)
{
s32 ind,r,g,b;
s32 inr,ing,inb,table[256];
s32 i;
for (i = 0; i < 256; ++i)
table[i] = i * i;
for (i = 0; i < ImageSize; ++i)
{
r = TrueColorPic[i*3 ];
g = TrueColorPic[i*3+1];
b = TrueColorPic[i*3+2];
inr = (r >> 3) + 1;
ing = (g >> 3) + 1;
inb = (b >> 3) + 1;
Qadd[i] = ind = (inr << 10) + (inr << 6) + inr + (ing << 5) + ing + inb;
++vwt[ind];
vmr[ind] += r;
vmg[ind] += g;
vmb[ind] += b;
m_2[ind] += (float) (table[r] + table[g] + table[b]);
}
}
/* At conclusion of the histogram step, we can interpret
* wt[r][g][b] = sum over voxel of P(c)
* mr[r][g][b] = sum over voxel of r*P(c) , similarly for mg, mb
* m2[r][g][b] = sum over voxel of c^2*P(c)
* Actually each of these should be divided by 'size' to give the usual
* interpretation of P() as ranging from 0 to 1, but we needn't do that here.
*/
/* We now convert histogram into moments so that we can rapidly calculate
* the sums of the above quantities over any desired box.
*/
void Momt3d(s32 *vwt, s32 *vmr, s32 *vmg, s32 *vmb, float *m_2)
{
u16 ind1,ind2;
u8 i,r,g,b;
s32 line,line_r,line_g,line_b,area[BOX],area_r[BOX],area_g[BOX],area_b[BOX];
float line2,area2[BOX];
for (r = 1; r <= 32; ++r)
{
for (i = 0; i <= 32; ++i)
{
area[i] = area_r[i] = area_g[i] = area_b[i] = 0;
area2[i] = 0.0;
}
for (g = 1; g <= 32; ++g)
{
line2 = 0.0;
line = line_r = line_g = line_b = 0;
for (b = 1; b <= 32; ++b)
{
ind1 = (r << 10) + (r << 6) + r + (g << 5) + g + b;
line += vwt[ind1];
line_r += vmr[ind1];
line_g += vmg[ind1];
line_b += vmb[ind1];
line2 += m_2[ind1];
area[b] += line;
area_r[b] += line_r;
area_g[b] += line_g;
area_b[b] += line_b;
area2[b] += line2;
ind2 = ind1 - 1089;
vwt[ind1] = vwt[ind2] + area[b];
vmr[ind1] = vmr[ind2] + area_r[b];
vmg[ind1] = vmg[ind2] + area_g[b];
vmb[ind1] = vmb[ind2] + area_b[b];
m_2[ind1] = m_2[ind2] + area2[b];
}
}
}
}
s32 Vol(struct box * cube, s32 mmt[BOX][BOX][BOX])
{
return (mmt[cube->r1][cube->g1][cube->b1] - mmt[cube->r1][cube->g1][cube->b0] - mmt[cube->r1][cube->g0][cube->b1]
+ mmt[cube->r1][cube->g0][cube->b0] - mmt[cube->r0][cube->g1][cube->b1] + mmt[cube->r0][cube->g1][cube->b0]
+ mmt[cube->r0][cube->g0][cube->b1] - mmt[cube->r0][cube->g0][cube->b0]);
}
/* The next two routines allow a slightly more efficient calculation
* of Vol() for a proposed subbox of a given box. The sum of Top()
* and Bottom() is the Vol() of a subbox split in the given direction
* and with the specified new upper bound.
*/
/* Compute part of Vol(cube, mmt) that doesn't depend on r1, g1, or b1 */
/* (depending on dir) */
s32 Bottom(struct box * cube, u8 dir, s32 mmt[BOX][BOX][BOX])
{
switch (dir)
{
case RED:
return (-mmt[cube->r0][cube->g1][cube->b1] + mmt[cube->r0][cube->g1][cube->b0]
+ mmt[cube->r0][cube->g0][cube->b1] - mmt[cube->r0][cube->g0][cube->b0]);
case GREEN:
return (-mmt[cube->r1][cube->g0][cube->b1] + mmt[cube->r1][cube->g0][cube->b0]
+ mmt[cube->r0][cube->g0][cube->b1] - mmt[cube->r0][cube->g0][cube->b0]);
case BLUE:
return (-mmt[cube->r1][cube->g1][cube->b0] + mmt[cube->r1][cube->g0][cube->b0]
+ mmt[cube->r0][cube->g1][cube->b0] - mmt[cube->r0][cube->g0][cube->b0]);
}
return 0;
}
s32 Top(struct box * cube, u8 dir, s32 pos, s32 mmt[BOX][BOX][BOX])
{
switch (dir)
{
case RED:
return (mmt[pos][cube->g1][cube->b1] - mmt[pos][cube->g1][cube->b0]
- mmt[pos][cube->g0][cube->b1] + mmt[pos][cube->g0][cube->b0]);
case GREEN:
return (mmt[cube->r1][pos][cube->b1] - mmt[cube->r1][pos][cube->b0]
- mmt[cube->r0][pos][cube->b1] + mmt[cube->r0][pos][cube->b0]);
case BLUE:
return (mmt[cube->r1][cube->g1][pos] - mmt[cube->r1][cube->g0][pos]
- mmt[cube->r0][cube->g1][pos] + mmt[cube->r0][cube->g0][pos]);
}
return 0;
}
/* Compute the weighted variance of a box */
/* NB: as with the raw statistics, this is really the variance * size */
float Var(struct box * cube)
{
float dr,dg,db,xx;
dr = (float)Vol(cube, mr);
dg = (float)Vol(cube, mg);
db = (float)Vol(cube, mb);
xx = m2[cube->r1][cube->g1][cube->b1] - m2[cube->r1][cube->g1][cube->b0] - m2[cube->r1][cube->g0][cube->b1]
+ m2[cube->r1][cube->g0][cube->b0] - m2[cube->r0][cube->g1][cube->b1] + m2[cube->r0][cube->g1][cube->b0]
+ m2[cube->r0][cube->g0][cube->b1] - m2[cube->r0][cube->g0][cube->b0];
return (xx - (dr * dr + dg * dg + db * db) / (float) Vol(cube, wt));
}
/* We want to minimize the sum of the variances of two subboxes.
* The sum(c^2) terms can be ignored since their sum over both subboxes
* is the same (the sum for the whole box) no matter where we split.
* The remaining terms have a minus sign in the variance formula,
* so we drop the minus sign and MAXIMIZE the sum of the two terms.
*/
float Maximize(struct box *cube, u8 dir, s32 first, s32 last, s32 *cut, s32 whole_r, s32 whole_g, s32 whole_b, s32 whole_w)
{
s32 half_r,half_g,half_b,half_w;
s32 base_r,base_g,base_b,base_w;
s32 i;
float temp,max;
base_r = Bottom(cube, dir, mr);
base_g = Bottom(cube, dir, mg);
base_b = Bottom(cube, dir, mb);
base_w = Bottom(cube, dir, wt);
max = 0.0;
*cut = -1;
for (i = first; i < last; ++i)
{
half_r = base_r + Top(cube, dir, i, mr);
half_g = base_g + Top(cube, dir, i, mg);
half_b = base_b + Top(cube, dir, i, mb);
half_w = base_w + Top(cube, dir, i, wt);
// now half_x is sum over lower half of box, if split at i
if (half_w == 0)
{
// subbox could be empty of pixels!
// never split into an empty box
continue;
}
else
{
temp = ((float) half_r * half_r + (float) half_g * half_g + (float) half_b * half_b) / half_w;
}
half_r = whole_r - half_r;
half_g = whole_g - half_g;
half_b = whole_b - half_b;
half_w = whole_w - half_w;
if (half_w == 0)
{
// subbox could be empty of pixels!
// never split into an empty box
continue;
}
else
{
temp += ((float) half_r * half_r + (float) half_g * half_g + (float) half_b * half_b) / half_w;
}
if (temp > max)
{
max = temp;
*cut = i;
}
}
return (max);
}
s32 Cut(struct box * set1, struct box * set2)
{
u8 dir;
s32 cutr,cutg,cutb;
float maxr,maxg,maxb;
s32 whole_r,whole_g,whole_b,whole_w;
whole_r = Vol(set1, mr);
whole_g = Vol(set1, mg);
whole_b = Vol(set1, mb);
whole_w = Vol(set1, wt);
maxr = Maximize(set1, RED, set1->r0 + 1, set1->r1, &cutr, whole_r, whole_g, whole_b, whole_w);
maxg = Maximize(set1, GREEN, set1->g0 + 1, set1->g1, &cutg, whole_r, whole_g, whole_b, whole_w);
maxb = Maximize(set1, BLUE, set1->b0 + 1, set1->b1, &cutb, whole_r, whole_g, whole_b, whole_w);
if ((maxr >= maxg) && (maxr >= maxb))
{
dir = RED;
if (cutr < 0)
return 0; /* can't split the box */
}
else if ((maxg >= maxr) && (maxg >= maxb))
{
dir = GREEN;
}
else
{
dir = BLUE;
}
set2->r1 = set1->r1;
set2->g1 = set1->g1;
set2->b1 = set1->b1;
switch (dir)
{
case RED:
set2->r0 = set1->r1 = cutr;
set2->g0 = set1->g0;
set2->b0 = set1->b0;
break;
case GREEN:
set2->g0 = set1->g1 = cutg;
set2->r0 = set1->r0;
set2->b0 = set1->b0;
break;
case BLUE:
set2->b0 = set1->b1 = cutb;
set2->r0 = set1->r0;
set2->g0 = set1->g0;
break;
}
set1->vol = (set1->r1 - set1->r0) * (set1->g1 - set1->g0) * (set1->b1 - set1->b0);
set2->vol = (set2->r1 - set2->r0) * (set2->g1 - set2->g0) * (set2->b1 - set2->b0);
return 1;
}
void Mark(struct box *cube, s32 label, u8 *tag)
{
s32 r,g,b;
for (r = cube->r0 + 1; r <= cube->r1; ++r)
for (g = cube->g0 + 1; g <= cube->g1; ++g)
for (b = cube->b0 + 1; b <= cube->b1; ++b)
tag[(r << 10) + (r << 6) + r + (g << 5) + g + b] = label;
}
s32 wuReduce(u8 *RGBpic, s32 numcolors, s32 picsize)
{
struct box cube[MAXCOLOR];
u8 *tag = 0;
float vv[MAXCOLOR],temp = 0.;
s32 i = 0,weight = 0;
s32 next = 0;
s32 j = 0,k = 0,l = 0;
TrueColorPic = RGBpic;
ImageSize = picsize;
PalSize = numcolors;
for (j=0;j<BOX;j++)
for (k=0;k<BOX;k++)
for (l=0;l<BOX;l++)
{
wt[j][k][l] = 0;
mr[j][k][l] = 0;
mg[j][k][l] = 0;
mb[j][k][l] = 0;
m2[j][k][l] = 0.;
}
Qadd = AQadd;
// Hist3d((long *)&wt, (long *)&mr, (long *)&mg, (long *)&mb, (float *)&m2);
// Momt3d((long *)&wt, (long *)&mr, (long *)&mg, (long *)&mb, (float *)&m2);
Hist3d((s32 *)&wt, (s32 *)&mr, (s32 *)&mg, (s32 *)&mb, (float *)&m2);
// Histogram done
Momt3d((s32 *)&wt, (s32 *)&mr, (s32 *)&mg, (s32 *)&mb, (float *)&m2);
// Moments done
cube[0].r0 = cube[0].g0 = cube[0].b0 = 0;
cube[0].r1 = cube[0].g1 = cube[0].b1 = 32;
next = 0;
for (i = 1; i < PalSize; ++i)
{
if (Cut(&cube[next], &cube[i]))
{
// volume test ensures we won't try to cut one-cell box
vv[next] = (float)((cube[next].vol > 1) ? Var(&cube[next]) : 0.0);
vv[i] = (float)((cube[i].vol > 1) ? Var(&cube[i]) : 0.0);
}
else
{
vv[next] = 0.0;
i--;
}
next = 0;
temp = vv[0];
for (k = 1; k <= i; ++k)
{
if (vv[k] > temp)
{
temp = vv[k];
next = k;
}
}
if (temp <= 0.0)
{
PalSize = i + 1;
break;
}
}
tag = Atag;
for (k = 0; k < PalSize; ++k)
{
Mark(&cube[k], k, tag);
weight = Vol(&cube[k], wt);
if (weight)
{
QuantizedPalette[k][2] = (unsigned char)(Vol(&cube[k], mr) / weight);
QuantizedPalette[k][0] = (unsigned char)(Vol(&cube[k], mb) / weight);
QuantizedPalette[k][1] = (unsigned char)(Vol(&cube[k], mg) / weight);
}
else
{
QuantizedPalette[k][0] = QuantizedPalette[k][1] = QuantizedPalette[k][2] = 0;
}
}
for (i = 0; i < ImageSize; i++)
Picture256[i] = tag[Qadd[i]];
return 0;
}

View File

@@ -0,0 +1,69 @@
#ifndef __defines_h__
#define __defines_h__
#include <stdint.h>
#include <common.h>
// Originates on Windows format is BGRX24
// TODO: is this right & ok for what code expects?
typedef struct tagRGBQUAD {
uint8_t rgbBlue;
uint8_t rgbGreen;
uint8_t rgbRed;
uint8_t rgbReserved;
} RGBQUAD;
#define CGB_ATTR_TILES_BANK_0 0x00u
#define CGB_ATTR_TILES_BANK_1 0x08u
#define CGB_ATTR_TILES_BANK 0x08u
#define CGB_ATTR_NO_FLIP 0x00u
#define CGB_ATTR_VFLIP 0x40u
#define CGB_ATTR_HFLIP 0x20u
#define CGB_ATTR_PALLETES_ONLY 0x07u
#define CGB_ATTR_WITHOUT_PALLETES (~(CGB_ATTR_PALLETES_ONLY))
#define CGB_TILES_START_BANK_0 0u
#define CGB_TILES_START_BANK_1 256u
#define RGB_SZ 3 // RGB888 size in bytes
#define TILE_SZ 16u // Tile size in bytes: (8 x 8) pixels x 2 Bits per pixel
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define s8 int8_t
#define s16 int16_t
#define s32 int32_t
enum conversion_types {
CONV_TYPE_MED_CUT_NO_DITHER = 1,
CONV_TYPE_MED_CUT_YES_DITHER = 2,
CONV_TYPE_WU = 3,
CONV_TYPE_MIN = CONV_TYPE_MED_CUT_NO_DITHER,
CONV_TYPE_MAX = CONV_TYPE_WU
};
#define CONV_SIDE_LEFT 0
#define CONV_SIDE_RIGHT 1
#define CONV_Y_SHIFT_UP_1 1
#define CONV_Y_SHIFT_NO 0
#define PAL_REGION_HEIGHT_PX 2
// #define BUF_WIDTH 160 // Originally 160
// #define BUF_WIDTH_TILE_MAX (BUF_WIDTH / TILE_WIDTH_PX)
#define BUF_HEIGHT 256 // Originally 144
#define BUF_HEIGHT_IN_TILES (BUF_HEIGHT / TILE_HEIGHT_PX)
#define BUF_HEIGHT_IN_TILES_RNDUP (BUF_HEIGHT_IN_TILES+1) // Use larger size[side] for rounded up amount
#define BUF_Y_REGION_COUNT_LR_RNDUP (((BUF_HEIGHT / PAL_REGION_HEIGHT_PX) + 1))
#define VALIDATE_WIDTH 160 // (BUF_WIDTH)
#define VALIDATE_HEIGHT (BUF_HEIGHT)
#endif

View File

@@ -0,0 +1,554 @@
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*/
#ifndef __FSDITHER_H__
#define __FSDITHER_H__
/* The following 5 arrays are used in performing floyd-steinberg
* error diffusion dithering. The range array allows the quick
* bounds checking of pixel values. The 4 error arrays contain
* the error computations for the east, south-east, south and
* south-west pixels surrounding the current pixel respectively.
*/
//short
unsigned char
range_array[] = {
/*
1 2 3 4 5 6 7 8 9 0
*/
0,0,0,0,0,0,0,0,0,0, // L20
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,1,2,3, // L45 (+25) 250 - 260-4=256
4,5,6,7,8,9,10,11,12,13,
14,15,16,17,18,19,20,21,22,23,
24,25,26,27,28,29,30,31,32,33,
34,35,36,37,38,39,40,41,42,43,
44,45,46,47,48,49,50,51,52,53,
54,55,56,57,58,59,60,61,62,63,
64,65,66,67,68,69,70,71,72,73,
74,75,76,77,78,79,80,81,82,83,
84,85,86,87,88,89,90,91,92,93,
94,95,96,97,98,99,100,101,102,103,
104,105,106,107,108,109,110,111,112,113,
114,115,116,117,118,119,120,121,122,123,
124,125,126,127,128,129,130,131,132,133,
134,135,136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,152,153,
154,155,156,157,158,159,160,161,162,163,
164,165,166,167,168,169,170,171,172,173,
174,175,176,177,178,179,180,181,182,183,
184,185,186,187,188,189,190,191,192,193,
194,195,196,197,198,199,200,201,202,203,
204,205,206,207,208,209,210,211,212,213,
214,215,216,217,218,219,220,221,222,223,
224,225,226,227,228,229,230,231,232,233,
234,235,236,237,238,239,240,241,242,243,
244,245,246,247,248,249,250,251,252,253,
254,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255, // L122 : 122-20=102
// 102*10-5 = 1015
};
short floyd_steinberg_error1[] = {
-223,-223,-222,-222,-221,-221,-220,-220,-220,-219,
-219,-218,-218,-217,-217,-217,-216,-216,-215,-215,
-214,-214,-213,-213,-213,-212,-212,-211,-211,-210,
-210,-210,-209,-209,-208,-208,-207,-207,-206,-206,
-206,-205,-205,-204,-204,-203,-203,-203,-202,-202,
-201,-201,-200,-200,-199,-199,-199,-198,-198,-197,
-197,-196,-196,-196,-195,-195,-194,-194,-193,-193,
-192,-192,-192,-191,-191,-190,-190,-189,-189,-189,
-188,-188,-187,-187,-186,-186,-185,-185,-185,-184,
-184,-183,-183,-182,-182,-182,-181,-181,-180,-180,
-179,-179,-178,-178,-178,-177,-177,-176,-176,-175,
-175,-175,-174,-174,-173,-173,-172,-172,-171,-171,
-171,-170,-170,-169,-169,-168,-168,-168,-167,-167,
-166,-166,-165,-165,-164,-164,-164,-163,-163,-162,
-162,-161,-161,-161,-160,-160,-159,-159,-158,-158,
-157,-157,-157,-156,-156,-155,-155,-154,-154,-154,
-153,-153,-152,-152,-151,-151,-150,-150,-150,-149,
-149,-148,-148,-147,-147,-147,-146,-146,-145,-145,
-144,-144,-143,-143,-143,-142,-142,-141,-141,-140,
-140,-140,-139,-139,-138,-138,-137,-137,-136,-136,
-136,-135,-135,-134,-134,-133,-133,-133,-132,-132,
-131,-131,-130,-130,-129,-129,-129,-128,-128,-127,
-127,-126,-126,-126,-125,-125,-124,-124,-123,-123,
-122,-122,-122,-121,-121,-120,-120,-119,-119,-119,
-118,-118,-117,-117,-116,-116,-115,-115,-115,-114,
-114,-113,-113,-112,-112,-112,-111,-111,-110,-110,
-109,-109,-108,-108,-108,-107,-107,-106,-106,-105,
-105,-105,-104,-104,-103,-103,-102,-102,-101,-101,
-101,-100,-100,-99,-99,-98,-98,-98,-97,-97,
-96,-96,-95,-95,-94,-94,-94,-93,-93,-92,
-92,-91,-91,-91,-90,-90,-89,-89,-88,-88,
-87,-87,-87,-86,-86,-85,-85,-84,-84,-84,
-83,-83,-82,-82,-81,-81,-80,-80,-80,-79,
-79,-78,-78,-77,-77,-77,-76,-76,-75,-75,
-74,-74,-73,-73,-73,-72,-72,-71,-71,-70,
-70,-70,-69,-69,-68,-68,-67,-67,-66,-66,
-66,-65,-65,-64,-64,-63,-63,-63,-62,-62,
-61,-61,-60,-60,-59,-59,-59,-58,-58,-57,
-57,-56,-56,-56,-55,-55,-54,-54,-53,-53,
-52,-52,-52,-51,-51,-50,-50,-49,-49,-49,
-48,-48,-47,-47,-46,-46,-45,-45,-45,-44,
-44,-43,-43,-42,-42,-42,-41,-41,-40,-40,
-39,-39,-38,-38,-38,-37,-37,-36,-36,-35,
-35,-35,-34,-34,-33,-33,-32,-32,-31,-31,
-31,-30,-30,-29,-29,-28,-28,-28,-27,-27,
-26,-26,-25,-25,-24,-24,-24,-23,-23,-22,
-22,-21,-21,-21,-20,-20,-19,-19,-18,-18,
-17,-17,-17,-16,-16,-15,-15,-14,-14,-14,
-13,-13,-12,-12,-11,-11,-10,-10,-10,-9,
-9,-8,-8,-7,-7,-7,-6,-6,-5,-5,
-4,-4,-3,-3,-3,-2,-2,-1,-1,
0,
0,0,0,0,1,1,2,2,3,3,
3,4,4,5,5,6,6,7,7,7,
8,8,9,9,10,10,10,11,11,12,
12,13,13,14,14,14,15,15,16,16,
17,17,17,18,18,19,19,20,20,21,
21,21,22,22,23,23,24,24,24,25,
25,26,26,27,27,28,28,28,29,29,
30,30,31,31,31,32,32,33,33,34,
34,35,35,35,36,36,37,37,38,38,
38,39,39,40,40,41,41,42,42,42,
43,43,44,44,45,45,45,46,46,47,
47,48,48,49,49,49,50,50,51,51,
52,52,52,53,53,54,54,55,55,56,
56,56,57,57,58,58,59,59,59,60,
60,61,61,62,62,63,63,63,64,64,
65,65,66,66,66,67,67,68,68,69,
69,70,70,70,71,71,72,72,73,73,
73,74,74,75,75,76,76,77,77,77,
78,78,79,79,80,80,80,81,81,82,
82,83,83,84,84,84,85,85,86,86,
87,87,87,88,88,89,89,90,90,91,
91,91,92,92,93,93,94,94,94,95,
95,96,96,97,97,98,98,98,99,99,
100,100,101,101,101,102,102,103,103,104,
104,105,105,105,106,106,107,107,108,108,
108,109,109,110,110,111,111,112,112,112,
113,113,114,114,115,115,115,116,116,117,
117,118,118,119,119,119,120,120,121,121,
122,122,122,123,123,124,124,125,125,126,
126,126,127,127,128,128,129,129,129,130,
130,131,131,132,132,133,133,133,134,134,
135,135,136,136,136,137,137,138,138,139,
139,140,140,140,141,141,142,142,143,143,
143,144,144,145,145,146,146,147,147,147,
148,148,149,149,150,150,150,151,151,152,
152,153,153,154,154,154,155,155,156,156,
157,157,157,158,158,159,159,160,160,161,
161,161,162,162,163,163,164,164,164,165,
165,166,166,167,167,168,168,168,169,169,
170,170,171,171,171,172,172,173,173,174,
174,175,175,175,176,176,177,177,178,178,
178,179,179,180,180,181,181,182,182,182,
183,183,184,184,185,185,185,186,186,187,
187,188,188,189,189,189,190,190,191,191,
192,192,192,193,193,194,194,195,195,196,
196,196,197,197,198,198,199,199,199,200,
200,201,201,202,202,203,203,203,204,204,
205,205,206,206,206,207,207,208,208,209,
209,210,210,210,211,211,212,212,213,213,
213,214,214,215,215,216,216,217,217,217,
218,218,219,219,220,220,220,221,221,222,
222,223,223,224,224,
};
short floyd_steinberg_error2[] = {
-95,-95,-95,-95,-95,-94,-94,-94,-94,-94,
-93,-93,-93,-93,-93,-93,-92,-92,-92,-92,
-92,-91,-91,-91,-91,-91,-90,-90,-90,-90,
-90,-90,-89,-89,-89,-89,-89,-88,-88,-88,
-88,-88,-87,-87,-87,-87,-87,-87,-86,-86,
-86,-86,-86,-85,-85,-85,-85,-85,-84,-84,
-84,-84,-84,-84,-83,-83,-83,-83,-83,-82,
-82,-82,-82,-82,-81,-81,-81,-81,-81,-81,
-80,-80,-80,-80,-80,-79,-79,-79,-79,-79,
-78,-78,-78,-78,-78,-78,-77,-77,-77,-77,
-77,-76,-76,-76,-76,-76,-75,-75,-75,-75,
-75,-75,-74,-74,-74,-74,-74,-73,-73,-73,
-73,-73,-72,-72,-72,-72,-72,-72,-71,-71,
-71,-71,-71,-70,-70,-70,-70,-70,-69,-69,
-69,-69,-69,-69,-68,-68,-68,-68,-68,-67,
-67,-67,-67,-67,-66,-66,-66,-66,-66,-66,
-65,-65,-65,-65,-65,-64,-64,-64,-64,-64,
-63,-63,-63,-63,-63,-63,-62,-62,-62,-62,
-62,-61,-61,-61,-61,-61,-60,-60,-60,-60,
-60,-60,-59,-59,-59,-59,-59,-58,-58,-58,
-58,-58,-57,-57,-57,-57,-57,-57,-56,-56,
-56,-56,-56,-55,-55,-55,-55,-55,-54,-54,
-54,-54,-54,-54,-53,-53,-53,-53,-53,-52,
-52,-52,-52,-52,-51,-51,-51,-51,-51,-51,
-50,-50,-50,-50,-50,-49,-49,-49,-49,-49,
-48,-48,-48,-48,-48,-48,-47,-47,-47,-47,
-47,-46,-46,-46,-46,-46,-45,-45,-45,-45,
-45,-45,-44,-44,-44,-44,-44,-43,-43,-43,
-43,-43,-42,-42,-42,-42,-42,-42,-41,-41,
-41,-41,-41,-40,-40,-40,-40,-40,-39,-39,
-39,-39,-39,-39,-38,-38,-38,-38,-38,-37,
-37,-37,-37,-37,-36,-36,-36,-36,-36,-36,
-35,-35,-35,-35,-35,-34,-34,-34,-34,-34,
-33,-33,-33,-33,-33,-33,-32,-32,-32,-32,
-32,-31,-31,-31,-31,-31,-30,-30,-30,-30,
-30,-30,-29,-29,-29,-29,-29,-28,-28,-28,
-28,-28,-27,-27,-27,-27,-27,-27,-26,-26,
-26,-26,-26,-25,-25,-25,-25,-25,-24,-24,
-24,-24,-24,-24,-23,-23,-23,-23,-23,-22,
-22,-22,-22,-22,-21,-21,-21,-21,-21,-21,
-20,-20,-20,-20,-20,-19,-19,-19,-19,-19,
-18,-18,-18,-18,-18,-18,-17,-17,-17,-17,
-17,-16,-16,-16,-16,-16,-15,-15,-15,-15,
-15,-15,-14,-14,-14,-14,-14,-13,-13,-13,
-13,-13,-12,-12,-12,-12,-12,-12,-11,-11,
-11,-11,-11,-10,-10,-10,-10,-10,-9,-9,
-9,-9,-9,-9,-8,-8,-8,-8,-8,-7,
-7,-7,-7,-7,-6,-6,-6,-6,-6,-6,
-5,-5,-5,-5,-5,-4,-4,-4,-4,-4,
-3,-3,-3,-3,-3,-3,-2,-2,-2,-2,
-2,-1,-1,-1,-1,-1,
0,0,0,0,
0,0,0,0,0,0,0,1,1,1,
1,1,2,2,2,2,2,3,3,3,
3,3,3,4,4,4,4,4,5,5,
5,5,5,6,6,6,6,6,6,7,
7,7,7,7,8,8,8,8,8,9,
9,9,9,9,9,10,10,10,10,10,
11,11,11,11,11,12,12,12,12,12,
12,13,13,13,13,13,14,14,14,14,
14,15,15,15,15,15,15,16,16,16,
16,16,17,17,17,17,17,18,18,18,
18,18,18,19,19,19,19,19,20,20,
20,20,20,21,21,21,21,21,21,22,
22,22,22,22,23,23,23,23,23,24,
24,24,24,24,24,25,25,25,25,25,
26,26,26,26,26,27,27,27,27,27,
27,28,28,28,28,28,29,29,29,29,
29,30,30,30,30,30,30,31,31,31,
31,31,32,32,32,32,32,33,33,33,
33,33,33,34,34,34,34,34,35,35,
35,35,35,36,36,36,36,36,36,37,
37,37,37,37,38,38,38,38,38,39,
39,39,39,39,39,40,40,40,40,40,
41,41,41,41,41,42,42,42,42,42,
42,43,43,43,43,43,44,44,44,44,
44,45,45,45,45,45,45,46,46,46,
46,46,47,47,47,47,47,48,48,48,
48,48,48,49,49,49,49,49,50,50,
50,50,50,51,51,51,51,51,51,52,
52,52,52,52,53,53,53,53,53,54,
54,54,54,54,54,55,55,55,55,55,
56,56,56,56,56,57,57,57,57,57,
57,58,58,58,58,58,59,59,59,59,
59,60,60,60,60,60,60,61,61,61,
61,61,62,62,62,62,62,63,63,63,
63,63,63,64,64,64,64,64,65,65,
65,65,65,66,66,66,66,66,66,67,
67,67,67,67,68,68,68,68,68,69,
69,69,69,69,69,70,70,70,70,70,
71,71,71,71,71,72,72,72,72,72,
72,73,73,73,73,73,74,74,74,74,
74,75,75,75,75,75,75,76,76,76,
76,76,77,77,77,77,77,78,78,78,
78,78,78,79,79,79,79,79,80,80,
80,80,80,81,81,81,81,81,81,82,
82,82,82,82,83,83,83,83,83,84,
84,84,84,84,84,85,85,85,85,85,
86,86,86,86,86,87,87,87,87,87,
87,88,88,88,88,88,89,89,89,89,
89,90,90,90,90,90,90,91,91,91,
91,91,92,92,92,92,92,93,93,93,
93,93,93,94,94,94,94,94,95,95,
95,95,95,96,96,
};
short floyd_steinberg_error3[] = {
-159,-159,-159,-158,-158,-158,-157,-157,-157,-156,
-156,-156,-155,-155,-155,-155,-154,-154,-154,-153,
-153,-153,-152,-152,-152,-151,-151,-151,-150,-150,
-150,-150,-149,-149,-149,-148,-148,-148,-147,-147,
-147,-146,-146,-146,-145,-145,-145,-145,-144,-144,
-144,-143,-143,-143,-142,-142,-142,-141,-141,-141,
-140,-140,-140,-140,-139,-139,-139,-138,-138,-138,
-137,-137,-137,-136,-136,-136,-135,-135,-135,-135,
-134,-134,-134,-133,-133,-133,-132,-132,-132,-131,
-131,-131,-130,-130,-130,-130,-129,-129,-129,-128,
-128,-128,-127,-127,-127,-126,-126,-126,-125,-125,
-125,-125,-124,-124,-124,-123,-123,-123,-122,-122,
-122,-121,-121,-121,-120,-120,-120,-120,-119,-119,
-119,-118,-118,-118,-117,-117,-117,-116,-116,-116,
-115,-115,-115,-115,-114,-114,-114,-113,-113,-113,
-112,-112,-112,-111,-111,-111,-110,-110,-110,-110,
-109,-109,-109,-108,-108,-108,-107,-107,-107,-106,
-106,-106,-105,-105,-105,-105,-104,-104,-104,-103,
-103,-103,-102,-102,-102,-101,-101,-101,-100,-100,
-100,-100,-99,-99,-99,-98,-98,-98,-97,-97,
-97,-96,-96,-96,-95,-95,-95,-95,-94,-94,
-94,-93,-93,-93,-92,-92,-92,-91,-91,-91,
-90,-90,-90,-90,-89,-89,-89,-88,-88,-88,
-87,-87,-87,-86,-86,-86,-85,-85,-85,-85,
-84,-84,-84,-83,-83,-83,-82,-82,-82,-81,
-81,-81,-80,-80,-80,-80,-79,-79,-79,-78,
-78,-78,-77,-77,-77,-76,-76,-76,-75,-75,
-75,-75,-74,-74,-74,-73,-73,-73,-72,-72,
-72,-71,-71,-71,-70,-70,-70,-70,-69,-69,
-69,-68,-68,-68,-67,-67,-67,-66,-66,-66,
-65,-65,-65,-65,-64,-64,-64,-63,-63,-63,
-62,-62,-62,-61,-61,-61,-60,-60,-60,-60,
-59,-59,-59,-58,-58,-58,-57,-57,-57,-56,
-56,-56,-55,-55,-55,-55,-54,-54,-54,-53,
-53,-53,-52,-52,-52,-51,-51,-51,-50,-50,
-50,-50,-49,-49,-49,-48,-48,-48,-47,-47,
-47,-46,-46,-46,-45,-45,-45,-45,-44,-44,
-44,-43,-43,-43,-42,-42,-42,-41,-41,-41,
-40,-40,-40,-40,-39,-39,-39,-38,-38,-38,
-37,-37,-37,-36,-36,-36,-35,-35,-35,-35,
-34,-34,-34,-33,-33,-33,-32,-32,-32,-31,
-31,-31,-30,-30,-30,-30,-29,-29,-29,-28,
-28,-28,-27,-27,-27,-26,-26,-26,-25,-25,
-25,-25,-24,-24,-24,-23,-23,-23,-22,-22,
-22,-21,-21,-21,-20,-20,-20,-20,-19,-19,
-19,-18,-18,-18,-17,-17,-17,-16,-16,-16,
-15,-15,-15,-15,-14,-14,-14,-13,-13,-13,
-12,-12,-12,-11,-11,-11,-10,-10,-10,-10,
-9,-9,-9,-8,-8,-8,-7,-7,-7,-6,
-6,-6,-5,-5,-5,-5,-4,-4,-4,-3,
-3,-3,-2,-2,-2,-1,-1,-1,
0,0,
0,0,0,0,0,1,1,1,2,2,
2,3,3,3,4,4,4,5,5,5,
5,6,6,6,7,7,7,8,8,8,
9,9,9,10,10,10,10,11,11,11,
12,12,12,13,13,13,14,14,14,15,
15,15,15,16,16,16,17,17,17,18,
18,18,19,19,19,20,20,20,20,21,
21,21,22,22,22,23,23,23,24,24,
24,25,25,25,25,26,26,26,27,27,
27,28,28,28,29,29,29,30,30,30,
30,31,31,31,32,32,32,33,33,33,
34,34,34,35,35,35,35,36,36,36,
37,37,37,38,38,38,39,39,39,40,
40,40,40,41,41,41,42,42,42,43,
43,43,44,44,44,45,45,45,45,46,
46,46,47,47,47,48,48,48,49,49,
49,50,50,50,50,51,51,51,52,52,
52,53,53,53,54,54,54,55,55,55,
55,56,56,56,57,57,57,58,58,58,
59,59,59,60,60,60,60,61,61,61,
62,62,62,63,63,63,64,64,64,65,
65,65,65,66,66,66,67,67,67,68,
68,68,69,69,69,70,70,70,70,71,
71,71,72,72,72,73,73,73,74,74,
74,75,75,75,75,76,76,76,77,77,
77,78,78,78,79,79,79,80,80,80,
80,81,81,81,82,82,82,83,83,83,
84,84,84,85,85,85,85,86,86,86,
87,87,87,88,88,88,89,89,89,90,
90,90,90,91,91,91,92,92,92,93,
93,93,94,94,94,95,95,95,95,96,
96,96,97,97,97,98,98,98,99,99,
99,100,100,100,100,101,101,101,102,102,
102,103,103,103,104,104,104,105,105,105,
105,106,106,106,107,107,107,108,108,108,
109,109,109,110,110,110,110,111,111,111,
112,112,112,113,113,113,114,114,114,115,
115,115,115,116,116,116,117,117,117,118,
118,118,119,119,119,120,120,120,120,121,
121,121,122,122,122,123,123,123,124,124,
124,125,125,125,125,126,126,126,127,127,
127,128,128,128,129,129,129,130,130,130,
130,131,131,131,132,132,132,133,133,133,
134,134,134,135,135,135,135,136,136,136,
137,137,137,138,138,138,139,139,139,140,
140,140,140,141,141,141,142,142,142,143,
143,143,144,144,144,145,145,145,145,146,
146,146,147,147,147,148,148,148,149,149,
149,150,150,150,150,151,151,151,152,152,
152,153,153,153,154,154,154,155,155,155,
155,156,156,156,157,157,157,158,158,158,
159,159,159,160,160,
};
short floyd_steinberg_error4[] = {
-34,-33,-33,-33,-33,-33,-34,-33,-32,-33,
-33,-33,-33,-33,-32,-31,-33,-32,-32,-32,
-32,-32,-33,-32,-31,-32,-32,-32,-32,-32,
-31,-30,-32,-31,-31,-31,-31,-31,-32,-31,
-30,-31,-31,-31,-31,-31,-30,-29,-31,-30,
-30,-30,-30,-30,-31,-30,-29,-30,-30,-30,
-30,-30,-29,-28,-30,-29,-29,-29,-29,-29,
-30,-29,-28,-29,-29,-29,-29,-29,-28,-27,
-29,-28,-28,-28,-28,-28,-29,-28,-27,-28,
-28,-28,-28,-28,-27,-26,-28,-27,-27,-27,
-27,-27,-28,-27,-26,-27,-27,-27,-27,-27,
-26,-25,-27,-26,-26,-26,-26,-26,-27,-26,
-25,-26,-26,-26,-26,-26,-25,-24,-26,-25,
-25,-25,-25,-25,-26,-25,-24,-25,-25,-25,
-25,-25,-24,-23,-25,-24,-24,-24,-24,-24,
-25,-24,-23,-24,-24,-24,-24,-24,-23,-22,
-24,-23,-23,-23,-23,-23,-24,-23,-22,-23,
-23,-23,-23,-23,-22,-21,-23,-22,-22,-22,
-22,-22,-23,-22,-21,-22,-22,-22,-22,-22,
-21,-20,-22,-21,-21,-21,-21,-21,-22,-21,
-20,-21,-21,-21,-21,-21,-20,-19,-21,-20,
-20,-20,-20,-20,-21,-20,-19,-20,-20,-20,
-20,-20,-19,-18,-20,-19,-19,-19,-19,-19,
-20,-19,-18,-19,-19,-19,-19,-19,-18,-17,
-19,-18,-18,-18,-18,-18,-19,-18,-17,-18,
-18,-18,-18,-18,-17,-16,-18,-17,-17,-17,
-17,-17,-18,-17,-16,-17,-17,-17,-17,-17,
-16,-15,-17,-16,-16,-16,-16,-16,-17,-16,
-15,-16,-16,-16,-16,-16,-15,-14,-16,-15,
-15,-15,-15,-15,-16,-15,-14,-15,-15,-15,
-15,-15,-14,-13,-15,-14,-14,-14,-14,-14,
-15,-14,-13,-14,-14,-14,-14,-14,-13,-12,
-14,-13,-13,-13,-13,-13,-14,-13,-12,-13,
-13,-13,-13,-13,-12,-11,-13,-12,-12,-12,
-12,-12,-13,-12,-11,-12,-12,-12,-12,-12,
-11,-10,-12,-11,-11,-11,-11,-11,-12,-11,
-10,-11,-11,-11,-11,-11,-10,-9,-11,-10,
-10,-10,-10,-10,-11,-10,-9,-10,-10,-10,
-10,-10,-9,-8,-10,-9,-9,-9,-9,-9,
-10,-9,-8,-9,-9,-9,-9,-9,-8,-7,
-9,-8,-8,-8,-8,-8,-9,-8,-7,-8,
-8,-8,-8,-8,-7,-6,-8,-7,-7,-7,
-7,-7,-8,-7,-6,-7,-7,-7,-7,-7,
-6,-5,-7,-6,-6,-6,-6,-6,-7,-6,
-5,-6,-6,-6,-6,-6,-5,-4,-6,-5,
-5,-5,-5,-5,-6,-5,-4,-5,-5,-5,
-5,-5,-4,-3,-5,-4,-4,-4,-4,-4,
-5,-4,-3,-4,-4,-4,-4,-4,-3,-2,
-4,-3,-3,-3,-3,-3,-4,-3,-2,-3,
-3,-3,-3,-3,-2,-1,-3,-2,-2,-2,
-2,-2,-3,-2,-1,-2,-2,-2,-2,-2,
-1,
0,1,2,2,2,2,2,1,2,
3,2,2,2,2,2,3,1,2,3,
3,3,3,3,2,3,4,3,3,3,
3,3,4,2,3,4,4,4,4,4,
3,4,5,4,4,4,4,4,5,3,
4,5,5,5,5,5,4,5,6,5,
5,5,5,5,6,4,5,6,6,6,
6,6,5,6,7,6,6,6,6,6,
7,5,6,7,7,7,7,7,6,7,
8,7,7,7,7,7,8,6,7,8,
8,8,8,8,7,8,9,8,8,8,
8,8,9,7,8,9,9,9,9,9,
8,9,10,9,9,9,9,9,10,8,
9,10,10,10,10,10,9,10,11,10,
10,10,10,10,11,9,10,11,11,11,
11,11,10,11,12,11,11,11,11,11,
12,10,11,12,12,12,12,12,11,12,
13,12,12,12,12,12,13,11,12,13,
13,13,13,13,12,13,14,13,13,13,
13,13,14,12,13,14,14,14,14,14,
13,14,15,14,14,14,14,14,15,13,
14,15,15,15,15,15,14,15,16,15,
15,15,15,15,16,14,15,16,16,16,
16,16,15,16,17,16,16,16,16,16,
17,15,16,17,17,17,17,17,16,17,
18,17,17,17,17,17,18,16,17,18,
18,18,18,18,17,18,19,18,18,18,
18,18,19,17,18,19,19,19,19,19,
18,19,20,19,19,19,19,19,20,18,
19,20,20,20,20,20,19,20,21,20,
20,20,20,20,21,19,20,21,21,21,
21,21,20,21,22,21,21,21,21,21,
22,20,21,22,22,22,22,22,21,22,
23,22,22,22,22,22,23,21,22,23,
23,23,23,23,22,23,24,23,23,23,
23,23,24,22,23,24,24,24,24,24,
23,24,25,24,24,24,24,24,25,23,
24,25,25,25,25,25,24,25,26,25,
25,25,25,25,26,24,25,26,26,26,
26,26,25,26,27,26,26,26,26,26,
27,25,26,27,27,27,27,27,26,27,
28,27,27,27,27,27,28,26,27,28,
28,28,28,28,27,28,29,28,28,28,
28,28,29,27,28,29,29,29,29,29,
28,29,30,29,29,29,29,29,30,28,
29,30,30,30,30,30,29,30,31,30,
30,30,30,30,31,29,30,31,31,31,
31,31,30,31,32,31,31,31,31,31,
32,30,31,32,32,32,32,32,31,32,
33,32,32,32,32,32,33,31,32,33,
33,33,33,33,32,33,34,33,33,33,
33,33,34,32,33,
};
#endif /* __FSDITHER_H__ */

View File

@@ -0,0 +1,958 @@
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "defines.h"
#include "hicolour.h"
#include "median.h"
#include "wu.h"
#include <common.h>
#include <options.h>
#include <files.h>
#include <image_info.h>
#include <logging.h>
#include <c_source.h>
#include <tile_dedupe.h>
/* Gameboy Hi-Colour Convertor */
/* Glen Cook */
/* Jeff Frohwein */
/* Rob Jones */
/*
This code is based on the code written by Jeff Frohwein. Jeff originally wrote a 128x128
Gameboy HiColour convertor and made the source publically available. The problem with
the original work, is that the output from the original code had a large white border
making the picture look framed.
The original code was then modified by another party to produce a full screen image, using
a fixed attribute block size of 3-2-3-2-3-2-3-2. The output from this modified code looked
great, but some pictures had artifacts, due to the fixed attribute size.
I then decided to modify the full screen code, to produce pictures with less artifacts, the
attribute blocks are not fixed, they can adapt their size based on the type of picture that
is being converted.
This program will step through every possible combination of attributes to find the best
possible solution.
The program gives the user the option of using fixed or adaptive attribute blocks, fixed
attribute blocks are much quicker to calculate, but the picture quality may not be perfect.
After creating a DOS version of this program, I then went ahead and wrote a windows interface
for it, to tidy it up, and also give me the chance to learn some windows programming. This is
my first windows program, so please be kind.
The best method for converting the pictures, is to use Adaptive method 3, although this can
take quite a bit longer to calculate than the fixed size calculations.
I believe that the new median cut method with dither produces the best results in general,
but the other quantisers can produce better results for other picture types.
I am releasing this program into the public domain, feel free to adapt it in anyway that you
deem fit. I you feel you have improved this program in anyway, drop me a line, and I will
incorperate the changes into newer versions. (GlenCook@hotmail.com)
*/
/* HISTORY */
// V1.0 - 27th March 2000 - First public release
// V1.1 - 30th March 2000 - Rob Jones added seperate thread for conversion process
// V1.2 - 8th April 2000 - Added other quantisation methods
// V1.4 - 2023 - Converted to cross platform console utility with PNG support (bbbbbr)
// Function prototypes
int br,bg,bb;
typedef struct
{
u8 p1;
u8 p2;
u8 FileType;
u8 p3[9];
u16 XSize;
u16 YSize;
u8 BitDepth;
u8 c1;
u8 data[160*BUF_HEIGHT][3];
} IMG_TYPE;
// u8 QR[BUF_HEIGHT][160][3];
u8 TileOffset[4]; // Offset into screen for attribute start
u8 TileWidth[4]; // No of character attributes width
u8 Pal[8][BUF_Y_REGION_COUNT_LR_RNDUP][28][3]; // Palettes for every other line
u8 IdealPal[8][BUF_Y_REGION_COUNT_LR_RNDUP][4][3]; // The best fit palette
u8 pic[160][BUF_HEIGHT][3]; // Original Picture
u8 pic2[160][BUF_HEIGHT][3]; // Output picture
u8 out[160][BUF_HEIGHT]; // Output data
u8 raw[2][160][BUF_HEIGHT][3]; // Original Picture Raw format.
// sourced from [0] = Normal , [1] = GB Color selected by ViewType
// TODO: delete?
s32 ViewType=0; // Type of view to show: 0 = Normal , 1 = GB Color
u8 Best[2][BUF_HEIGHT_IN_TILES_RNDUP]; // Best Attribute type to use
u8 LConversion; // Conversion type for left hand side of the screen
u8 RConversion; // Conversion type for right hand side of the screen
// HWND Ghdwnd; // Global window handle
u8 MapTileIDs[20][BUF_HEIGHT_IN_TILES]; // Attribute table for final render
u8 MapAttributes[20][BUF_HEIGHT_IN_TILES]; // Attribute table for final render
uint8_t TileSet[20 * BUF_HEIGHT_IN_TILES * TILE_SZ]; // Sequential Tileset Data in Game Boy 2bpp format
uint8_t TileSetDeduped[20 * BUF_HEIGHT_IN_TILES * TILE_SZ]; // Sequential Tileset Data in Game Boy 2bpp format
unsigned int TileCountDeduped;
// u8 OldLConv=0; // Conversion type
// u8 OldRConv=0;
uint8_t * pBuffer;
// u8 Message[2000];
s32 ConvertType; //=2;
u8 Data[160*BUF_HEIGHT*3]; // Gets used for quantizing regions. Maybe other things too?
u32 TempD;
s32 BestLine=0; // TODO: convert to local var
u32 BestQuantLine;
// RGBQUAD GBView; // converted to local vars
// Shim buffers for the former windows rendered images that were also used for some calculationss
// bmihsource.biWidth = 160;
// bmihsource.biHeight = BUF_HEIGHT;
// bmihsource.biPlanes = 1;
// bmihsource.biBitCount = 24;
static uint8_t Bitsdest[160 * BUF_HEIGHT * 3]; // TODO: RGBA 4 bytes per pixel?
static uint8_t Bitssource[160 * BUF_HEIGHT * 3];
//
static uint8_t *pBitsdest = Bitsdest;
uint8_t *pBitssource = Bitssource;
#define MAX_CONVERSION_TYPES 83
#define MAX_QUANTISER_TYPES 4
int image_y_min;
int image_y_max;
int image_height;
int y_region_count_left;
int y_region_count_right;
int y_region_count_lr_rndup;
int y_region_count_both_sides;
int y_height_in_tiles_left;
int y_height_in_tiles_right;
int y_height_in_tiles;
int y_height_in_tiles_lr_rndup;
static void PrepareTileSet(void);
static void PrepareMap(void);
static void PrepareAttributes(void);
static void DedupeTileset(void);
static void ExportPalettes(const char * fname_base);
static void ExportTileSet(const char * fname_base);
static void ExportMap(const char * fname_base);
static void ExportMapAttributes(const char * fname_base);
void hicolor_init(void) {
// Defaults
LConversion = 3; // Default Conversion (Fixed 3-2-3-2) Left Screen
RConversion = 3; // Default Conversion (Fixed 3-2-3-2) Righ Screen
ConvertType = 1; // Normal default is 1 ("Median cut - no dither")
}
static void hicolor_vars_prep(image_data * p_loaded_image) {
log_debug("hicolor_vars_prep()\n");
image_height = p_loaded_image->height;
image_y_min = 0;
image_y_max = p_loaded_image->height - 1;
// // Screen palette region updates are 80 pixels wide and 2 pixels tall
// // since palette 0-3 allocated to left side, 4-7 allocated to right side
// // and only 4 palettes are updated per scanline, so Left and Right alternate in gettig udpates
// // 73(L) & 72(R) for standard GB screen
// One extra region due to starting at -1 Y offset from screen grid, and so there is a last extra entry that "hangs off" the bottom of the screen
y_region_count_left = ((image_height / PAL_REGION_HEIGHT_PX) + 1);
y_region_count_right = (image_height / PAL_REGION_HEIGHT_PX);
// Use larger size[side] for rounded up amount
y_region_count_lr_rndup = (y_region_count_left);
y_region_count_both_sides = (y_region_count_left + y_region_count_right);
// 19(L) & 18(R) for standard GB Full screen height
// One extra region due to starting at -1 Y offset from screen grid, and so there is a last extra entry that "hangs off" the bottom of the screen
y_height_in_tiles_left = ((image_height / TILE_HEIGHT_PX) + 1);
y_height_in_tiles_right = (image_height / TILE_HEIGHT_PX);
y_height_in_tiles = (image_height / TILE_HEIGHT_PX);
// Use larger size[side] for rounded up amount
y_height_in_tiles_lr_rndup = (y_height_in_tiles_left);
}
void hicolor_set_convert_left_pattern(uint8_t new_value) {
// IDC_CONVERTLEFT
LConversion = new_value;
log_verbose("HiColor: Left pattern set to %d\n", new_value);
}
void hicolor_set_convert_right_pattern(uint8_t new_value) {
// IDC_CONVERTRIGHT
RConversion = new_value;
log_verbose("HiColor: Right pattern set to %d\n", new_value);
}
void hicolor_set_type(uint8_t new_value) {
// IDC_CONVERTTYPE
ConvertType = new_value;
log_verbose("HiColor: Convert type set to %d\n", new_value);
}
///////////////////////////////////
// Equivalent of former file loading
static void hicolor_image_import(image_data * p_loaded_image) {
log_debug("hicolor_image_import()\n");
// TODO: input guarding
// TODO: deduplicate some of the array copying around
uint8_t * p_input_img = p_loaded_image->p_img_data;
for (int y=0; y< image_height; y++) {
for (int x=0; x< 160; x++) {
// Clamp to CGB max R/G/B value in RGB 888 mode (31u << 3)
// png_image[].rgb -> pic2[].rgb -> pBitssource[].bgr??
pic2[x][y][0] = (p_input_img[RGB_RED] & 0xf8u);
pic2[x][y][1] = (p_input_img[RGB_GREEN] & 0xf8u);
pic2[x][y][2] = (p_input_img[RGB_BLUE] & 0xf8u);
p_input_img += RGB_24SZ; // Move to next pixel of source image
}
}
// TODO: Eventually clean up data pathway to remove some vestigial former display rendering buffers
// It's convoluted, but pBitssource & pBitsdest are used for:
// - display as windows DIBs (formerly)
// - and for some calculations at the end of ConvertRegions()
for (int y=0; y<image_height; y++) {
for (int x=0; x<160; x++) {
for (int z=0; z<3; z++) {
// TODO: (2-z) seems to be swapping RGB for BGR?
*(pBitssource+(image_y_max-y)*3*160+x*3+z) = pic2[x][y][2-z]; // Invert the dib, cos windows likes it like that !!
}
}
}
}
// TODO: fix
// TODO: Operates on RGB data in pic[] copied from RGB data in pic2
static void hicolor_convert(void) {
log_debug("hicolor_convert()\n");
for(int x=0; x<160; x++)
{
for(int y=0; y<image_height; y++)
{
pic[x][y][0] = pic2[x][y][0];
pic[x][y][1] = pic2[x][y][1];
pic[x][y][2] = pic2[x][y][2];
for(int i=0; i<3; i++)
{
*(Data + y*160*3+x*3+i) = pic[x][y][i];
}
}
}
ConvertToHiColor(ConvertType-1);
}
static void hicolor_save(const char * fname_base) {
// Default tile count to non-deduplicated number
int tile_count = y_height_in_tiles * (160 / TILE_WIDTH_PX);
log_debug("hicolor_save()\n");
PrepareTileSet();
PrepareMap();
PrepareAttributes();
if (opt_get_tile_dedupe()) {
DedupeTileset();
tile_count = TileCountDeduped;
}
ExportTileSet(fname_base);
ExportPalettes(fname_base);
ExportMap(fname_base);
ExportMapAttributes(fname_base);
if (opt_get_c_file_output()) {
file_c_output_write(fname_base, opt_get_bank_num(), tile_count, y_height_in_tiles);
}
}
// Currently expects width x height x 3(RGB888)
void hicolor_process_image(image_data * p_loaded_image, const char * fname_base) {
log_debug("hicolor_process_image(), fname_base: \"%s\"\n", fname_base);
hicolor_vars_prep(p_loaded_image);
hicolor_image_import(p_loaded_image);
hicolor_convert();
hicolor_save(fname_base);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void DedupeTileset(void)
{
unsigned int map_tile_id;
uint8_t new_tile_id, new_attribs;
TileCountDeduped = 0;
// Traverse all tiles in the image/map
for (int mapy = 0; mapy < y_height_in_tiles; mapy++) {
for (int mapx = 0; mapx < 20; mapx++) {
map_tile_id = MapTileIDs[mapx][mapy];
map_tile_id += (MapAttributes[mapx][mapy] & CGB_ATTR_TILES_BANK) ? CGB_TILES_START_BANK_1 : CGB_TILES_START_BANK_0;
if (!tileset_find_matching_tile(&TileSet[map_tile_id * TILE_SZ], &TileSetDeduped[0], TileCountDeduped, &new_tile_id, &new_attribs)) {
// If no match, copy tile to new tile set and save new index for remapping
new_tile_id = TileCountDeduped;
new_attribs = (TileCountDeduped < CGB_TILES_START_BANK_1) ? CGB_ATTR_TILES_BANK_0 : CGB_ATTR_TILES_BANK_1;
memcpy(&TileSetDeduped[TileCountDeduped * TILE_SZ], &TileSet[map_tile_id * TILE_SZ], TILE_SZ);
TileCountDeduped++;
}
// Update map data and attributes to new index
// Mask out everything except palettes and then apply the new attribs (bank, vflip, hflip)
MapTileIDs[mapx][mapy] = new_tile_id;
MapAttributes[mapx][mapy] = (MapAttributes[mapx][mapy] & CGB_ATTR_PALLETES_ONLY) | new_attribs;
}
}
log_verbose("DedupeTileset(): Reduced tiles from %d (%d bytes) to %d (%d bytes) = %d bytes saved. %%%d of original size\n",
map_tile_id + 1, (map_tile_id + 1) * TILE_SZ, TileCountDeduped, TileCountDeduped * TILE_SZ,
((map_tile_id + 1) * TILE_SZ) - (TileCountDeduped * TILE_SZ), (TileCountDeduped * 100) / (map_tile_id + 1));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void PrepareTileSet(void) {
uint32_t byteWritten;
u32 x, y;
u8 c1,c2;
u8 dx,dy;
u8 c;
uint8_t * p_buf = TileSet;
// Write out tilemap data, Left -> Right, Top -> Bottom, 16 bytes per tile
for (y=0; y<image_height; y=y+8)
{
for (x=0; x<160; x=x+8)
{
for (dy=0; dy<8; dy++)
{
c1 = 0;
c2 = 0;
for (dx=0; dx<8; dx++)
{
c1 = (u8)(c1 << 1);
c2 = (u8)(c2 << 1);
c = out[x+dx][y+dy];
if (c & 2) c1++;
if (c & 1) c2++;
}
*p_buf++ = c2;
*p_buf++ = c1;
}
}
}
}
static void PrepareMap(void) {
// Set up export Map Tile IDs
// Note: The indexes are clipped to 0-255 (instead of 0-512),
// the attribute tile index+256 bit is auto-calculated in the attribute map in PrepareAttributes()
int tile_id = 0;
for (int mapy = 0; mapy < y_height_in_tiles; mapy++) {
for (int mapx = 0; mapx < 20; mapx++) {
MapTileIDs[mapx][mapy] = (uint8_t)tile_id;
tile_id++;
}
}
}
static void PrepareAttributes(void) {
// Set up the Map Attributes table
unsigned int tile_id = 0;
for(int MastY=0;MastY<y_height_in_tiles_right;MastY++)
{
for(int MastX=0;MastX<2;MastX++)
{
int Line=Best[MastX][MastY];
int width=0;
for(int i=0;i<4;i++)
{
TileOffset[i]=width;
TileWidth[i]=SplitData[Line][i];
width+=TileWidth[i];
}
for(int x=0;x<4;x++) {
for(int z=TileOffset[x];z<(TileOffset[x]+TileWidth[x]);z++) {
MapAttributes[MastX*10+z][MastY]=x+MastX*4;
// Mask in second CGB Tile Bank flag if tile index is over 256 tiles
if (tile_id++ >= 256)
MapAttributes[MastX*10+z][MastY] |= CGB_ATTR_TILES_BANK_1;
}
}
}
}
}
static void ExportTileSet(const char * fname_base)
{
char filename[MAX_PATH*2];
strcpy(filename, fname_base);
strcat(filename, ".til");
log_verbose("Writing Tile Patterns to: %s\n", filename);
if (opt_get_tile_dedupe()) {
int outbuf_sz_tiles = TileCountDeduped * TILE_SZ;
if (!file_write_from_buffer(filename, TileSetDeduped, outbuf_sz_tiles))
set_exit_error();
} else {
int outbuf_sz_tiles = ((image_height / TILE_HEIGHT_PX) * (160 / TILE_WIDTH_PX) * 8 * 2);
if (!file_write_from_buffer(filename, TileSet, outbuf_sz_tiles))
set_exit_error();
}
}
static void ExportPalettes(const char * fname_base)
{
char filename[MAX_PATH * 2];
uint32_t byteWritten;
uint8_t tmpByte;
s32 i, j, k;
s32 r,g,b,v;
strcpy(filename, fname_base);
strcat(filename, ".pal");
log_verbose("Writing Palette to: %s\n", filename);
int outbuf_sz_pals = (((y_region_count_both_sides) * 4 * 4 * 2) + 1);
uint8_t output_buf[outbuf_sz_pals];
uint8_t * p_buf = output_buf;
for (i = 0; i < (y_region_count_both_sides); i++) // Number of palette sets (left side updates + right side updates)
{
for (j = 0; j < 4; j++) // Each palette in the set
{
for(k=0; k<4;k++) // Each color in the palette
{
r = IdealPal[(i%2)*4+j][i/2][k][0];
g = IdealPal[(i%2)*4+j][i/2][k][1];
b = IdealPal[(i%2)*4+j][i/2][k][2];
// TODO: Converting to BGR555 probably
v = ((b/8)*32*32) + ((g/8)*32) + (r/8);
// 2 bytes per color
*p_buf++ = (u8)(v & 255);
*p_buf++ = (u8)(v / 256);
}
}
}
// TODO: What is this and why? :)
*p_buf++ = 0x2d;
if (!file_write_from_buffer(filename, output_buf, outbuf_sz_pals))
set_exit_error();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void ExportMap(const char * fname_base)
{
char filename[MAX_PATH*2];
strcpy(filename, fname_base);
strcat(filename, ".map");
log_verbose("Writing Tile Map to: %s\n", filename);
int outbuf_sz_map = (20 * y_height_in_tiles);
uint8_t output_buf_map[outbuf_sz_map];
int tile_id = 0;
for (int y = 0; y < y_height_in_tiles; y++) {
for (int x = 0; x < 20; x++) {
uint8_t tile_num = MapTileIDs[x][y];
// This needs to happen here, after optional deduplication stage
// since that may rewrite the tile pattern order and indexes
if (opt_get_map_tile_order() != OPT_MAP_TILE_SEQUENTIAL_ORDER) // implied: OPT_MAP_TILE_ORDER_BY_VRAM_ID
tile_num = ((tile_num < 128) ? (tile_num) + 128 : (tile_num) - 128); // Previous ordering that was: 128 -> 255 -> 0 -> 127
output_buf_map[tile_id] = tile_num;
tile_id++;
}
}
if (!file_write_from_buffer(filename, output_buf_map, outbuf_sz_map))
set_exit_error();
}
static void ExportMapAttributes(const char * fname_base)
{
char filename[MAX_PATH*2];
strcpy(filename, fname_base);
strcat(filename, ".atr");
log_verbose("Writing Attribute Map to: %s\n", filename);
int outbuf_sz_map = (20 * y_height_in_tiles);
uint8_t output_buf_map[outbuf_sz_map];
int tile_id = 0;
for (int y = 0; y < y_height_in_tiles; y++)
{
for (int x = 0; x < 20; x++)
{
output_buf_map[tile_id++] = MapAttributes[x][y];
}
}
if (!file_write_from_buffer(filename, output_buf_map, outbuf_sz_map))
set_exit_error();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// This section of code is used to convert an RGB (pc) triplet into a RGB (gameboy)
// triplet. This section of code was kindly donated by Brett Bibby (GameBrains).
uint8_t intensity[32] =
{
0x00,0x10,0x20,0x30,0x40,0x50,0x5e,0x6c,0x7a,0x88,0x94,0xa0,0xae,0xb7,0xbf,0xc6,
0xce,0xd3,0xd9,0xdf,0xe3,0xe7,0xeb,0xef,0xf3,0xf6,0xf9,0xfb,0xfd,0xfe,0xff,0xff
};
unsigned char influence[3][3] =
{
{16,4,4},
{8,16,8},
{0,8,16}
};
RGBQUAD translate(uint8_t rgb[3])
{
RGBQUAD color;
uint8_t tmp[3];
uint8_t m[3][3];
uint8_t i,j;
for (i=0;i<3;i++)
for (j=0;j<3;j++)
m[i][j] = (intensity[rgb[i]>>3]*influence[i][j]) >> 5;
for (i=0;i<3;i++)
{
if (m[0][i]>m[1][i])
{
j=m[0][i];
m[0][i]=m[1][i];
m[1][i]=j;
}
if (m[1][i]>m[2][i])
{
j=m[1][i];
m[1][i]=m[2][i];
m[2][i]=j;
}
if (m[0][i]>m[1][i])
{
j=m[0][i];
m[0][i]=m[1][i];
m[1][i]=j;
}
tmp[i]=(((m[0][i]+m[1][i]*2+m[2][i]*4)*5) >> 4)+32;
}
color.rgbRed = tmp[0];
color.rgbGreen = tmp[1];
color.rgbBlue = tmp[2];
return color;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Data table containing all of the possible combinations of attribute blocks
// for one side of the screen.
// The higher the adaptive level, the more combinations of attributes are tested.
u8 SplitData[80][4]=
{
{3,2,3,2},{2,3,2,3},{2,2,3,3},{2,3,3,2},{3,2,2,3},{3,3,2,2},{4,2,2,2},{2,2,2,4},{2,2,4,2},{2,4,2,2},{1,1,2,6},
{1,1,3,5},{1,1,4,4},{1,1,5,3},{1,1,6,2},{1,2,1,6},{1,2,2,5},{1,2,3,4},{1,2,4,3},{1,2,5,2},{1,2,6,1},{1,3,1,5},
{1,3,2,4},{1,3,3,3},{1,3,4,2},{1,3,5,1},{1,4,1,4},{1,4,2,3},{1,4,3,2},{1,4,4,1},{1,5,1,3},{1,5,2,2},{1,5,3,1},
{1,6,1,2},{1,6,2,1},{2,1,1,6},{2,1,2,5},{2,1,3,4},{2,1,4,3},{2,1,5,2},{2,1,6,1},{2,2,1,5},{2,2,5,1},{2,3,1,4},
{2,3,4,1},{2,4,1,3},{2,4,3,1},{2,5,1,2},{2,5,2,1},{2,6,1,1},{3,1,1,5},{3,1,2,4},{3,1,3,3},{3,1,4,2},{3,1,5,1},
{3,2,1,4},{3,2,4,1},{3,3,1,3},{3,3,3,1},{3,4,1,2},{3,4,2,1},{3,5,1,1},{4,1,1,4},{4,1,2,3},{4,1,3,2},{4,1,4,1},
{4,2,1,3},{4,2,3,1},{4,3,1,2},{4,3,2,1},{4,4,1,1},{5,1,1,3},{5,1,2,2},{5,1,3,1},{5,2,1,2},{5,2,2,1},{5,3,1,1},
{6,1,1,2},{6,1,2,1},{6,2,1,1}
};
unsigned int ImageRating(u8 *src, u8 *dest, int StartX, int StartY, int Width, int Height)
{
log_debug("ImageRating()\n");
unsigned int tot;
int x,y;
unsigned int accum=0;
int scradd;
for(y=StartY;y<(StartY+Height);y++)
{
for(x=StartX;x<(StartX+Width);x++)
{
scradd=(image_y_max-y)*(160*3)+x*3;
tot=(*(src+scradd)-*(dest+scradd)) * (*(src+scradd)-*(dest+scradd));
tot+=(*(src+scradd+1)-*(dest+scradd+1)) * (*(src+scradd+1)-*(dest+scradd+1));
tot+=(*(src+scradd+2)-*(dest+scradd+2)) * (*(src+scradd+2)-*(dest+scradd+2));
accum+=tot;
}
}
return accum;
}
// TODO: rename to something that aligns with other convert functions
void ConvertToHiColor(int ConvertType)
{
log_debug("ConvertToHiColor()\n");
int res;
int x,y,z,i;
int StartSplit=0;
int NumSplit=1;
int Steps;
int MastX,MastY;
int Line;
int width;
unsigned int tile_id;
switch(LConversion)
{
case 0:
StartSplit=0;
NumSplit=6;
Steps=504;
break;
case 1:
StartSplit=0;
NumSplit=10;
Steps=792;
break;
case 2:
StartSplit=0;
NumSplit=80;
Steps=5832;
break;
default:
StartSplit=LConversion-3;
NumSplit=1;
Steps=image_height;
break;
}
switch(RConversion)
{
case 0:
Steps+=504;
break;
case 1:
Steps+=792;
break;
case 2:
Steps+=5832;
break;
default:
Steps+=image_height;
break;
}
// Convert left side with one extra tile of height to fix
// the glitching where the last scanline on left bottom region
// lacks tile and palette data
res=ConvertRegions(0,1,0,y_height_in_tiles_left,StartSplit,NumSplit,ConvertType); // Step through all options
ConvertRegions(0,1,0,y_height_in_tiles_left,res,1,ConvertType);
// Formerly: for(y=0;y<189;y++)
// Treating it as a typo (intended a "18") since 189 would be out of bounds for the original array
for(y=0;y<y_height_in_tiles_left;y++)
Best[0][y]=res;
switch(RConversion)
{
case 0:
StartSplit=0;
NumSplit=6;
break;
case 1:
StartSplit=0;
NumSplit=10;
break;
case 2:
StartSplit=0;
NumSplit=80;
break;
default:
StartSplit=RConversion-3;
NumSplit=1;
break;
}
for(y=0;y<y_height_in_tiles_right;y++)
{
res=ConvertRegions(1,1,y,1,StartSplit,NumSplit,ConvertType); // Step through all options
ConvertRegions(1,1,y,1,res,1,ConvertType);
Best[1][y]=res;
}
// TODO: fix me -> pBitsdest being used in conversion process
for(y=0;y<image_height;y++)
{
for(x=0;x<160;x++)
{
raw[0][x][y][0] = *(pBitsdest+(image_y_max-y)*3*160+x*3+2);
raw[0][x][y][1] = *(pBitsdest+(image_y_max-y)*3*160+x*3+1);
raw[0][x][y][2] = *(pBitsdest+(image_y_max-y)*3*160+x*3);
RGBQUAD GBView=translate(raw[0][x][y]);
raw[1][x][y][0] = GBView.rgbRed;
raw[1][x][y][1] = GBView.rgbGreen;
raw[1][x][y][2] = GBView.rgbBlue;
}
}
log_progress("\n");
}
// Start X = 0 for Left / 1 for Right
// Width = 1 for half screen 2 = full screen
// StartY = 0 - 17 : Starting attribute block
// Height = Number of attribute blocks to check / process
int ConvertRegions(int StartX, int Width, int StartY, int Height, int StartJ, int FinishJ, int ConvertType)
{
log_debug("ConvertRegions()\n");
u32 Accum,width,x1,ts,tw,y2,x2,y_offset;
s32 x,y;
s32 i,j;
u8 col;
BestQuantLine=0xffffffff;
for(x=StartX;x<(StartX+Width);x++)
{
// Left side of screen is offset by -1 Y
// (Left side calcs hang off top and bottom of screen
// due to Left/Right palette update interleaving)
if (x == CONV_SIDE_LEFT)
y_offset = CONV_Y_SHIFT_UP_1;
else
y_offset = CONV_Y_SHIFT_NO;
for(j=StartJ;j<(StartJ+FinishJ);j++)
{
Accum=0;
width=0;
for(i=0;i<4;i++)
{
TileOffset[i]=width;
TileWidth[i]=SplitData[j][i]<<3;
width+=TileWidth[i];
}
for(y=StartY*4;y<(StartY+Height)*4;y++)
{
log_progress(".");
for(x1=0;x1<4;x1++)
{
ts=TileOffset[x1];
tw=TileWidth[x1];
for(y2=0;y2<2;y2++)
{
// Skip if Y line is outside image borders (prevents buffer overflow)
// (Left side calcs hang off top and bottom of screen
// due to Left/Right palette update interleaving)
s32 y_line = (y*2+y2-y_offset);
if ((y_line < image_y_min) || (y_line > image_y_max)) continue;
for(x2=0;x2<tw;x2++)
{
// i is iterating over r/g/b slots for the current pixel
for(i=0;i<3;i++)
{
*(Data+(tw*3*y2)+x2*3+i) = pic[x*80+ts+x2][y*2+y2-y_offset][i];
}
}
}
switch(ConvertType)
{
case 0:
to_indexed(Data,4,0,TileWidth[x1],2); // Median Reduction No Dither
break;
case 1:
to_indexed(Data,4,1,TileWidth[x1],2); // Median Reduction With Dither
break;
case 2:
wuReduce(Data,4,TileWidth[x1]*2); // Wu Reduction
break;
}
for(y2=0;y2<4;y2++)
{
// Skip if Y is outside allocated Palette size (prevents buffer overflow)
// (Left side calcs hang off top and bottom of screen
// due to Left/Right palette update interleaving)
if (y >= y_region_count_lr_rndup) continue;
IdealPal[x*4+x1][y][y2][0]=QuantizedPalette[y2][2];
IdealPal[x*4+x1][y][y2][1]=QuantizedPalette[y2][1];
IdealPal[x*4+x1][y][y2][2]=QuantizedPalette[y2][0];
}
for(y2=0;y2<2;y2++)
{
for(x2=0;x2<tw;x2++)
{
// Skip if Y line is outside image borders (prevents buffer overflow)
// since Left side calcs hang off top and bottom of image/screen
s32 y_line = (y*2+y2-y_offset);
if ((y_line < image_y_min) || (y_line > image_y_max)) continue;
col=Picture256[y2*tw+x2];
out[x*80+x2+ts][y*2+y2-y_offset]=col;
for(i=0;i<3;i++)
{
*(pBitsdest+(image_y_max-(y*2+y2-y_offset))*3*160+(x*80+ts+x2)*3+i)=QuantizedPalette[col][i];
}
}
}
}
}
TempD=ImageRating(pBitssource,pBitsdest,StartX*80,StartY*8,Width*80,Height*8);
if(TempD<BestQuantLine)
{
BestLine=j;
BestQuantLine=TempD;
}
}
}
return BestLine;
}

View File

@@ -0,0 +1,63 @@
#ifndef __hicolour_h__
#define __hicolour_h__
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <defines.h>
#include <image_info.h>
#include <logging.h>
// For displaying available conversion patterns to the user
#define HELP_CONV_PATTERN_STR \
"Available conversion palette/attribute pattern widths for -cL:N and -cR:N\n" \
"\n" \
"- Default for Left and Right is 3\n" \
"- Fixed pattern widths are faster but less optimal than adaptive\n" \
"- Adaptive pattern width speeds are 1: fastest, 2: medium, 3: slowest (slower = higher quality)\n" \
"\n" \
" 0: Adaptive-1 1: Adaptive-2 2: Adaptive-3 3: 3-2-3-2 4: 2-3-2-3 \n" \
" 5: 2-2-3-3 6: 2-3-3-2 7: 3-2-2-3 8: 3-3-2-2 9: 4-2-2-2 \n" \
" 10: 2-2-2-4 11: 2-2-4-2 12: 2-4-2-2 13: 1-1-2-6 14: 1-1-3-5 \n" \
" 15: 1-1-4-4 16: 1-1-5-3 17: 1-1-6-2 18: 1-2-1-6 19: 1-2-2-5 \n" \
" 20: 1-2-3-4 21: 1-2-4-3 22: 1-2-5-2 23: 1-2-6-1 24: 1-3-1-5 \n" \
" 25: 1-3-2-4 26: 1-3-3-3 27: 1-3-4-2 28: 1-3-5-1 29: 1-4-1-4 \n" \
" 30: 1-4-2-3 31: 1-4-3-2 32: 1-4-4-1 33: 1-5-1-3 34: 1-5-2-2 \n" \
" 35: 1-5-3-1 36: 1-6-1-2 37: 1-6-2-1 38: 2-1-1-6 39: 2-1-2-5 \n" \
" 40: 2-1-3-4 41: 2,1,4,3 42: 2-1-5-2 43: 2-1-6-1 44: 2-2-1-5 \n" \
" 45: 2-2-5-1 46: 2-3-1-4 47: 2-3-4-1 48: 2-4-1-3 49: 2-4-3-1 \n" \
" 50: 2-5-1-2 51: 2-5-2-1 52: 2-6-1-1 53: 3-1-1-5 54: 3-1-2-4 \n" \
" 55: 3-1-3-3 56: 3-1-4-2 57: 3-1-5-1 58: 3-2-1-4 59: 3-2-4-1 \n" \
" 60: 3-3-1-3 61: 3-3-3-1 62: 3-4-1-2 63: 3-4-2-1 64: 3-5-1-1 \n" \
" 65: 4-1-1-4 66: 4-1-2-3 67: 4-1-3-2 68: 4-1-4-1 69: 4-2-1-3 \n" \
" 70: 4-2-3-1 71: 4-3-1-2 72: 4-3-2-1 73: 4-4-1-1 74: 5-1-1-3 \n" \
" 75: 5-1-2-2 76: 5-1-3-1 77: 5-2-1-2 78: 5-2-2-1 79: 5-3-1-1 \n" \
" 80: 6-1-1-2 81: 6-1-2-1 82: 6-2-1-1 \n"
extern u8 TileOffset[4]; // Offset into screen for attribute start
extern u8 TileWidth[4]; // No of character attributes width
extern u8 SplitData[80][4];
extern RGBQUAD GBView;
void hicolor_init(void);
void hicolor_set_type(uint8_t new_value);
void hicolor_set_convert_left_pattern(uint8_t new_value);
void hicolor_set_convert_right_pattern(uint8_t new_value);
void hicolor_process_image(image_data * p_decoded_image, const char * fname);
/////////////////////////////////////////////////////////////////////////////
RGBQUAD translate(uint8_t rgb[3]);
unsigned int ImageRating(u8 *src, u8 *dest, int StartX, int StartY, int Width, int Height);
void ConvertToHiColor(int ConvertType);
int ConvertRegions(int StartX, int Width, int StartY, int Height, int StartJ, int FinishJ, int ConvertType);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,74 @@
#ifndef __median_h__
#define __median_h__
#define MAXNUMCOLORS 256
#define PRECISION_R 5
#define PRECISION_G 5
#define PRECISION_B 5
#define R_SCALE << 1
#define G_SCALE * 3
#define B_SCALE
#define HIST_R_ELEMS (1<<PRECISION_R)
#define HIST_G_ELEMS (1<<PRECISION_G)
#define HIST_B_ELEMS (1<<PRECISION_B)
#define MR HIST_G_ELEMS*HIST_B_ELEMS
#define MG HIST_B_ELEMS
#define BITS_IN_SAMPLE 8
#define R_SHIFT (BITS_IN_SAMPLE - PRECISION_R)
#define G_SHIFT (BITS_IN_SAMPLE - PRECISION_G)
#define B_SHIFT (BITS_IN_SAMPLE - PRECISION_B)
typedef struct _Color Color;
typedef struct _QuantizeObj QuantizeObj;
typedef void (*Pass_Func) (QuantizeObj *, unsigned char *, unsigned char *, long, long);
typedef unsigned long ColorFreq;
typedef ColorFreq *Histogram;
struct _Color
{
int red;
int green;
int blue;
};
typedef struct
{
int Rmin, Rmax; // The bounds of the box (inclusive); expressed as histogram indexes
int Gmin, Gmax;
int Bmin, Bmax;
int volume; // The volume (actually 2-norm) of the box
long colorcount; //The number of nonzero histogram cells within this box */
} mbox, *boxptr;
extern u8 Picture256[160*BUF_HEIGHT*3];
extern u8 QuantizedPalette[256][3];
void zero_histogram_rgb(void);
boxptr find_biggest_color_pop(boxptr boxlist,s32 numboxes);
boxptr find_biggest_volume(boxptr boxlist,s32 numboxes);
void update_box_rgb(boxptr boxp);
s32 median_cut_rgb(boxptr boxlist,s32 numboxes);
void compute_color_rgb(boxptr boxp,s32 icolor);
s32 find_nearby_colors(s32 minR,s32 minG,s32 minB,s32 colorlist[]);
void find_best_colors(s32 minR, s32 minG, s32 minB,s32 numcolors,s32 colorlist[],s32 bestcolor[]);
void fill_inverse_cmap_rgb(s32 R, s32 G, s32 B);
void median_cut_pass1_rgb(u8 *src,u8 *dest,s32 width,s32 height);
s32 *init_error_limit(void);
void to_indexed(u8 *input,s32 ncolors,s32 dither,s32 width,s32 height);
#endif

View File

@@ -0,0 +1,30 @@
#ifndef __WU_H__
#define __WU_H__
struct box
{
s32 r0; /* min value, exclusive */
s32 r1; /* max value, inclusive */
s32 g0;
s32 g1;
s32 b0;
s32 b1;
s32 vol;
};
#define BOX 33
void Hist3d(s32 *vwt,s32 *vmr,s32 *vmg,s32 *vmb, float *m_2);
void Momt3d(s32 *vwt, s32 *vmr, s32 *vmg, s32 *vmb, float *m_2);
s32 Vol(struct box * cube, s32 mmt[BOX][BOX][BOX]);
s32 Bottom(struct box * cube, u8 dir, s32 mmt[BOX][BOX][BOX]);
s32 Top(struct box * cube, u8 dir, s32 pos, s32 mmt[BOX][BOX][BOX]);
float Var(struct box * cube);
float Maximize(struct box *cube, u8 dir, s32 first, s32 last, s32 *cut, s32 whole_r, s32 whole_g, s32 whole_b, s32 whole_w);
s32 Cut(struct box * set1, struct box * set2);
void Mark(struct box *cube, s32 label, u8 *tag);
s32 wuReduce(u8 *RGBpic, s32 numcolors, s32 picsize);
#endif

View File

@@ -0,0 +1,53 @@
//
// image_info.h
//
#include <stdint.h>
#ifndef IMAGE_INFO_HEADER
#define IMAGE_INFO_HEADER
#define RGBA_32SZ 4 // 4 bytes
#define RGBA_RED 0
#define RGBA_GREEN 1
#define RGBA_BLUE 2
#define RGBA_ALPHA 3
#define RGB_24SZ 3 // 3 bytes
#define RGB_RED 0
#define RGB_GREEN 1
#define RGB_BLUE 2
#define BGRX_BLUE 0
#define BGRX_GREEN 1
#define BGRX_RED 2
#define MODE_8_BIT_INDEXED ( 8 / 8)
#define MODE_8_BIT_INDEXED_ALPHA (16 / 8)
#define MODE_24_BIT_RGB (24 / 8)
#define MODE_32_BIT_RGBA (32 / 8)
#define COLOR_DATA_PAL_MAX_COUNT 256 // 255 Colors max for indexed
#define COLOR_DATA_BYTES_PER_COLOR 3 // RGB 1 byte per color
#define COLOR_DATA_PAL_SIZE COLOR_DATA_PAL_MAX_COUNT * COLOR_DATA_BYTES_PER_COLOR
#define USER_PAL_MAX_COLORS 32
typedef struct {
uint8_t bytes_per_pixel;
// uint16_t width;
// uint16_t height;
unsigned int width;
unsigned int height;
uint32_t size; // size in bytes
uint8_t * p_img_data;
uint16_t tile_width; // should be even multiple of width
uint16_t tile_height; // should be even multiple of height
uint16_t palette_tile_width; // should be even multiple of width
uint16_t palette_tile_height; // should be even multiple of height
} image_data;
#endif

View File

@@ -0,0 +1,114 @@
// image_load.c
//
// Handles loading PNG images and reformatting them to a usable state
//
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "common.h"
#include "logging.h"
#include "image_info.h"
#include "options.h"
#include <defines.h>
#include <lodepng.h>
static bool image_validate(image_data *);
static bool image_load_png(image_data *, const char *);
// Validates incoming image properties
// Return: true if success
static bool image_validate(image_data * p_decoded_image) {
if (p_decoded_image->width != VALIDATE_WIDTH) {
log_error("Error: Image width is %d, must be %d\n", p_decoded_image->width, VALIDATE_WIDTH);
return false;
}
// TODO: There is a soft upper bound of 25 since:
// Width is currently fixed at 160 pixels (20 tiles)
// 20 tiles wide x 25 tiles high = 500 tiles
// Which is the highest possible within the 512 available on the CGB
// without reloading the tiles and maps using an offset
if ((p_decoded_image->height == 0) || (p_decoded_image->height > VALIDATE_HEIGHT)) {
log_error("Error: Image height is %d, must be between 0 and %d\n", p_decoded_image->height, VALIDATE_HEIGHT);
return false;
}
if ((p_decoded_image->height % 8) != 0u) {
log_verbose("Error: Image height must be a multiple of 8, %d is not\n", p_decoded_image->height);
return false;
}
if ((p_decoded_image->width % 8) != 0u) {
log_verbose("Error: Image width must be a multiple of 8, %d is not\n", p_decoded_image->width);
return false;
}
return true;
}
// Loads a PNG image image into RGB888(24) format
// Return: true if success
static bool image_load_png(image_data * p_decoded_image, const char * filename) {
bool status = true;
LodePNGState png_state;
lodepng_state_init(&png_state);
// Decode with auto conversion to RGB888(24)
unsigned error = lodepng_decode24_file(&p_decoded_image->p_img_data, &p_decoded_image->width, &p_decoded_image->height, filename);
if (error) {
status = false;
log_error("Error: PNG load: %u: %s\n", error, lodepng_error_text(error));
} else {
p_decoded_image->bytes_per_pixel = RGB_24SZ;
p_decoded_image->size = p_decoded_image->width * p_decoded_image->height * p_decoded_image->bytes_per_pixel;
}
// Free resources
lodepng_state_cleanup(&png_state);
return status;
}
// Loads an image image
// Return: true if success
bool image_load(image_data * p_decoded_image, const char * filename, const uint8_t image_type) {
bool status = true;
log_verbose("Loading image from file: %s, type: %d\n", filename, image_type);
switch (image_type) {
case IMG_TYPE_PNG:
status = image_load_png(p_decoded_image, filename);
break;
default:
status = false;
log_error("Invalid image format. No image will be loaded\n");
break;
}
if (status)
status = image_validate(p_decoded_image);
if (status) {
log_verbose("Decoded image.width: %d\n", p_decoded_image->width);
log_verbose("Decoded image.height: %d\n", p_decoded_image->height);
log_verbose("Decoded image.size: %d\n", p_decoded_image->size);
log_verbose("Decoded image.bytes_per_pixel: %d\n", p_decoded_image->bytes_per_pixel);
}
return status;
}

View File

@@ -0,0 +1,9 @@
#ifndef _IMAGE_LOAD_C
#define _IMAGE_LOAD_C
#include "image_info.h"
bool image_load(image_data * p_src_image, const char * filename, const uint8_t img_type);
#endif

View File

@@ -0,0 +1,21 @@
Copyright (c) 2005-2018 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
// logging.c
#include <stdarg.h>
#include <stdio.h>
#include "logging.h"
int output_level = OUTPUT_LEVEL_DEFAULT;
#define VA_LIST_PRINT() \
va_list args; \
va_start (args, format); \
vprintf (format, args); \
va_end (args); \
fflush(stdout); // If logging a lot of output to a file turn off the excessive flushing
void log_set_level(int new_output_level) {
output_level = new_output_level;
}
void log_debug(const char * format, ...){
if (output_level > OUTPUT_LEVEL_DEBUG) return;
VA_LIST_PRINT();
}
void log_verbose(const char * format, ...){
if (output_level > OUTPUT_LEVEL_VERBOSE) return;
VA_LIST_PRINT();
}
void log_standard(const char * format, ...){
// Only print if quiet mode and error_only are NOT enabled
if ((output_level == OUTPUT_LEVEL_QUIET) ||
(output_level == OUTPUT_LEVEL_ONLY_ERRORS)) return;
VA_LIST_PRINT();
}
void log_error(const char * format, ...){
// Only print if quiet mode is NOT enabled
if (output_level == OUTPUT_LEVEL_QUIET) return;
VA_LIST_PRINT();
}

View File

@@ -0,0 +1,22 @@
// logging.h
#ifndef _LOGGING_H
#define _LOGGING_H
enum output_levels {
OUTPUT_LEVEL_DEBUG,
OUTPUT_LEVEL_VERBOSE,
OUTPUT_LEVEL_DEFAULT,
OUTPUT_LEVEL_ONLY_ERRORS,
OUTPUT_LEVEL_QUIET
};
#define log_progress log_verbose
void log_set_level(int new_output_level);
void log_debug(const char * format, ...);
void log_verbose(const char * format, ...);
void log_standard(const char * format, ...);
void log_error(const char * format, ...);
#endif

View File

@@ -0,0 +1,244 @@
// See LICENSE file for license details
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
#include "common.h"
#include "path_ops.h"
#include "options.h"
#include "logging.h"
#include "image_load.h"
#include <hicolour.h>
#define VERSION "version 1.4.1"
static void init(void);
static void cleanup(void);
static void display_help(void);
static int handle_args(int argc, char * argv[]);
static void set_drag_and_drop_mode_defaults(void);
image_data src_image;
char filename_in[MAX_STR_LEN] = {'\0'};
char opt_filename_out[MAX_STR_LEN] = "";
// bool opt_strip_output_filename_ext = true;
int show_help_and_exit = false;
static void init(void) {
// Handle global init
src_image.p_img_data = NULL;
hicolor_init();
}
// Registered as an atexit handler
static void cleanup(void) {
// Handle any cleanup on exit
// Free the image data
if (src_image.p_img_data != NULL)
free(src_image.p_img_data);
}
int main( int argc, char *argv[] ) {
int ret = EXIT_FAILURE; // Default to failure on exit
// Register cleanup with exit handler
atexit(cleanup);
init();
#ifdef DRAG_AND_DROP_MODE
// Non-interactive mode, set some reasonable default
set_drag_and_drop_mode_defaults();
#endif
if (handle_args(argc, argv)) {
if (show_help_and_exit) {
ret = EXIT_SUCCESS;
}
else {
// detect file extension
if (matches_extension(filename_in, (char *)".png")) {
// Load source image (from first argument)
if (image_load(&src_image, filename_in, IMG_TYPE_PNG)) {
// If output filename is not specified, use source filename
if (opt_filename_out[0] == '\0')
strcpy(opt_filename_out, filename_in);
filename_remove_extension(opt_filename_out);
hicolor_process_image(&src_image, opt_filename_out);
ret = EXIT_SUCCESS; // Exit with success
}
}
}
}
// Override exit code if was set during processing
if (get_exit_error())
ret = EXIT_FAILURE;
// if (ret == EXIT_FAILURE)
// log_error("Error loading, converting or writing out the image: %s\n", filename_in);
#ifdef DRAG_AND_DROP_MODE
// Wait for input to keep the console window open after processing
log_standard("\n\nPress Any Key to Continue\n");
getchar();
#endif
return ret;
}
static void display_help(void) {
log_standard(
"\n"
"png2hicolorgb input_image.png [options]\n"
VERSION": bbbbbr. Based on Glen Cook's Windows GUI \"hicolour.exe\" 1.2\n"
"Convert an image to Game Boy Hi-Color format\n"
"\n"
"Options\n"
"-h : Show this help\n"
"-v* : Set log level: \"-v\" verbose, \"-vQ\" quiet, \"-vE\" only errors, \"-vD\" debug\n"
"-o <file> : Set base output filename (otherwise from input image)\n"
// "--keepext : Do not strip extension from output filename\n"
"--csource : Export C source format with incbins for data files\n"
"--bank=N : Set bank number for C source output where N is decimal bank number 1-511\n"
"--type=N : Set conversion type where N is one of below \n"
" 1: Median Cut - No Dither (*Default*)\n"
" 2: Median Cut - With Dither\n"
" 3: Wu Quantiser\n"
"-p : Show screen attribute pattern options (no processing)\n"
"-L=N : Set Left screen attribute pattern where N is decimal entry (-p to show patterns)\n"
"-R=N : Set Right screen attribute pattern where N is decimal entry (-p to show patterns)\n"
"--vaddrid : Map uses vram id (128->255->0->127) instead of (*Default*) sequential tile order (0->255)\n"
"--nodedupe : Turn off tile pattern deduplication\n"
"\n"
"Example 1: \"png2hicolorgb myimage.png\"\n"
"Example 2: \"png2hicolorgb myimage.png --csource -o=my_output_filename\"\n"
"* Default settings provide good results. Better quality but slower: \"--type=3 -L=2 -R=2\"\n"
"\n"
"Historical credits and info:\n"
" Original Concept : Icarus Productions\n"
" Original Code : Jeff Frohwein\n"
" Full Screen Modification : Anon\n"
" Adaptive Code : Glen Cook\n"
" Windows Interface : Glen Cook\n"
" Additional Windows Programming : Rob Jones\n"
" Original Quantiser Code : Benny\n"
" Quantiser Conversion : Glen Cook\n"
"\n"
);
}
// Default options for Windows Drag and Drop recipient mode
static void set_drag_and_drop_mode_defaults(void) {
// Set some options here
}
static int handle_args(int argc, char * argv[]) {
int i;
if( argc < 2 ) {
display_help();
return false;
}
// Copy input filename (if not preceded with option dash)
if (argv[1][0] != '-')
snprintf(filename_in, sizeof(filename_in), "%s", argv[1]);
// Start at first optional argument, argc is zero based
for (i = 1; i <= (argc -1); i++ ) {
if ((strstr(argv[i], "-h") == argv[i]) || (strstr(argv[i], "-?") == argv[i])) {
display_help();
show_help_and_exit = true;
return true; // Don't parse further input when -h is used
} else if (strstr(argv[i], "-p") == argv[i]) {
log_standard(HELP_CONV_PATTERN_STR);
show_help_and_exit = true;
return true; // Don't parse further input when -h is used
} else if (strstr(argv[i], "-vD") == argv[i]) {
log_set_level(OUTPUT_LEVEL_DEBUG);
} else if (strstr(argv[i], "-vE") == argv[i]) {
log_set_level(OUTPUT_LEVEL_ONLY_ERRORS);
} else if (strstr(argv[i], "-vQ") == argv[i]) {
log_set_level(OUTPUT_LEVEL_QUIET);
} else if (strstr(argv[i], "-v") == argv[i]) {
log_set_level(OUTPUT_LEVEL_VERBOSE);
} else if (strstr(argv[i], "--type=") == argv[i]) {
uint8_t new_type = strtol(argv[i] + strlen("--type="), NULL, 10);
if ((new_type < CONV_TYPE_MIN) || (new_type > CONV_TYPE_MAX)) {
log_standard("Error: --type specified with invalid conversion setting: %d\n", new_type);
display_help();
show_help_and_exit = true;
return false; // Abort
}
else
hicolor_set_type(new_type);
} else if (strstr(argv[i], "-L=") == argv[i]) {
hicolor_set_convert_left_pattern( strtol(argv[i] + strlen("-L="), NULL, 10));
} else if (strstr(argv[i], "-R=") == argv[i]) {
hicolor_set_convert_right_pattern( strtol(argv[i] + strlen("-R="), NULL, 10));
} else if (strstr(argv[i], "-o") == argv[i]) {
i++; // Move to next argument
// Require colon and filename to be present
if (*argv[i] == '-')
log_standard("Warning: -o specified but filename has dash and looks like an option argument. Usage: -o my_base_output_filename\n");
snprintf(opt_filename_out, sizeof(opt_filename_out), "%s", argv[i]);
// } else if (strstr(argv[i], "--keepext") == argv[i]) {
// opt_strip_output_filename_ext = false;
} else if (strstr(argv[i], "--csource") == argv[i]) {
opt_set_c_file_output(true);
} else if (strstr(argv[i], "--bank=") == argv[i]) {
opt_set_bank_num( strtol(argv[i] + strlen("--bank="), NULL, 10) );
if ((opt_get_bank_num() < BANK_NUM_MIN) || (opt_get_bank_num() > BANK_NUM_MAX)) {
log_standard("Error: Invalid bank number specified with --bank=%d\n", opt_get_bank_num());
display_help();
show_help_and_exit = true;
return false; // Abort
}
} else if (strstr(argv[i], "--vaddrid") == argv[i]) {
opt_set_map_tile_order(OPT_MAP_TILE_ORDER_BY_VRAM_ID);
} else if (strstr(argv[i], "--nodedupe") == argv[i]) {
opt_set_tile_dedupe(false);
} else if (argv[i][0] == '-') {
log_error("Unknown argument: %s\n\n", argv[i]);
display_help();
return false;
}
}
return true;
}

View File

@@ -0,0 +1,28 @@
//
// options.c
//
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <options.h>
// Use sequential tile order number (0->255) for map index instead of VRAM tile index (128->255->0->127)
bool opt_map_use_sequential_tile_index = true;
bool opt_tile_dedupe = true;
bool opt_c_file_output = false;
int opt_bank_num = BANK_NUM_UNSET;
void opt_set_map_tile_order(bool newval) { opt_map_use_sequential_tile_index = newval; }
bool opt_get_map_tile_order(void) { return opt_map_use_sequential_tile_index; }
void opt_set_tile_dedupe(bool newval) { opt_tile_dedupe = newval; }
bool opt_get_tile_dedupe(void) { return opt_tile_dedupe; }
void opt_set_c_file_output(bool newval) { opt_c_file_output = newval; }
bool opt_get_c_file_output(void) { return opt_c_file_output; }
void opt_set_bank_num(int newval) { opt_bank_num = newval; }
int opt_get_bank_num(void) { return opt_bank_num; }

View File

@@ -0,0 +1,31 @@
// "options.h"
#ifndef OPTIONS_H
#define OPTIONS_H
#include <stdint.h>
#include <stdbool.h>
#define BANK_NUM_UNSET 0
#define BANK_NUM_MIN 1
#define BANK_NUM_MAX 511
#define OPTION_UNSET 0xFFFF
#define OPT_MAP_TILE_ORDER_BY_VRAM_ID false
#define OPT_MAP_TILE_SEQUENTIAL_ORDER true
void opt_set_map_tile_order(bool newval);
bool opt_get_map_tile_order(void);
void opt_set_tile_dedupe(bool newval);
bool opt_get_tile_dedupe(void);
void opt_set_c_file_output(bool newval);
bool opt_get_c_file_output(void);
void opt_set_bank_num(int newval);
int opt_get_bank_num(void);
#endif

View File

@@ -0,0 +1,157 @@
// This is free and unencumbered software released into the public domain.
// For more information, please refer to <https://unlicense.org>
// bbbbbr 2020
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "common.h"
#include "path_ops.h"
const char kExtensionSeparator = '.';
const char kPathSeparator =
#ifdef _WIN32
#ifndef _WIN32
#define __WIN32__
#endif
#endif
#ifdef __WIN32__
'\\';
#else
'/';
#endif
const char kPathSeparator_unix = '/';
void filename_replace_extension(char * filename, char * new_ext, size_t maxlen) {
// Use a temp work string in case out and in filename are the same pointer
char temp[MAX_PATH];
char ext_sep[2] = {'\0'}; // default to empty string
// Add leading . to path if needed
if (new_ext[0] != kExtensionSeparator) {
ext_sep[0] = kExtensionSeparator;
ext_sep[1] = '\0';
}
// Strip extension from filename, append new extension
filename_remove_extension(filename);
snprintf(temp, maxlen, "%s%s%s", filename, ext_sep, new_ext);
snprintf(filename, maxlen, "%s", temp);
}
void filename_replace_path(char * filename, char * new_path, size_t maxlen) {
// Use a temp work string in case out and in filename are the same pointer
char temp[MAX_PATH];
char path_sep[2] = {'\0'}; // default to empty string
// Add trailing slash to path if needed (Windows needs both for when running under linix like env)
#ifdef __WIN32__
if (((new_path[(strlen(new_path)-1)] != kPathSeparator)) &&
((new_path[(strlen(new_path)-1)] != kPathSeparator_unix)))
#else
if ((new_path[(strlen(new_path)-1)] != kPathSeparator))
#endif
{
path_sep[0] = kPathSeparator;
path_sep[1] = '\0';
}
// Strip path from path+filename, pre-pend new path
snprintf(temp, maxlen, "%s%s%s", new_path, path_sep, get_filename_from_path(filename));
snprintf(filename, maxlen, "%s", temp);
}
const char * get_filename_from_path(const char * path)
{
size_t i;
// Returns string starting at last occurrance of path separator char
for(i = strlen(path) - 1; i; i--) {
// Add trailing slash to path if needed (Windows needs both for when running under linix like env)
#ifdef __WIN32__
if ((path[i] == kPathSeparator) || (path[i] == kPathSeparator_unix))
#else
if (path[i] == kPathSeparator)
#endif
{
return &path[i+1];
}
}
return path;
}
void filename_remove_extension(char * path)
{
char * last_ext;
char * last_slash;
// Find the last path separator if present
// Starting from here ensures that no path ".." characters
// get mistaken as extension delimiters.
last_slash = strrchr (path, kExtensionSeparator);
if (!last_slash)
last_slash = path;
// Then check to see if there is an extension (starting with the last occurance of '.')
// (tries to remove *all* trailing extensions backward until the last slash)
last_ext = strrchr (last_slash, kExtensionSeparator);
while (last_ext) {
if (last_ext != NULL) {
// If an extension is found then overwrite it with a string terminator
*last_ext = '\0';
}
last_ext = strrchr (last_slash, kExtensionSeparator);
}
}
bool get_path_without_filename(const char * path, char * path_only, uint32_t str_max)
{
size_t i;
if (strlen(path) + 1 > str_max)
return false;
for(i = strlen(path) - 1; i; i--) {
// Add trailing slash to path if needed (Windows needs both for when running under linix like env)
#ifdef __WIN32__
if ((path[i] == kPathSeparator) || (path[i] == kPathSeparator_unix))
#else
if (path[i] == kPathSeparator)
#endif
{
memcpy(path_only, path, i+1 );
path_only[i+1] = '\0';
return true;
}
}
// memcpy(path_only, path, strlen(path));
// No separater found, so no path
path_only[0] = '\0';
return true;
}
// Case insensitive
bool matches_extension(char * filename, char * extension) {
if (strlen(filename) >= strlen(extension)) {
char * str_ext = filename + (strlen(filename) - strlen(extension));
return (strncasecmp(str_ext, extension, strlen(extension)) == 0);
}
else
return false;
}

View File

@@ -0,0 +1,18 @@
// This is free and unencumbered software released into the public domain.
// For more information, please refer to <https://unlicense.org>
// bbbbbr 2020
#ifndef _PATH_OPS_H
#define _PATH_OPS_H
#include <stdint.h>
#include <stdbool.h>
void filename_replace_extension(char * filename, char * new_ext, size_t maxlen);
void filename_replace_path(char * filename, char * new_path, size_t maxlen);
const char * get_filename_from_path(const char * path);
void filename_remove_extension(char * path);
bool get_path_without_filename(const char * path, char * path_only, uint32_t str_max);
bool matches_extension(char *, char *);
#endif // _PATH_OPS_H

View File

@@ -0,0 +1,109 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <common.h>
#include <defines.h>
#include <logging.h>
#include <options.h>
static void tile_calc_flip_gb2bpp(uint8_t * flipped_tile, const uint8_t* src_tile, bool flip_x, bool flip_y);
static void tile_generate_flips(const uint8_t * p_src_tile);
uint8_t tile_permutations[4 * TILE_SZ];
#define FLIP_X_YES true
#define FLIP_X_NO false
#define FLIP_Y_YES true
#define FLIP_Y_NO false
// These should match the order of tile flip permutations in tile_generate_flips()
const uint8_t tile_flip_flags[] = {
CGB_ATTR_NO_FLIP,
CGB_ATTR_HFLIP,
CGB_ATTR_VFLIP,
CGB_ATTR_HFLIP | CGB_ATTR_VFLIP};
const uint8_t bits_reverse_LUT[256] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
static void tile_calc_flip_gb2bpp(uint8_t * flipped_tile, const uint8_t * src_tile, bool flip_x, bool flip_y)
{
for (int ty = 0; ty < TILE_HEIGHT_PX; ty++) {
int y = flip_y ? (TILE_HEIGHT_PX - 1 - ty) : ty;
flipped_tile[2 * ty] = flip_x ? bits_reverse_LUT[src_tile[2 * y]] : src_tile[2 * y];
flipped_tile[2 * ty + 1] = flip_x ? bits_reverse_LUT[src_tile[2 * y + 1]] : src_tile[2 * y + 1];
}
}
// Generate all the X/Y flipped combinations of a 2bpp Game Boy tile
// Output is used for testing if CGB flipping map attributes allow for tile pattern deduplciation
//
// tile_permutations[] should be 4 x 16 bytes
static void tile_generate_flips(const uint8_t * p_src_tile)
{
tile_calc_flip_gb2bpp(&tile_permutations[0 * TILE_SZ], p_src_tile, FLIP_X_NO, FLIP_Y_NO);
tile_calc_flip_gb2bpp(&tile_permutations[1 * TILE_SZ], p_src_tile, FLIP_X_YES, FLIP_Y_NO);
tile_calc_flip_gb2bpp(&tile_permutations[2 * TILE_SZ], p_src_tile, FLIP_X_NO, FLIP_Y_YES);
tile_calc_flip_gb2bpp(&tile_permutations[3 * TILE_SZ], p_src_tile, FLIP_X_YES, FLIP_Y_YES);
}
// bool tileset_find_matching_tile(unsigned int source_tile_id, unsigned int * TileCountDeduped, uint8_t * new_tile_id, uint8_t * new_attribs)
bool tileset_find_matching_tile(const uint8_t * src_tile, const uint8_t * tile_set_deduped, const unsigned int tile_count_deduped, uint8_t * new_tile_id, uint8_t * new_attribs)
{
tile_generate_flips(src_tile);
for (unsigned int index_dedupe = 0; index_dedupe < tile_count_deduped; index_dedupe++) {
for (int f = 0; f < ARRAY_LEN(tile_flip_flags); f++) {
if (memcmp(&tile_set_deduped[index_dedupe * TILE_SZ], &tile_permutations[f * TILE_SZ], TILE_SZ) == 0) {
// Match Found
*new_tile_id = (uint8_t)(index_dedupe & 0xFFu);
*new_attribs = (index_dedupe < CGB_TILES_START_BANK_1) ? CGB_ATTR_TILES_BANK_0 : CGB_ATTR_TILES_BANK_1;
*new_attribs |= tile_flip_flags[f];
// log_debug(" * Tile Dedupe MATCH. new ID -> %d (dedupe count=%d), new_tile_id=%d, new_attribs=0x%02x, flip_index=%d\n", *new_tile_id, tile_count_deduped, *new_tile_id, *new_attribs, f);
return true;
}
}
}
return false; // No match found
}

View File

@@ -0,0 +1,10 @@
#ifndef TILE_DEDUPE_H
#define TILE_DEDUPE_H
#include <stdint.h>
#include <stdbool.h>
bool tileset_find_matching_tile(const uint8_t * src_tile, const uint8_t * tile_set_deduped, unsigned int tile_count_deduped, uint8_t * new_tile_id, uint8_t * new_attribs);
#endif

View File

@@ -0,0 +1,93 @@
The updated version of this software is released under the same license used by the original author.
- bbbbbr
GBDK Example project
----------------------------------------------------------------------
Source code: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
Example image: Pixel art originally by RodrixAP under Creative Commons Attribution 2.0 Generic (CC BY 2.0)
https://www.flickr.com/photos/rodrixap/10591266994/in/album-72157637154901153/
Main program:
----------------------------------------------------------------------
...
Feel free to use this code any way you feel fit.
You can use this code / algorithm in any commercial product. It would be great
if you could send me an e-mail letting me know.
And now for the usual bull..
This software is supplied on an as-is basis, although I don't believe that
there are any bugs in my code, I will not be held responsible for any damage
this program may cause....
Glen Cook.
...
I am releasing this program into the public domain, feel free to adapt it in anyway that you
deem fit. I you feel you have improved this program in anyway, drop me a line, and I will
incorperate the changes into newer versions. (GlenCook --a--t-- hotmail -- com)
Wu quantizer:
----------------------------------------------------------------------
C Implementation of Wu's Color Quantizer (v. 2)
(see Graphics Gems vol. II, pp. 126-133)
Author: Xiaolin Wu
Dept. of Computer Science
Univ. of Western Ontario
London, Ontario N6A 5B7
wu@csd.uwo.ca
Algorithm: Greedy orthogonal bipartition of RGB space for variance
minimization aided by inclusion-exclusion tricks.
For speed no nearest neighbor search is done. Slightly
better performance can be expected by more sophisticated
but more expensive versions.
The author thanks Tom Lane at Tom_Lane@G.GP.CS.CMU.EDU for much of
additional documentation and a cure to a previous bug.
Free to distribute, comments and suggestions are appreciated.
PNG support:
----------------------------------------------------------------------
Lodepng license
Copyright (c) 2005-2018 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.