| @@ -1,7 +1,7 @@ | |||||
| // | // | ||||
| // Lol Engine | // 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 | // 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 | // modify it under the terms of the Do What The Fuck You Want To | ||||
| // Public License, Version 2, as published by Sam Hocevar. See | // 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 | * DEPTH = 3 gives good quality, and higher values may improve the results | ||||
| * even more but at the cost of significantly longer computation times. */ | * even more but at the cost of significantly longer computation times. */ | ||||
| #define WIDTH 240 | #define WIDTH 240 | ||||
| #define HEIGHT 200 | |||||
| #define DEPTH 2 | #define DEPTH 2 | ||||
| /* | /* | ||||
| @@ -39,6 +38,7 @@ public: | |||||
| private: | private: | ||||
| static String ReadScreen(char const *name); | static String ReadScreen(char const *name); | ||||
| static void WriteScreen(Image &image, Array<uint8_t> &result); | |||||
| }; | }; | ||||
| DECLARE_IMAGE_CODEC(OricImageCodec, 100) | 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) | 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) | String OricImageCodec::ReadScreen(char const *name) | ||||
| @@ -144,61 +178,6 @@ String OricImageCodec::ReadScreen(char const *name) | |||||
| return data.Sub(filename_end + 1); | 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 | /* Error diffusion table, similar to Floyd-Steinberg. I choose not to | ||||
| * propagate 100% of the error, because doing so creates awful artifacts | * propagate 100% of the error, because doing so creates awful artifacts | ||||
| * (full lines of the same colour, massive colour bleeding) for unclear | * (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 | /* Clamp pixel value to avoid colour bleeding. Deactivated because it | ||||
| * does not give satisfactory results. */ | * does not give satisfactory results. */ | ||||
| #define CLAMP 0x1000 | #define CLAMP 0x1000 | ||||
| static inline int clamp(int p) | |||||
| static inline int myclamp(int p) | |||||
| { | { | ||||
| #if 0 | #if 0 | ||||
| /* FIXME: doesn’t give terribly good results on eg. eatme.png */ | /* 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++) | for (c = 0; c < 3; c++) | ||||
| { | { | ||||
| /* Experiment shows that this is important at small depths */ | /* 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]; | int b = out[i * 3 + c]; | ||||
| tmperr[c] = (a - b) * FS0 / FSX; | tmperr[c] = (a - b) * FS0 / FSX; | ||||
| tmperr[c + (i * 3 + 3)] += (a - b) * FS1 / 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++) | for (i = 0; i < 6; i++) | ||||
| { | { | ||||
| int vec1[3], vec2[3]; | |||||
| int veca[3], vecb[3]; | |||||
| int smalle1 = 0, smalle2 = 0; | 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++) | for (c = 0; c < 3; c++) | ||||
| { | { | ||||
| int delta1, delta2; | 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; | 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; | smalle2 += delta2 / 256 * delta2; | ||||
| } | } | ||||
| if (smalle1 < smalle2) | if (smalle1 < smalle2) | ||||
| { | { | ||||
| memcpy(tmpvec, vec1, 3 * sizeof(int)); | |||||
| memcpy(tmpvec, veca, 3 * sizeof(int)); | |||||
| memcpy(tmprgb + i * 3, bgcolor, 3 * sizeof(int)); | memcpy(tmprgb + i * 3, bgcolor, 3 * sizeof(int)); | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| memcpy(tmpvec, vec2, 3 * sizeof(int)); | |||||
| memcpy(tmpvec, vecb, 3 * sizeof(int)); | |||||
| memcpy(tmprgb + i * 3, fgcolor, 3 * sizeof(int)); | memcpy(tmprgb + i * 3, fgcolor, 3 * sizeof(int)); | ||||
| command |= (1 << (5 - i)); | command |= (1 << (5 - i)); | ||||
| } | } | ||||
| @@ -509,72 +488,71 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, | |||||
| return bestcommand; | 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 */ | /* 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 */ | /* Let the fun begin */ | ||||
| for (y = 0; y < HEIGHT; y++) | |||||
| for (int y = 0; y < size.y; y++) | |||||
| { | { | ||||
| uint8_t bg = 0, fg = 7; | 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 errvec[3] = { 0, 0, 0 }; | ||||
| int dummy, i; | |||||
| int dummy; | |||||
| uint8_t command; | 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 */ | /* 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 */ | /* 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 */ | /* Iterate */ | ||||
| domove(command, &bg, &fg); | domove(command, &bg, &fg); | ||||
| /* Write byte to file */ | /* Write byte to file */ | ||||
| screen[y * (WIDTH / 6) + (x / 6)] = command; | |||||
| result << command; | |||||
| } | } | ||||
| } | } | ||||
| image.Unlock(pixels); | |||||
| //fprintf(stderr, " done.\n"); | //fprintf(stderr, " done.\n"); | ||||
| } | } | ||||
| #endif | |||||
| } /* namespace lol */ | } /* namespace lol */ | ||||