mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-23 15:52:55 +01:00
Merge pull request #1191 from ThomasdenH/develop
Add dithering to sprite importer
This commit is contained in:
@@ -60,6 +60,7 @@ int cmdline_run(const char **argv, int argc)
|
||||
OPT_HELP(),
|
||||
OPT_BOOLEAN('v', "version", &version, "show version information and exit"),
|
||||
OPT_BOOLEAN(0, "verbose", &verbose, "log verbose messages"),
|
||||
OPT_INTEGER('m', "mode", &sprite_mode, "the type of sprite conversion. 0 = default, 1 = simple closest pixel match, 2 = dithering"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
/** The exit code for OpenRCT2 when it exits. */
|
||||
extern int gExitCode;
|
||||
|
||||
int sprite_mode;
|
||||
|
||||
int cmdline_run(const char **argv, int argc);
|
||||
int cmdline_for_sprite(const char **argv, int argc);
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
#include "drawing/drawing.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#define MODE_DEFAULT 0
|
||||
#define MODE_CLOSEST 1
|
||||
#define MODE_DITHERING 2
|
||||
|
||||
typedef struct {
|
||||
uint32 num_entries;
|
||||
uint32 total_size;
|
||||
@@ -171,17 +175,56 @@ bool sprite_file_export(int spriteIndex, const char *outPath)
|
||||
}
|
||||
}
|
||||
|
||||
int get_palette_index(uint32 colour)
|
||||
{
|
||||
uint8 *rgba = (uint8*)(&colour);
|
||||
|
||||
if (rgba[3] < 128)
|
||||
bool is_transparent_pixel(sint16 *colour){
|
||||
return colour[3] < 128;
|
||||
}
|
||||
|
||||
// Returns true if pixel index is an index not used for remapping
|
||||
bool is_changable_pixel(int palette_index) {
|
||||
if (palette_index == -1)
|
||||
return true;
|
||||
if (palette_index == 0)
|
||||
return false;
|
||||
if (palette_index >= 203 && palette_index < 214)
|
||||
return false;
|
||||
if (palette_index == 226)
|
||||
return false;
|
||||
if (palette_index >= 227 && palette_index < 229)
|
||||
return false;
|
||||
if (palette_index >= 243)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int get_closest_palette_index(sint16 *colour){
|
||||
uint32 smallest_error = -1;
|
||||
int best_match = -1;
|
||||
|
||||
for (int x = 0; x < 256; x++){
|
||||
if (is_changable_pixel(x)){
|
||||
uint32 error =
|
||||
((sint16)(spriteFilePalette[x].r) - colour[0]) * ((sint16)(spriteFilePalette[x].r) - colour[0]) +
|
||||
((sint16)(spriteFilePalette[x].g) - colour[1]) * ((sint16)(spriteFilePalette[x].g) - colour[1]) +
|
||||
((sint16)(spriteFilePalette[x].b) - colour[2]) * ((sint16)(spriteFilePalette[x].b) - colour[2]);
|
||||
|
||||
if (smallest_error == -1 || smallest_error > error){
|
||||
best_match = x;
|
||||
smallest_error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best_match;
|
||||
}
|
||||
|
||||
int get_palette_index(sint16 *colour)
|
||||
{
|
||||
if (is_transparent_pixel(colour))
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (spriteFilePalette[i].r != rgba[0]) continue;
|
||||
if (spriteFilePalette[i].g != rgba[1]) continue;
|
||||
if (spriteFilePalette[i].b != rgba[2]) continue;
|
||||
if ((sint16)(spriteFilePalette[i].r) != colour[0]) continue;
|
||||
if ((sint16)(spriteFilePalette[i].g) != colour[1]) continue;
|
||||
if ((sint16)(spriteFilePalette[i].b) != colour[2]) continue;
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -193,7 +236,7 @@ typedef struct {
|
||||
uint8 offset_x;
|
||||
} rle_code;
|
||||
|
||||
bool sprite_file_import(const char *path, rct_g1_element *outElement, uint8 **outBuffer, int *outBufferLength)
|
||||
bool sprite_file_import(const char *path, rct_g1_element *outElement, uint8 **outBuffer, int *outBufferLength, int mode)
|
||||
{
|
||||
unsigned char *pixels;
|
||||
unsigned int width, height;
|
||||
@@ -215,7 +258,13 @@ bool sprite_file_import(const char *path, rct_g1_element *outElement, uint8 **ou
|
||||
|
||||
uint8 *buffer = malloc((height * 2) + (width * height * 16));
|
||||
uint16 *yOffsets = (uint16*)buffer;
|
||||
uint8 *src = pixels;
|
||||
|
||||
// A larger range is needed for proper dithering
|
||||
sint16 *src = malloc(height * width * 4 * 2);
|
||||
for (uint32 x = 0; x < height * width * 4; x++){
|
||||
src[x] = (sint16) pixels[x];
|
||||
}
|
||||
|
||||
uint8 *dst = buffer + (height * 2);
|
||||
|
||||
for (unsigned int y = 0; y < height; y++) {
|
||||
@@ -230,7 +279,56 @@ bool sprite_file_import(const char *path, rct_g1_element *outElement, uint8 **ou
|
||||
int pixels = 0;
|
||||
bool pushRun = false;
|
||||
for (unsigned int x = 0; x < width; x++) {
|
||||
int paletteIndex = get_palette_index(*((uint32*)src));
|
||||
int paletteIndex = get_palette_index(src);
|
||||
|
||||
if (mode == MODE_CLOSEST || mode == MODE_DITHERING)
|
||||
if (paletteIndex == -1 && !is_transparent_pixel(src))
|
||||
paletteIndex = get_closest_palette_index(src);
|
||||
|
||||
|
||||
if (mode == MODE_DITHERING)
|
||||
if (!is_transparent_pixel(src) && is_changable_pixel(get_palette_index(src))){
|
||||
sint16 dr = src[0] - (sint16)(spriteFilePalette[paletteIndex].r);
|
||||
sint16 dg = src[1] - (sint16)(spriteFilePalette[paletteIndex].g);
|
||||
sint16 db = src[2] - (sint16)(spriteFilePalette[paletteIndex].b);
|
||||
|
||||
if (x + 1 < width){
|
||||
if (!is_transparent_pixel(src + 4) && is_changable_pixel(get_palette_index(src + 4))){
|
||||
// Right
|
||||
src[4] += dr * 7 / 16;
|
||||
src[5] += dg * 7 / 16;
|
||||
src[6] += db * 7 / 16;
|
||||
}
|
||||
}
|
||||
|
||||
if (y + 1 < height){
|
||||
if (x > 0){
|
||||
if (!is_transparent_pixel(src + 4 * (width - 1)) && is_changable_pixel(get_palette_index(src + 4 * (width - 1)))){
|
||||
// Bottom left
|
||||
src[4 * (width - 1)] += dr * 3 / 16;
|
||||
src[4 * (width - 1) + 1] += dg * 3 / 16;
|
||||
src[4 * (width - 1) + 2] += db * 3 / 16;
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom
|
||||
if (!is_transparent_pixel(src + 4 * width) && is_changable_pixel(get_palette_index(src + 4 * width))){
|
||||
src[4 * width] += dr * 5 / 16;
|
||||
src[4 * width + 1] += dg * 5 / 16;
|
||||
src[4 * width + 2] += db * 5 / 16;
|
||||
}
|
||||
|
||||
if (x + 1 < width){
|
||||
if (!is_transparent_pixel(src + 4 * (width - 1)) && is_changable_pixel(get_palette_index(src + 4 * (width + 1)))){
|
||||
// Bottom right
|
||||
src[4 * (width + 1)] += dr * 1 / 16;
|
||||
src[4 * (width + 1) + 1] += dg * 1 / 16;
|
||||
src[4 * (width + 1) + 2] += db * 1 / 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
src += 4;
|
||||
if (paletteIndex == -1) {
|
||||
if (pixels != 0) {
|
||||
@@ -391,7 +489,7 @@ int cmdline_for_sprite(const char **argv, int argc)
|
||||
rct_g1_element spriteElement;
|
||||
uint8 *buffer;
|
||||
int bufferLength;
|
||||
if (!sprite_file_import(imagePath, &spriteElement, &buffer, &bufferLength))
|
||||
if (!sprite_file_import(imagePath, &spriteElement, &buffer, &bufferLength, sprite_mode))
|
||||
return -1;
|
||||
|
||||
if (!sprite_file_open(spriteFilePath)) {
|
||||
@@ -445,7 +543,7 @@ int cmdline_for_sprite(const char **argv, int argc)
|
||||
rct_g1_element spriteElement;
|
||||
uint8 *buffer;
|
||||
int bufferLength;
|
||||
if (!sprite_file_import(imagePath, &spriteElement, &buffer, &bufferLength)) {
|
||||
if (!sprite_file_import(imagePath, &spriteElement, &buffer, &bufferLength, sprite_mode)) {
|
||||
fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath);
|
||||
return -1;
|
||||
}
|
||||
@@ -488,6 +586,8 @@ int cmdline_for_sprite(const char **argv, int argc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static rct_sprite_file_palette_entry _standardPalette[256] = {
|
||||
// 0 (unused)
|
||||
{ 0, 0, 0, 255 },
|
||||
|
||||
Reference in New Issue
Block a user