|
|
@@ -1,7 +1,7 @@ |
|
|
|
// |
|
|
|
// Lol Engine |
|
|
|
// |
|
|
|
// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net> |
|
|
|
// Copyright: (c) 2010-2014 Sam Hocevar <sam@hocevar.net> |
|
|
|
// This program is free software; you can redistribute it and/or |
|
|
|
// modify it under the terms of the Do What The Fuck You Want To |
|
|
|
// Public License, Version 2, as published by Sam Hocevar. See |
|
|
@@ -24,7 +24,6 @@ namespace lol |
|
|
|
* DEPTH = 3 gives good quality, and higher values may improve the results |
|
|
|
* even more but at the cost of significantly longer computation times. */ |
|
|
|
#define WIDTH 240 |
|
|
|
#define HEIGHT 200 |
|
|
|
#define DEPTH 2 |
|
|
|
|
|
|
|
/* |
|
|
@@ -39,6 +38,7 @@ public: |
|
|
|
|
|
|
|
private: |
|
|
|
static String ReadScreen(char const *name); |
|
|
|
static void WriteScreen(Image &image, Array<uint8_t> &result); |
|
|
|
}; |
|
|
|
|
|
|
|
DECLARE_IMAGE_CODEC(OricImageCodec, 100) |
|
|
@@ -110,7 +110,41 @@ bool OricImageCodec::Load(Image *image, char const *path) |
|
|
|
|
|
|
|
bool OricImageCodec::Save(Image *image, char const *path) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
int len = strlen(path); |
|
|
|
if (len < 4 || path[len - 4] != '.' |
|
|
|
|| toupper(path[len - 3]) != 'T' |
|
|
|
|| toupper(path[len - 2]) != 'A' |
|
|
|
|| toupper(path[len - 1]) != 'P') |
|
|
|
return false; |
|
|
|
|
|
|
|
Array<uint8_t> result; |
|
|
|
|
|
|
|
result << 0x16 << 0x16 << 0x16 << 0x16 << 0x24; |
|
|
|
result << 0 << 0xff << 0x80 << 0 << 0xbf << 0x3f << 0xa0 << 0; |
|
|
|
|
|
|
|
/* Add filename, except the last 4 characters */ |
|
|
|
for (char const *name = path; name[4]; ++name) |
|
|
|
result << (uint8_t)name[0]; |
|
|
|
result << 0; |
|
|
|
|
|
|
|
Image tmp; |
|
|
|
ivec2 size = image->GetSize(); |
|
|
|
if (size.x != WIDTH) |
|
|
|
{ |
|
|
|
size.y = (int)((float)size.y * WIDTH / size.x); |
|
|
|
size.x = WIDTH; |
|
|
|
tmp = image->Resize(size, ResampleAlgorithm::Bresenham); |
|
|
|
image = &tmp; |
|
|
|
} |
|
|
|
|
|
|
|
WriteScreen(*image, result); |
|
|
|
|
|
|
|
File f; |
|
|
|
f.Open(path, FileAccess::Write); |
|
|
|
f.Write(result.Data(), result.Bytes()); |
|
|
|
f.Close(); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
String OricImageCodec::ReadScreen(char const *name) |
|
|
@@ -144,61 +178,6 @@ String OricImageCodec::ReadScreen(char const *name) |
|
|
|
return data.Sub(filename_end + 1); |
|
|
|
} |
|
|
|
|
|
|
|
#if 0 |
|
|
|
|
|
|
|
pipi_image_t *pipi_load_oric(char const *name) |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
int pipi_save_oric(pipi_image_t *img, char const *name) |
|
|
|
{ |
|
|
|
pipi_image_t *tmp = NULL; |
|
|
|
pipi_pixels_t *p; |
|
|
|
float *data; |
|
|
|
uint8_t *screen; |
|
|
|
FILE *fp; |
|
|
|
size_t len; |
|
|
|
|
|
|
|
len = strlen(name); |
|
|
|
if (len < 4 || name[len - 4] != '.' |
|
|
|
|| toupper(name[len - 3]) != 'T' |
|
|
|
|| toupper(name[len - 2]) != 'A' |
|
|
|
|| toupper(name[len - 1]) != 'P') |
|
|
|
return -1; |
|
|
|
|
|
|
|
fp = fopen(name, "w"); |
|
|
|
if (!fp) |
|
|
|
return -1; |
|
|
|
|
|
|
|
fwrite("\x16\x16\x16\x16\x24", 1, 5, fp); |
|
|
|
fwrite("\x00\xff\x80\x00\xbf\x3f\xa0\x00\x00", 1, 9, fp); |
|
|
|
fwrite(name, 1, len - 4, fp); |
|
|
|
fwrite("\x00", 1, 1, fp); |
|
|
|
|
|
|
|
if (img->w != WIDTH || img->h != HEIGHT) |
|
|
|
tmp = pipi_resize_bicubic(img, WIDTH, HEIGHT); |
|
|
|
else |
|
|
|
tmp = img; |
|
|
|
p = pipi_get_pixels(tmp, PIPI_PIXELS_RGBA_F32); |
|
|
|
data = p->pixels; |
|
|
|
screen = malloc(WIDTH * HEIGHT / 6); |
|
|
|
write_screen(data, screen); |
|
|
|
pipi_release_pixels(tmp, p); |
|
|
|
if (tmp != img) |
|
|
|
pipi_free(tmp); |
|
|
|
|
|
|
|
fwrite(screen, 1, WIDTH * HEIGHT / 6, fp); |
|
|
|
fclose(fp); |
|
|
|
|
|
|
|
free(screen); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/* |
|
|
|
* XXX: The following functions are local. |
|
|
|
*/ |
|
|
|
|
|
|
|
/* Error diffusion table, similar to Floyd-Steinberg. I choose not to |
|
|
|
* propagate 100% of the error, because doing so creates awful artifacts |
|
|
|
* (full lines of the same colour, massive colour bleeding) for unclear |
|
|
@@ -251,7 +230,7 @@ static inline void domove(uint8_t command, uint8_t *bg, uint8_t *fg) |
|
|
|
/* Clamp pixel value to avoid colour bleeding. Deactivated because it |
|
|
|
* does not give satisfactory results. */ |
|
|
|
#define CLAMP 0x1000 |
|
|
|
static inline int clamp(int p) |
|
|
|
static inline int myclamp(int p) |
|
|
|
{ |
|
|
|
#if 0 |
|
|
|
/* FIXME: doesn’t give terribly good results on eg. eatme.png */ |
|
|
@@ -283,7 +262,7 @@ static inline int geterror(int const *in, int const *inerr, |
|
|
|
for (c = 0; c < 3; c++) |
|
|
|
{ |
|
|
|
/* Experiment shows that this is important at small depths */ |
|
|
|
int a = clamp(in[i * 3 + c] + tmperr[c]); |
|
|
|
int a = myclamp(in[i * 3 + c] + tmperr[c]); |
|
|
|
int b = out[i * 3 + c]; |
|
|
|
tmperr[c] = (a - b) * FS0 / FSX; |
|
|
|
tmperr[c + (i * 3 + 3)] += (a - b) * FS1 / FSX; |
|
|
@@ -430,30 +409,30 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, |
|
|
|
|
|
|
|
for (i = 0; i < 6; i++) |
|
|
|
{ |
|
|
|
int vec1[3], vec2[3]; |
|
|
|
int veca[3], vecb[3]; |
|
|
|
int smalle1 = 0, smalle2 = 0; |
|
|
|
|
|
|
|
memcpy(vec1, tmpvec, 3 * sizeof(int)); |
|
|
|
memcpy(vec2, tmpvec, 3 * sizeof(int)); |
|
|
|
memcpy(veca, tmpvec, 3 * sizeof(int)); |
|
|
|
memcpy(vecb, tmpvec, 3 * sizeof(int)); |
|
|
|
for (c = 0; c < 3; c++) |
|
|
|
{ |
|
|
|
int delta1, delta2; |
|
|
|
delta1 = clamp(in[i * 3 + c] + tmpvec[c]) - bgcolor[c]; |
|
|
|
vec1[c] = delta1 * FS0 / FSX; |
|
|
|
delta1 = myclamp(in[i * 3 + c] + tmpvec[c]) - bgcolor[c]; |
|
|
|
veca[c] = delta1 * FS0 / FSX; |
|
|
|
smalle1 += delta1 / 256 * delta1; |
|
|
|
delta2 = clamp(in[i * 3 + c] + tmpvec[c]) - fgcolor[c]; |
|
|
|
vec2[c] = delta2 * FS0 / FSX; |
|
|
|
delta2 = myclamp(in[i * 3 + c] + tmpvec[c]) - fgcolor[c]; |
|
|
|
vecb[c] = delta2 * FS0 / FSX; |
|
|
|
smalle2 += delta2 / 256 * delta2; |
|
|
|
} |
|
|
|
|
|
|
|
if (smalle1 < smalle2) |
|
|
|
{ |
|
|
|
memcpy(tmpvec, vec1, 3 * sizeof(int)); |
|
|
|
memcpy(tmpvec, veca, 3 * sizeof(int)); |
|
|
|
memcpy(tmprgb + i * 3, bgcolor, 3 * sizeof(int)); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
memcpy(tmpvec, vec2, 3 * sizeof(int)); |
|
|
|
memcpy(tmpvec, vecb, 3 * sizeof(int)); |
|
|
|
memcpy(tmprgb + i * 3, fgcolor, 3 * sizeof(int)); |
|
|
|
command |= (1 << (5 - i)); |
|
|
|
} |
|
|
@@ -509,72 +488,71 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, |
|
|
|
return bestcommand; |
|
|
|
} |
|
|
|
|
|
|
|
static void write_screen(float const *data, uint8_t *screen) |
|
|
|
void OricImageCodec::WriteScreen(Image &image, Array<uint8_t> &result) |
|
|
|
{ |
|
|
|
int *src, *srcl, *dst, *dstl; |
|
|
|
int stride, x, y, depth, c; |
|
|
|
ivec2 size = image.GetSize(); |
|
|
|
vec4 *pixels = image.Lock<PixelFormat::RGBA_F32>(); |
|
|
|
|
|
|
|
stride = (WIDTH + 1) * 3; |
|
|
|
int stride = (size.x + 1); |
|
|
|
|
|
|
|
src = malloc((WIDTH + 1) * (HEIGHT + 1) * 3 * sizeof(int)); |
|
|
|
memset(src, 0, (WIDTH + 1) * (HEIGHT + 1) * 3 * sizeof(int)); |
|
|
|
ivec3 *src = (ivec3 *)malloc((size.x + 1) * (size.y + 1) * sizeof(ivec3)); |
|
|
|
memset(src, 0, (size.x + 1) * (size.y + 1) * sizeof(ivec3)); |
|
|
|
|
|
|
|
dst = malloc((WIDTH + 1) * (HEIGHT + 1) * 3 * sizeof(int)); |
|
|
|
memset(dst, 0, (WIDTH + 1) * (HEIGHT + 1) * 3 * sizeof(int)); |
|
|
|
ivec3 *dst = (ivec3 *)malloc((size.x + 1) * (size.y + 1) * sizeof(ivec3)); |
|
|
|
memset(dst, 0, (size.x + 1) * (size.y + 1) * sizeof(ivec3)); |
|
|
|
|
|
|
|
/* Import pixels into our custom format */ |
|
|
|
for (y = 0; y < HEIGHT; y++) |
|
|
|
for (x = 0; x < WIDTH; x++) |
|
|
|
for (c = 0; c < 3; c++) |
|
|
|
src[y * stride + x * 3 + c] = |
|
|
|
0xffff * data[(y * WIDTH + x) * 4 + (2 - c)]; |
|
|
|
for (int y = 0; y < size.y; y++) |
|
|
|
for (int x = 0; x < size.x; x++) |
|
|
|
for (int c = 0; c < 3; c++) |
|
|
|
src[y * stride + x][c] = 0xffff * pixels[y * size.x + x][2 - c]; |
|
|
|
|
|
|
|
/* Let the fun begin */ |
|
|
|
for (y = 0; y < HEIGHT; y++) |
|
|
|
for (int y = 0; y < size.y; y++) |
|
|
|
{ |
|
|
|
uint8_t bg = 0, fg = 7; |
|
|
|
|
|
|
|
//fprintf(stderr, "\rProcessing... %i%%", (y * 100 + 99) / HEIGHT); |
|
|
|
//fprintf(stderr, "\rProcessing... %i%%", (y * 100 + 99) / size.y); |
|
|
|
|
|
|
|
for (x = 0; x < WIDTH; x += 6) |
|
|
|
for (int x = 0; x < size.x; x += 6) |
|
|
|
{ |
|
|
|
int errvec[3] = { 0, 0, 0 }; |
|
|
|
int dummy, i; |
|
|
|
int dummy; |
|
|
|
uint8_t command; |
|
|
|
|
|
|
|
depth = (x + DEPTH < WIDTH) ? DEPTH : (WIDTH - x) / 6 - 1; |
|
|
|
srcl = src + y * stride + x * 3; |
|
|
|
dstl = dst + y * stride + x * 3; |
|
|
|
int depth = (x + DEPTH < size.x) ? DEPTH : (size.x - x) / 6 - 1; |
|
|
|
ivec3 *srcl = src + y * stride + x; |
|
|
|
ivec3 *dstl = dst + y * stride + x; |
|
|
|
|
|
|
|
/* Recursively compute and apply best command */ |
|
|
|
command = bestmove(srcl, bg, fg, errvec, depth, 0x7fffff, |
|
|
|
&dummy, dstl); |
|
|
|
command = bestmove((int *)srcl, bg, fg, errvec, depth, 0x7fffff, |
|
|
|
&dummy, (int *)dstl); |
|
|
|
/* Propagate error */ |
|
|
|
for (c = 0; c < 3; c++) |
|
|
|
for (int c = 0; c < 3; c++) |
|
|
|
{ |
|
|
|
for (i = 0; i < 6; i++) |
|
|
|
for (int i = 0; i < 6; i++) |
|
|
|
{ |
|
|
|
int error = srcl[i * 3 + c] - dstl[i * 3 + c]; |
|
|
|
srcl[i * 3 + c + 3] = |
|
|
|
clamp(srcl[i * 3 + c + 3] + error * FS0 / FSX); |
|
|
|
srcl[i * 3 + c + stride - 3] += error * FS1 / FSX; |
|
|
|
srcl[i * 3 + c + stride] += error * FS2 / FSX; |
|
|
|
srcl[i * 3 + c + stride + 3] += error * FS3 / FSX; |
|
|
|
int error = srcl[i][c] - dstl[i][c]; |
|
|
|
srcl[i + 1][c] = myclamp(srcl[i + 1][c] + error * FS0 / FSX); |
|
|
|
srcl[i + stride - 1][c] += error * FS1 / FSX; |
|
|
|
srcl[i + stride][c] += error * FS2 / FSX; |
|
|
|
srcl[i + stride + 1][c] += error * FS3 / FSX; |
|
|
|
} |
|
|
|
|
|
|
|
for (i = -1; i < 7; i++) |
|
|
|
srcl[i * 3 + c + stride] = clamp(srcl[i * 3 + c + stride]); |
|
|
|
for (int i = -1; i < 7; i++) |
|
|
|
srcl[i + stride][c] = myclamp(srcl[i + stride][c]); |
|
|
|
} |
|
|
|
/* Iterate */ |
|
|
|
domove(command, &bg, &fg); |
|
|
|
/* Write byte to file */ |
|
|
|
screen[y * (WIDTH / 6) + (x / 6)] = command; |
|
|
|
result << command; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
image.Unlock(pixels); |
|
|
|
|
|
|
|
//fprintf(stderr, " done.\n"); |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
} /* namespace lol */ |
|
|
|
|