Browse Source

image: we can now read Oric .tap files.

undefined
Sam Hocevar 10 years ago
parent
commit
478b74c6fe
4 changed files with 151 additions and 159 deletions
  1. +0
    -1
      TODO
  2. +1
    -1
      src/Makefile.am
  3. +146
    -154
      src/image/codec/oric-image.cpp
  4. +4
    -3
      src/image/image.cpp

+ 0
- 1
TODO View File

@@ -23,7 +23,6 @@ Image:
· codec/coreimage.h
· codec.cpp
· codec/jpeg.cpp
· codec/oric.cpp
· colorstring.cpp
· combine/blit.cpp
· combine/merge.cpp


+ 1
- 1
src/Makefile.am View File

@@ -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 \


src/image/codec/oric.cpp → src/image/codec/oric-image.cpp View File

@@ -1,30 +1,24 @@
/*
* libpipi Pathetic image processing interface library
* Copyright (c) 2004-2008 Sam Hocevar <sam@zoy.org>
* 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 <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
// 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
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<PixelFormat::RGBA_8>();

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 */


+ 4
- 3
src/image/image.cpp View File

@@ -35,7 +35,6 @@ static bool RegisterAllCodecs(Array<ImageCodec *> &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<ImageCodec *> &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;
}


Loading…
Cancel
Save