diff --git a/TODO b/TODO index 6013beb3..64d6afc0 100644 --- a/TODO +++ b/TODO @@ -23,7 +23,6 @@ Image: · codec/coreimage.h · codec.cpp · codec/jpeg.cpp - · codec/oric.cpp · colorstring.cpp · combine/blit.cpp · combine/merge.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 8da3efdd..0e4586ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -115,7 +115,7 @@ liblolcore_sources = \ image/codec/gdiplus-image.cpp image/codec/imlib2-image.cpp \ image/codec/sdl-image.cpp image/codec/ios-image.cpp \ image/codec/zed-image.cpp image/codec/zed-palette-image.cpp \ - image/codec/dummy-image.cpp \ + image/codec/oric-image.cpp image/codec/dummy-image.cpp \ image/color/cie1931.cpp image/color/color.cpp \ image/dither/random.cpp image/dither/ediff.cpp \ image/dither/ostromoukhov.cpp image/dither/ordered.cpp \ diff --git a/src/image/codec/oric.cpp b/src/image/codec/oric-image.cpp similarity index 76% rename from src/image/codec/oric.cpp rename to src/image/codec/oric-image.cpp index 45244f79..322b50ff 100644 --- a/src/image/codec/oric.cpp +++ b/src/image/codec/oric-image.cpp @@ -1,30 +1,24 @@ -/* - * libpipi Pathetic image processing interface library - * Copyright (c) 2004-2008 Sam Hocevar - * All Rights Reserved - * - * $Id$ - * - * This library is free software. It comes without any warranty, to - * the extent permitted by applicable law. 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 - * http://sam.zoy.org/wtfpl/COPYING for more details. - */ - -/* - * oric.c: Oric Atmos import/export functions - */ +// +// Lol Engine +// +// Copyright: (c) 2010-2011 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 +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif -#include "config.h" +#include "core.h" +#include "../../image/image-private.h" -#include -#include -#include -#include +using namespace std; -#include "pipi.h" -#include "pipi-internals.h" +namespace lol +{ /* Image dimensions and recursion depth. DEPTH = 2 is a reasonable value, * DEPTH = 3 gives good quality, and higher values may improve the results @@ -33,86 +27,127 @@ #define HEIGHT 200 #define DEPTH 2 -static int read_screen(char const *name, uint8_t *screen); -static void write_screen(float const *data, uint8_t *screen); +/* + * Image implementation class + */ -pipi_image_t *pipi_load_oric(char const *name) +class OricImageCodec : public ImageCodec { - static uint8_t const pal[32] = - { - 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0xff, 0xff, - 0x00, 0xff, 0x00, 0xff, - 0x00, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0xff, 0xff, - 0xff, 0xff, 0x00, 0xff, - 0xff, 0xff, 0xff, 0xff, - }; +public: + virtual bool Load(Image *image, char const *path); + virtual bool Save(Image *image, char const *path); - pipi_image_t *img; - pipi_pixels_t *p; - uint8_t *screen, *data; - int x, y, i; +private: + static String ReadScreen(char const *name); +}; - screen = malloc(WIDTH * HEIGHT / 6); +DECLARE_IMAGE_CODEC(OricImageCodec, 100) - if(read_screen(name, screen) < 0) +/* + * Public Image class + */ + +bool OricImageCodec::Load(Image *image, char const *path) +{ + static u8vec4 const pal[8] = { - free(screen); - return NULL; - } + u8vec4(0x00, 0x00, 0x00, 0xff), + u8vec4(0x00, 0x00, 0xff, 0xff), + u8vec4(0x00, 0xff, 0x00, 0xff), + u8vec4(0x00, 0xff, 0xff, 0xff), + u8vec4(0xff, 0x00, 0x00, 0xff), + u8vec4(0xff, 0x00, 0xff, 0xff), + u8vec4(0xff, 0xff, 0x00, 0xff), + u8vec4(0xff, 0xff, 0xff, 0xff), + }; - img = pipi_new(WIDTH, HEIGHT); - p = pipi_get_pixels(img, PIPI_PIXELS_RGBA_U8); - data = p->pixels; + String screen = ReadScreen(path); + if (screen.Count() == 0) + return false; + + image->SetSize(ivec2(WIDTH, screen.Count() * 6 / WIDTH)); + + u8vec4 *pixels = image->Lock(); - for(y = 0; y < HEIGHT; y++) + for (int y = 0; y < image->GetSize().y; y++) { int bg = 0, fg = 7; - for(x = 0; x < 40; x++) + for (int x = 0; x < 40; x++) { int col; uint8_t c = screen[y * 40 + x]; - if(c & 0x40) + if (c & 0x40) { - for(i = 0; i < 6; i++) + for (int i = 0; i < 6; i++) { col = (c & (1 << (5 - i))) ? (c & 0x80) ? 7 - fg : fg : (c & 0x80) ? 7 - bg : bg; - memcpy(data + (y * WIDTH + x * 6 + i) * 4, - pal + 4 * col, 4); + pixels[y * WIDTH + x * 6 + i] = pal[col]; } } - else if((c & 0x60) == 0x00) + else if ((c & 0x60) == 0x00) { - if(c & 0x10) + if (c & 0x10) bg = c & 0x7; else fg = c & 0x7; col = (c & 0x80) ? 7 - bg : bg; - for(i = 0; i < 6; i++) - memcpy(data + (y * WIDTH + x * 6 + i) * 4, - pal + 4 * col, 4); + for (int i = 0; i < 6; i++) + pixels[y * WIDTH + x * 6 + i] = pal[col]; } /* else: invalid sequence */ } } - free(screen); + image->Unlock(pixels); + + return true; +} - img->codec_priv = NULL; +bool OricImageCodec::Save(Image *image, char const *path) +{ + return false; +} - img->wrap = 0; - img->u8 = 1; +String OricImageCodec::ReadScreen(char const *name) +{ + File f; + f.Open(name, FileAccess::Read); + String data = f.ReadString(); + f.Close(); - pipi_release_pixels(img, p); + /* Skip the sync bytes */ + if (data[0] != 0x16) + return ""; + int header = 1; + while (data[header] == 0x16) + ++header; + if (data[header] != 0x24) + return ""; + ++header; - return img; + /* Skip the header, ignoring the last byte’s value */ + if (data.Sub(header, 8) != String("\x00\xff\x80\x00\xbf\x3f\xa0\x00", 8)) + return ""; + + /* Skip the file name, including trailing nul char */ + data = data.Sub(header + 8); + int filename_end = data.IndexOf('\0'); + if (filename_end < 0) + return ""; + + /* Read screen data */ + 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) @@ -125,14 +160,14 @@ int pipi_save_oric(pipi_image_t *img, char const *name) size_t len; len = strlen(name); - if(len < 4 || name[len - 4] != '.' + 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) + if (!fp) return -1; fwrite("\x16\x16\x16\x16\x24", 1, 5, fp); @@ -140,7 +175,7 @@ int pipi_save_oric(pipi_image_t *img, char const *name) fwrite(name, 1, len - 4, fp); fwrite("\x00", 1, 1, fp); - if(img->w != WIDTH || img->h != HEIGHT) + if (img->w != WIDTH || img->h != HEIGHT) tmp = pipi_resize_bicubic(img, WIDTH, HEIGHT); else tmp = img; @@ -149,7 +184,7 @@ int pipi_save_oric(pipi_image_t *img, char const *name) screen = malloc(WIDTH * HEIGHT / 6); write_screen(data, screen); pipi_release_pixels(tmp, p); - if(tmp != img) + if (tmp != img) pipi_free(tmp); fwrite(screen, 1, WIDTH * HEIGHT / 6, fp); @@ -164,52 +199,6 @@ int pipi_save_oric(pipi_image_t *img, char const *name) * XXX: The following functions are local. */ -static int read_screen(char const *name, uint8_t *screen) -{ - FILE *fp; - int ch; - - fp = fopen(name, "r"); - if(!fp) - return -1; - - /* Skip the sync bytes */ - ch = fgetc(fp); - if(ch != 0x16) - goto syntax_error; - while((ch = fgetc(fp)) == 0x16) - ; - if(ch != 0x24) - goto syntax_error; - - /* Skip the header, ignoring the last byte’s value */ - if(fgetc(fp) != 0x00 || fgetc(fp) != 0xff || fgetc(fp) != 0x80 - || fgetc(fp) != 0x00 || fgetc(fp) != 0xbf || fgetc(fp) != 0x3f - || fgetc(fp) != 0xa0 || fgetc(fp) != 0x00 || fgetc(fp) == EOF) - goto syntax_error; - - /* Skip the file name, including trailing nul char */ - for(;;) - { - ch = fgetc(fp); - if(ch == EOF) - goto syntax_error; - if(ch == 0x00) - break; - } - - /* Read screen data */ - if(fread(screen, 1, WIDTH * HEIGHT / 6, fp) != WIDTH * HEIGHT / 6) - goto syntax_error; - - fclose(fp); - return 0; - -syntax_error: - fclose(fp); - return -1; -} - /* 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 @@ -253,9 +242,9 @@ static const int palette[8][6 * 3] = /* Set new background and foreground colours according to the given command. */ static inline void domove(uint8_t command, uint8_t *bg, uint8_t *fg) { - if((command & 0x78) == 0x00) + if ((command & 0x78) == 0x00) *fg = command & 0x7; - else if((command & 0x78) == 0x10) + else if ((command & 0x78) == 0x10) *bg = command & 0x7; } @@ -266,8 +255,8 @@ static inline int clamp(int p) { #if 0 /* FIXME: doesn’t give terribly good results on eg. eatme.png */ - if(p < - CLAMP) return - CLAMP; - if(p > 0xffff + CLAMP) return 0xffff + CLAMP; + if (p < - CLAMP) return - CLAMP; + if (p > 0xffff + CLAMP) return 0xffff + CLAMP; #endif return p; } @@ -289,9 +278,9 @@ static inline int geterror(int const *in, int const *inerr, memcpy(tmperr, inerr, 3 * sizeof(int)); memset(tmperr + 3, 0, 8 * 3 * sizeof(int)); - for(i = 0; i < 6; i++) + for (i = 0; i < 6; i++) { - for(c = 0; c < 3; c++) + for (c = 0; c < 3; c++) { /* Experiment shows that this is important at small depths */ int a = clamp(in[i * 3 + c] + tmperr[c]); @@ -304,9 +293,9 @@ static inline int geterror(int const *in, int const *inerr, } } - for(i = 0; i < 4; i++) + for (i = 0; i < 4; i++) { - for(c = 0; c < 3; c++) + for (c = 0; c < 3; c++) { /* Experiment shows that this is important at large depths */ int a = ((in[i * 3 + c] + in[i * 3 + 3 + c] @@ -320,7 +309,7 @@ static inline int geterror(int const *in, int const *inerr, /* Using the diffused error as a perceptual error component is stupid, * because that’s not what it is at all, but I found that it helped a * bit in some cases. */ - for(i = 0; i < 3; i++) + for (i = 0; i < 3; i++) ret += tmperr[i] / 256 * tmperr[i] / 256; memcpy(outerr, tmperr, 3 * sizeof(int)); @@ -348,7 +337,7 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, /* Precompute sub-error for the case where we print pixels (and hence * don’t change the palette). It’s not the exact error because we should * be propagating the error to the first pixel here. */ - if(depth > 0) + if (depth > 0) { int tmp[3] = { 0, 0, 0 }; bestmove(in + 6 * 3, bg, fg, tmp, depth - 1, maxerror, &statice, NULL); @@ -364,7 +353,7 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, besterror = 0x7ffffff; bestcommand = 0x10; memcpy(bestrgb, voidrgb, 6 * 3 * sizeof(int)); - for(j = 0; j < 34; j++) + for (j = 0; j < 34; j++) { static uint8_t const lookup[] = { @@ -382,7 +371,7 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, /* Keeping bg and fg is useless, because we could use standard * pixel printing instead */ - if((command & 0x40) == 0x00 && newbg == bg && newfg == fg) + if ((command & 0x40) == 0x00 && newbg == bg && newfg == fg) continue; /* I *think* having newfg == newbg is useless, too, but I don’t @@ -391,33 +380,33 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, #if 0 /* Bit 6 off and bit 5 on seems illegal */ - if((command & 0x60) == 0x20) + if ((command & 0x60) == 0x20) continue; /* Bits 6 and 5 off and bit 3 on seems illegal */ - if((command & 0x68) == 0x08) + if ((command & 0x68) == 0x08) continue; #endif - if((command & 0xf8) == 0x00) + if ((command & 0xf8) == 0x00) { curerror = voide; rgb = voidrgb; vec = voidvec; } - else if((command & 0xf8) == 0x80) + else if ((command & 0xf8) == 0x80) { curerror = nvoide; rgb = nvoidrgb; vec = nvoidvec; } - else if((command & 0xf8) == 0x10) + else if ((command & 0xf8) == 0x10) { rgb = palette[newbg]; curerror = geterror(in, errvec, rgb, tmpvec); vec = tmpvec; } - else if((command & 0xf8) == 0x90) + else if ((command & 0xf8) == 0x90) { rgb = palette[7 - newbg]; curerror = geterror(in, errvec, rgb, tmpvec); @@ -427,7 +416,7 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, { int const *bgcolor, *fgcolor; - if((command & 0x80) == 0x00) + if ((command & 0x80) == 0x00) { bgcolor = palette[bg]; fgcolor = palette[fg]; } @@ -439,14 +428,14 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, memcpy(tmpvec, errvec, 3 * sizeof(int)); curerror = 0; - for(i = 0; i < 6; i++) + for (i = 0; i < 6; i++) { int vec1[3], vec2[3]; int smalle1 = 0, smalle2 = 0; memcpy(vec1, tmpvec, 3 * sizeof(int)); memcpy(vec2, tmpvec, 3 * sizeof(int)); - for(c = 0; c < 3; c++) + for (c = 0; c < 3; c++) { int delta1, delta2; delta1 = clamp(in[i * 3 + c] + tmpvec[c]) - bgcolor[c]; @@ -457,7 +446,7 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, smalle2 += delta2 / 256 * delta2; } - if(smalle1 < smalle2) + if (smalle1 < smalle2) { memcpy(tmpvec, vec1, 3 * sizeof(int)); memcpy(tmprgb + i * 3, bgcolor, 3 * sizeof(int)); @@ -477,7 +466,7 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, vec = tmpvec; } - if(curerror > besterror) + if (curerror > besterror) continue; /* Try to avoid bad decisions now that will have a high cost @@ -485,9 +474,9 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, * the current error. */ curerror = curerror * 3 / 4; - if(depth == 0) + if (depth == 0) suberror = 0; /* It’s the end of the tree */ - else if((command & 0x68) == 0x00) + else if ((command & 0x68) == 0x00) { bestmove(in + 6 * 3, newbg, newfg, vec, depth - 1, besterror - curerror, &suberror, NULL); @@ -496,16 +485,16 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, /* Slight penalty for colour changes; they're hard to revert. The * value of 2 was determined empirically. 1.5 is not enough and * 3 is too much. */ - if(newbg != bg) + if (newbg != bg) suberror = suberror * 10 / 8; - else if(newfg != fg) + else if (newfg != fg) suberror = suberror * 9 / 8; #endif } else suberror = statice; - if(curerror + suberror < besterror) + if (curerror + suberror < besterror) { besterror = curerror + suberror; bestcommand = command; @@ -514,7 +503,7 @@ static uint8_t bestmove(int const *in, uint8_t bg, uint8_t fg, } *error = besterror; - if(out) + if (out) memcpy(out, bestrgb, 6 * 3 * sizeof(int)); return bestcommand; @@ -534,20 +523,20 @@ static void write_screen(float const *data, uint8_t *screen) memset(dst, 0, (WIDTH + 1) * (HEIGHT + 1) * 3 * sizeof(int)); /* Import pixels into our custom format */ - for(y = 0; y < HEIGHT; y++) - for(x = 0; x < WIDTH; x++) - for(c = 0; c < 3; c++) + 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)]; /* Let the fun begin */ - for(y = 0; y < HEIGHT; y++) + for (y = 0; y < HEIGHT; y++) { uint8_t bg = 0, fg = 7; //fprintf(stderr, "\rProcessing... %i%%", (y * 100 + 99) / HEIGHT); - for(x = 0; x < WIDTH; x += 6) + for (x = 0; x < WIDTH; x += 6) { int errvec[3] = { 0, 0, 0 }; int dummy, i; @@ -561,9 +550,9 @@ static void write_screen(float const *data, uint8_t *screen) command = bestmove(srcl, bg, fg, errvec, depth, 0x7fffff, &dummy, dstl); /* Propagate error */ - for(c = 0; c < 3; c++) + for (c = 0; c < 3; c++) { - for(i = 0; i < 6; i++) + for (i = 0; i < 6; i++) { int error = srcl[i * 3 + c] - dstl[i * 3 + c]; srcl[i * 3 + c + 3] = @@ -573,7 +562,7 @@ static void write_screen(float const *data, uint8_t *screen) srcl[i * 3 + c + stride + 3] += error * FS3 / FSX; } - for(i = -1; i < 7; i++) + for (i = -1; i < 7; i++) srcl[i * 3 + c + stride] = clamp(srcl[i * 3 + c + stride]); } /* Iterate */ @@ -585,4 +574,7 @@ static void write_screen(float const *data, uint8_t *screen) //fprintf(stderr, " done.\n"); } +#endif + +} /* namespace lol */ diff --git a/src/image/image.cpp b/src/image/image.cpp index 2fdfa5f1..db2468a0 100644 --- a/src/image/image.cpp +++ b/src/image/image.cpp @@ -35,7 +35,6 @@ static bool RegisterAllCodecs(Array &codeclist) #if defined __ANDROID__ REGISTER_IMAGE_CODEC(AndroidImageCodec) #endif - REGISTER_IMAGE_CODEC(DummyImageCodec) #if defined USE_GDIPLUS REGISTER_IMAGE_CODEC(GdiPlusImageCodec) #endif @@ -48,11 +47,13 @@ static bool RegisterAllCodecs(Array &codeclist) #if defined USE_SDL_IMAGE REGISTER_IMAGE_CODEC(SdlImageCodec) #endif - REGISTER_IMAGE_CODEC(ZedImageCodec) - REGISTER_IMAGE_CODEC(ZedPaletteImageCodec) #if defined USE_IMLIB2 REGISTER_IMAGE_CODEC(Imlib2ImageCodec) #endif + REGISTER_IMAGE_CODEC(DummyImageCodec) + REGISTER_IMAGE_CODEC(ZedImageCodec) + REGISTER_IMAGE_CODEC(ZedPaletteImageCodec) + REGISTER_IMAGE_CODEC(OricImageCodec) return true; }