From 93a6b1b75bf27bd747717f7440563aeb1b9be4a9 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Mon, 23 Jun 2014 06:40:27 +0000 Subject: [PATCH] image: we can now write Oric .tap files. --- src/image/codec/oric-image.cpp | 184 +++++++++++++++------------------ 1 file changed, 81 insertions(+), 103 deletions(-) diff --git a/src/image/codec/oric-image.cpp b/src/image/codec/oric-image.cpp index 322b50ff..1f1b32f6 100644 --- a/src/image/codec/oric-image.cpp +++ b/src/image/codec/oric-image.cpp @@ -1,7 +1,7 @@ // // Lol Engine // -// Copyright: (c) 2010-2011 Sam Hocevar +// Copyright: (c) 2010-2014 Sam Hocevar // 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 &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 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 &result) { - int *src, *srcl, *dst, *dstl; - int stride, x, y, depth, c; + ivec2 size = image.GetSize(); + vec4 *pixels = image.Lock(); - 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 */