summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Shaw <jshaw@netsurf-browser.org>2008-03-27 21:24:20 (GMT)
committer James Shaw <jshaw@netsurf-browser.org>2008-03-27 21:24:20 (GMT)
commit270bf2ad00b469eac1e33eb82c3bfea27f015061 (patch)
tree3cf78700f86496ed75ffee2a0d162cba1d8c0e5a
parent704f880692d5ac4597ec3cadbf3aeb48c8ba91cb (diff)
downloadlibrosprite-270bf2ad00b469eac1e33eb82c3bfea27f015061.tar.gz
librosprite-270bf2ad00b469eac1e33eb82c3bfea27f015061.tar.bz2
More docs, add function decls for private functions, put them in a more
logical ordering svn path=/import/jshaw/libsprite/; revision=10019
-rw-r--r--trunk/Makefile4
-rw-r--r--trunk/example.c173
-rw-r--r--trunk/librosprite.c683
-rw-r--r--trunk/librosprite.h33
4 files changed, 470 insertions, 423 deletions
diff --git a/trunk/Makefile b/trunk/Makefile
index 873f212..0dcabcb 100644
--- a/trunk/Makefile
+++ b/trunk/Makefile
@@ -8,9 +8,9 @@ CFLAGS = -Wall -Wextra -Wundef -Wpointer-arith -Wcast-align \
-Wnested-externs -Werror -pedantic -std=c99 \
-Wno-format-zero-length -Wformat-security -Wstrict-aliasing=2 \
-Wmissing-format-attribute -Wunused -Wunreachable-code \
- -Wformat=2 -Werror-implicit-function-declaration
+ -Wformat=2 -Werror-implicit-function-declaration \
+ -Wmissing-declarations -Wmissing-prototypes
LDFLAGS = -L./
-#-Wmissing-declarations -Wmissing-prototypes
all: example palette2c
diff --git a/trunk/example.c b/trunk/example.c
index 5de3139..25fff0a 100644
--- a/trunk/example.c
+++ b/trunk/example.c
@@ -4,93 +4,16 @@
#include "librosprite.h"
-/* color is 0xrrggbbaa */
-void sdl_draw_pixel(SDL_Surface* surface, uint32_t x, uint32_t y, uint32_t color)
-{
- uint32_t* pixel = ((uint32_t*) (surface->pixels)) + (y * surface->pitch/4) + x;
- /* pretty sure SDL can do this, but can't figure out how */
- uint32_t bg_color = ((int) (x / 4.0) + ((int)(y / 4.0) % 2)) % 2 ? 0x99 : 0x66;
-
- uint32_t alpha = color & 0x000000ff;
- uint32_t r = (color & 0xff000000) >> 24;
- uint32_t g = (color & 0x00ff0000) >> 16;
- uint32_t b = (color & 0x0000ff00) >> 8;
- r = ((alpha / 255.0) * r) + (((255-alpha) / 255.0) * bg_color);
- g = ((alpha / 255.0) * g) + (((255-alpha) / 255.0) * bg_color);
- b = ((alpha / 255.0) * b) + (((255-alpha) / 255.0) * bg_color);
- uint32_t mapped_color = SDL_MapRGB(surface->format, r, g, b);
-
- *pixel = mapped_color;
-}
-
-void sdl_blank(SDL_Surface* surface)
-{
- for (uint32_t y = 0; y < (uint32_t) surface->h; y++) {
- for (uint32_t x = 0; x < (uint32_t) surface->w; x++) {
- sdl_draw_pixel(surface, x, y, (uint32_t) ((int) (x / 4.0) + ((int)(y / 4.0) % 2)) % 2 ? 0x999999ff : 0x666666ff);
- }
- }
-}
-
-int load_file_to_memory(const char *filename, uint8_t **result)
-{
- int size = 0;
- FILE *f = fopen(filename, "rb");
- if (f == NULL)
- {
- *result = NULL;
- return -1; // -1 means file opening fail
- }
- fseek(f, 0, SEEK_END);
- size = ftell(f);
- fseek(f, 0, SEEK_SET);
- *result = (uint8_t *)malloc(size+1);
- if ((unsigned int) size != fread(*result, sizeof(char), size, f))
- {
- free(*result);
- return -2; // -2 means file reading fail
- }
- fclose(f);
- (*result)[size] = 0;
- return size;
-}
-
-int create_file_context(char* filename, void** result)
-{
- FILE *f = fopen(filename, "rb");
- if (!f) {
- *result = NULL;
- return -1;
- }
-
- struct rosprite_file_context* ctx;
- if (rosprite_create_file_context(f, &ctx) != ROSPRITE_OK) {
- return -1;
- }
- *result = ctx;
-
- return 0;
-}
-
-int create_mem_context(char* filename, void** result)
-{
- uint8_t* content;
-
- int size = load_file_to_memory(filename, &content);
- if (size < 0) return -1;
- struct rosprite_mem_context* ctx;
- if (rosprite_create_mem_context(content, size, &ctx) != ROSPRITE_OK) {
- return -1;
- }
- *result = ctx;
-
- return 0;
-}
+void sdl_draw_pixel(SDL_Surface* surface, uint32_t x, uint32_t y, uint32_t color);
+void sdl_blank(SDL_Surface* surface);
+int load_file_to_memory(const char *filename, uint8_t **result);
+int create_file_context(char* filename, void** result);
+int create_mem_context(char* filename, void** result);
int main(int argc, char *argv[])
{
if (argc < 2) {
- printf("Usage: example spritefile\n");
+ fprintf(stderr, "Usage: example spritefile\n");
exit(EXIT_FAILURE);
}
@@ -106,7 +29,8 @@ int main(int argc, char *argv[])
printf("Loading %s\n", filename);
struct rosprite_area* sprite_area;
- if (!rosprite_load(rosprite_file_reader, ctx, &sprite_area)) {
+ if (rosprite_load(rosprite_file_reader, ctx, &sprite_area) != ROSPRITE_OK) {
+ fprintf(stderr, "Error loading spritefile\n");
exit(EXIT_FAILURE);
};
printf("sprite_count %u\n", sprite_area->sprite_count);
@@ -151,4 +75,85 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS;
}
+int create_file_context(char* filename, void** result)
+{
+ FILE *f = fopen(filename, "rb");
+ if (!f) {
+ *result = NULL;
+ return -1;
+ }
+ struct rosprite_file_context* ctx;
+ if (rosprite_create_file_context(f, &ctx) != ROSPRITE_OK) {
+ return -1;
+ }
+ *result = ctx;
+
+ return 0;
+}
+
+int create_mem_context(char* filename, void** result)
+{
+ uint8_t* content;
+
+ int size = load_file_to_memory(filename, &content);
+ if (size < 0) return -1;
+ struct rosprite_mem_context* ctx;
+ if (rosprite_create_mem_context(content, size, &ctx) != ROSPRITE_OK) {
+ return -1;
+ }
+ *result = ctx;
+
+ return 0;
+}
+
+int load_file_to_memory(const char *filename, uint8_t **result)
+{
+ int size = 0;
+ FILE *f = fopen(filename, "rb");
+ if (f == NULL)
+ {
+ *result = NULL;
+ return -1; // -1 means file opening fail
+ }
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ *result = (uint8_t *)malloc(size+1);
+ if ((unsigned int) size != fread(*result, sizeof(char), size, f))
+ {
+ free(*result);
+ return -2; // -2 means file reading fail
+ }
+ fclose(f);
+ (*result)[size] = 0;
+ return size;
+}
+
+/* color is 0xrrggbbaa */
+void sdl_draw_pixel(SDL_Surface* surface, uint32_t x, uint32_t y, uint32_t color)
+{
+ uint32_t* pixel = ((uint32_t*) (surface->pixels)) + (y * surface->pitch/4) + x;
+ /* pretty sure SDL can do this, but can't figure out how */
+ uint32_t bg_color = ((int) (x / 4.0) + ((int)(y / 4.0) % 2)) % 2 ? 0x99 : 0x66;
+
+ uint32_t alpha = color & 0x000000ff;
+ uint32_t r = (color & 0xff000000) >> 24;
+ uint32_t g = (color & 0x00ff0000) >> 16;
+ uint32_t b = (color & 0x0000ff00) >> 8;
+ r = ((alpha / 255.0) * r) + (((255-alpha) / 255.0) * bg_color);
+ g = ((alpha / 255.0) * g) + (((255-alpha) / 255.0) * bg_color);
+ b = ((alpha / 255.0) * b) + (((255-alpha) / 255.0) * bg_color);
+ uint32_t mapped_color = SDL_MapRGB(surface->format, r, g, b);
+
+ *pixel = mapped_color;
+}
+
+void sdl_blank(SDL_Surface* surface)
+{
+ for (uint32_t y = 0; y < (uint32_t) surface->h; y++) {
+ for (uint32_t x = 0; x < (uint32_t) surface->w; x++) {
+ sdl_draw_pixel(surface, x, y, (uint32_t) ((int) (x / 4.0) + ((int)(y / 4.0) % 2)) % 2 ? 0x999999ff : 0x666666ff);
+ }
+ }
+}
diff --git a/trunk/librosprite.c b/trunk/librosprite.c
index fcea0d0..bc23244 100644
--- a/trunk/librosprite.c
+++ b/trunk/librosprite.c
@@ -7,10 +7,14 @@
#include "librosprite.h"
-/* reads four bytes, 00, 11, 22 and 33, of a byte array b to give 0x33221100 */
+/**
+ * Reads four bytes, 00, 11, 22 and 33, of a byte array b to give 0x33221100.
+ */
#define BTUINT(b) (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24))
-/* reverse the byte order of a word such that 0xAABBCCDD becomes 0xDDCCBBAA */
+/**
+ * Reverse the byte order of a word such that 0xAABBCCDD becomes 0xDDCCBBAA.
+ */
#define BSWAP(word) (((word & (0x000000ff)) << 24) | ((word & 0x0000ff00) << 8) | ((word & 0x00ff0000) >> 8) | ((word & 0xff000000) >> 24))
#define ERRCHK(x) do { \
@@ -196,6 +200,107 @@ static const uint32_t sprite_8bpp_palette[] = {
0xccccccff, 0xddddddff, 0xeeeeeeff, 0xffffffff
};
+static inline rosprite_error rosprite_read_word(reader reader, void* ctx, uint32_t* result);
+static rosprite_error rosprite_get_mode(uint32_t spriteMode, struct rosprite_mode* result);
+static uint32_t rosprite_palette_lookup(struct rosprite* sprite, uint32_t pixel);
+static inline uint32_t rosprite_cmyk_to_rgb(uint32_t cmyk);
+static uint32_t rosprite_next_mask_pixel(uint8_t* mask, struct rosprite_mask_state* mask_state);
+static rosprite_error rosprite_load_high_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header);
+static rosprite_error rosprite_load_low_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header);
+rosprite_error rosprite_load_sprite(reader reader, void* ctx, struct rosprite** result);
+static rosprite_error rosprite_init_mask_state(struct rosprite* sprite, struct rosprite_header* header, uint8_t* mask, struct rosprite_mask_state** result);
+static uint32_t rosprite_upscale_color(uint32_t pixel, struct rosprite_mode* mode, bool* has_alpha_pixel_data);
+static inline void rosprite_fix_alpha(uint32_t* image, uint32_t pixels);
+
+rosprite_error rosprite_load(reader reader, void* ctx, struct rosprite_area** result)
+{
+ struct rosprite_area* sprite_area = malloc(sizeof(struct rosprite_area));
+
+ ERRCHK(rosprite_read_word(reader, ctx, &(sprite_area->sprite_count)));
+
+ uint32_t firstSpriteOffset, firstFreeWordOffset;
+ ERRCHK(rosprite_read_word(reader, ctx, &firstSpriteOffset));
+ ERRCHK(rosprite_read_word(reader, ctx, &firstFreeWordOffset)); /* TODO: use this for some sanity checking? */
+ sprite_area->extension_size = 16 - firstSpriteOffset;
+
+ sprite_area->extension_words = NULL;
+ if (sprite_area->extension_size > 0) {
+ sprite_area->extension_words = malloc(sprite_area->extension_size);
+ int bytes_read = reader(sprite_area->extension_words, (size_t) (sprite_area->extension_size), ctx);
+ if (bytes_read < (signed long) sprite_area->extension_size) {
+ return ROSPRITE_EOF;
+ }
+ }
+
+ sprite_area->sprites = malloc(sizeof(struct rosprite*) * sprite_area->sprite_count); /* allocate array of pointers */
+ for (uint32_t i = 0; i < sprite_area->sprite_count; i++) {
+ struct rosprite* sprite;
+ ERRCHK(rosprite_load_sprite(reader, ctx, &sprite));
+ sprite_area->sprites[i] = sprite;
+ }
+
+ *result = sprite_area;
+
+ return ROSPRITE_OK;
+}
+
+void rosprite_destroy_sprite_area(struct rosprite_area* sprite_area)
+{
+ for (uint32_t i = 0; i < sprite_area->sprite_count; i++) {
+ struct rosprite* sprite = sprite_area->sprites[i];
+ if (sprite->has_palette) free(sprite->palette);
+ free(sprite->image);
+ free(sprite);
+ }
+
+ free(sprite_area->sprites);
+ if (sprite_area->extension_size > 0) free(sprite_area->extension_words);
+ free(sprite_area);
+}
+
+rosprite_error rosprite_load_palette(reader reader, void* ctx, struct rosprite_palette** result)
+{
+ /* TODO: currently assume palette has linear entries (2nd byte in is 00, 01, 02 etc) */
+ struct rosprite_palette* palette = malloc(sizeof(struct rosprite_palette));
+
+ palette->palette = malloc(sizeof(uint32_t) * 256); /* allocate 256 whether we need them all or not */
+
+ uint32_t c = 0;
+ uint8_t b[6];
+
+ unsigned int bytesRead = reader(b, 6, ctx);
+ assert(bytesRead % 6 == 0);
+ while (bytesRead == 6) {
+ assert(b[0] == 19); /* VDU 19 */
+
+ /* only process logical colours */
+ if (b[2] == 16) {
+ /*assert(c == b[1]);*/
+
+ uint32_t entry = (b[3] << 24) | (b[4] << 16) | (b[5] << 8) | 0xff; /* last byte is alpha */
+ palette->palette[c] = entry;
+
+ c++;
+ assert(c <= 256);
+ }
+
+ bytesRead = reader(b, 6, ctx);
+ assert(bytesRead % 6 == 0);
+ }
+
+ palette->size = c;
+
+ *result = palette;
+
+ return ROSPRITE_OK;
+}
+
+void rosprite_destroy_palette(struct rosprite_palette* palette)
+{
+ free(palette->palette);
+ free(palette);
+}
+
rosprite_error rosprite_create_file_context(FILE* f, struct rosprite_file_context** result)
{
struct rosprite_file_context* ctx = malloc(sizeof(struct rosprite_file_context));
@@ -253,17 +358,104 @@ int rosprite_mem_reader(uint8_t* buf, size_t count, void* ctx)
return copy_size;
}
-rosprite_error sprite_read_word(reader reader, void* ctx, uint32_t* result)
+rosprite_error rosprite_load_sprite(reader reader, void* ctx, struct rosprite** result)
{
- unsigned char b[4];
- if (reader(b, 4, ctx) < 4) {
- return ROSPRITE_EOF;
+ uint32_t nextSpriteOffset;
+ ERRCHK(rosprite_read_word(reader, ctx, &nextSpriteOffset));
+
+ struct rosprite* sprite = malloc(sizeof(struct rosprite));
+ struct rosprite_header* header = malloc(sizeof(struct rosprite_header));
+
+ reader(sprite->name, 12, ctx);
+ sprite->name[12] = '\0';
+
+ ERRCHK(rosprite_read_word(reader, ctx, &header->width_words)); /* file has width - 1 and height - 1 */
+ header->width_words += 1;
+ ERRCHK(rosprite_read_word(reader, ctx, &(sprite->height)));
+ sprite->height += 1;
+ ERRCHK(rosprite_read_word(reader, ctx, &(header->first_used_bit))); /* old format only (spriteType = 0) */
+ ERRCHK(rosprite_read_word(reader, ctx, &(header->last_used_bit)));
+
+ uint32_t imageOffset;
+ ERRCHK(rosprite_read_word(reader, ctx, &imageOffset));
+ assert(imageOffset >= 44); /* should never be smaller than the size of the header) */
+
+ uint32_t maskOffset, spriteModeWord;
+ ERRCHK(rosprite_read_word(reader, ctx, &maskOffset));
+ ERRCHK(rosprite_read_word(reader, ctx, &spriteModeWord));
+
+ ERRCHK(rosprite_get_mode(spriteModeWord, &(sprite->mode)));
+
+ /* TODO left-hand wastage */
+
+ assert((header->last_used_bit + 1) % sprite->mode.colorbpp == 0);
+ /*assert(header->width_words % sprite->mode->colorbpp == 0);*/
+ sprite->width = (header->width_words * 32 - header->first_used_bit - (31 - header->last_used_bit)) / sprite->mode.colorbpp;
+
+ sprite->palettesize = imageOffset - 44;
+ sprite->has_palette = (sprite->palettesize > 0);
+
+ /* sprite has no mask if imageOffset == maskOffset */
+ if (imageOffset == maskOffset) {
+ sprite->has_mask = false;
+ header->image_size = nextSpriteOffset - 44 - sprite->palettesize;
+ header->mask_size = 0;
+ } else {
+ sprite->has_mask = true;
+ header->image_size = maskOffset - imageOffset;
+ header->mask_size = nextSpriteOffset - 44 - sprite->palettesize - header->image_size;
}
- *result = BTUINT(b);
+
+ if (sprite->has_palette) {
+ assert(sprite->palettesize % 8 == 0);
+ sprite->palette = malloc(sizeof(uint32_t) * sprite->palettesize);
+ uint32_t paletteEntries = sprite->palettesize / 8;
+
+ /* Each palette entry is two words big
+ * The second word is a duplicate of the first
+ * I think this is in case you ever wanted flashing colours
+ * PRM1-730
+ */
+ for (uint32_t j = 0; j < paletteEntries; j++) {
+ uint32_t word1, word2;
+ ERRCHK(rosprite_read_word(reader, ctx, &word1));
+ ERRCHK(rosprite_read_word(reader, ctx, &word2));
+ assert(word1 == word2); /* if they aren't equal, flashing colours are desired, which we don't support */
+
+ /* swap rr and bb parts -- PRM1-731 */
+ uint32_t entry = ((word1 & 0xff000000) >> 16) | (word1 & 0x00ff0000) | ((word1 & 0x0000ff00) << 16) | 0xff;
+ sprite->palette[j] = entry;
+ }
+ }
+
+ uint8_t* image = malloc(header->image_size);
+ reader(image, header->image_size, ctx);
+
+ uint8_t* mask = NULL;
+ if (sprite->has_mask) {
+ mask = malloc(header->mask_size);
+ reader(mask, header->mask_size, ctx);
+ }
+
+ /* sanity check image_size */
+ assert((header->width_words) * 4 * (sprite->height) == header->image_size);
+ /* TODO: sanity check mask_size */
+ if (sprite->mode.colorbpp > 8) {
+ ERRCHK(rosprite_load_high_color(image, mask, sprite, header));
+ } else {
+ ERRCHK(rosprite_load_low_color(image, mask, sprite, header));
+ }
+
+ free(image);
+ free(mask);
+ free(header);
+
+ *result = sprite;
+
return ROSPRITE_OK;
}
-static rosprite_error sprite_get_mode(uint32_t spriteMode, struct rosprite_mode* result)
+static rosprite_error rosprite_get_mode(uint32_t spriteMode, struct rosprite_mode* result)
{
struct rosprite_mode mode;
@@ -303,7 +495,6 @@ static rosprite_error sprite_get_mode(uint32_t spriteMode, struct rosprite_mode*
return ROSPRITE_BADMODE;
}
} else {
- /* clone station mode and return */
assert(spriteMode < 256); /* don't think you can have modes over 255? */
mode = oldmodes[spriteMode];
@@ -319,176 +510,11 @@ static rosprite_error sprite_get_mode(uint32_t spriteMode, struct rosprite_mode*
return ROSPRITE_OK;
}
-static uint32_t sprite_palette_lookup(struct rosprite* sprite, uint32_t pixel)
-{
- uint32_t translated_pixel;
- /* because we're dealing with 8bpp or less */
- if (sprite->has_palette) {
- assert(pixel <= sprite->palettesize); /* TODO: what to do if your color depth is bigger than palette? */
- translated_pixel = sprite->palette[pixel];
- } else {
- switch (sprite->mode.colorbpp) {
- case 8:
- assert(pixel < 256);
- translated_pixel = sprite_8bpp_palette[pixel];
- break;
- case 4:
- assert(pixel < 16);
- translated_pixel = sprite_4bpp_palette[pixel];
- break;
- case 2:
- assert(pixel < 4);
- translated_pixel = sprite_2bpp_palette[pixel];
- break;
- case 1:
- assert(pixel < 2);
- translated_pixel = sprite_1bpp_palette[pixel];
- break;
- default:
- assert(false);
- }
- }
- return translated_pixel;
-}
-
-static inline uint32_t sprite_cmyk_to_rgb(uint32_t cmyk)
-{
- uint8_t c = cmyk & 0xff;
- uint8_t m = (cmyk & 0xff00) >> 8;
- uint8_t y = (cmyk & 0xff0000) >> 16;
- uint8_t k = cmyk >> 24;
-
- /* Convert to CMY colourspace */
- uint8_t C = c + k;
- uint8_t M = m + k;
- uint8_t Y = y + k;
-
- /* And to RGB */
- uint8_t r = 255 - C;
- uint8_t g = 255 - M;
- uint8_t b = 255 - Y;
-
- return r << 24 | g << 16 | b << 8;
-}
-
-static uint32_t sprite_upscale_color(uint32_t pixel, struct rosprite_mode* mode, bool* has_alpha_pixel_data)
-{
- switch (mode->colorbpp) {
- case 32:
- if (mode->color_model == ROSPRITE_RGB) {
- /* swap from 0xAABBGGRR to 0xRRGGBBAA */
- pixel = BSWAP(pixel);
-
- uint8_t alpha = pixel & 0xff;
- if (alpha == 0x00) {
- if (!(*has_alpha_pixel_data)) {
- pixel = pixel | 0xff;
- }
- } else {
- *has_alpha_pixel_data = true;
- }
- return pixel;
- } else {
- return sprite_cmyk_to_rgb(pixel);
- }
- case 24:
- /* reverse byte order */
- return BSWAP(pixel);
- case 16:
- /* incoming format is b_00000000000000000bbbbbgggggrrrrr */
- {
- uint8_t red = pixel & 31;
- uint8_t green = (pixel & (31 << 5)) >> 5;
- uint8_t blue = (pixel & (31 << 10)) >> 10;
-
- /* sanity check */
- assert(red < 32);
- assert(green < 32);
- assert(blue < 32);
-
- pixel = (sprite_16bpp_translate[red] << 24)
- | (sprite_16bpp_translate[green] << 16)
- | (sprite_16bpp_translate[blue] << 8);
- return pixel;
- }
- case 8:
- case 4:
- case 2:
- case 1:
- assert(false); /* shouldn't need to call for <= 8bpp, since a palette lookup will return 32bpp */
- default:
- assert(false); /* unknown bpp */
- }
-}
-
-static inline void sprite_fix_alpha(uint32_t* image, uint32_t pixels)
-{
- for (uint32_t i = 0; i <= pixels; i++) {
- image[i] = image[i] & 0xffffff00;
- }
-}
-
-static rosprite_error sprite_init_mask_state(struct rosprite* sprite, struct rosprite_header* header, uint8_t* mask, struct rosprite_mask_state** result)
-{
- struct rosprite_mask_state* mask_state = malloc(sizeof(struct rosprite_mask_state));
- if (!mask_state) return ROSPRITE_NOMEM;
-
- mask_state->x = header->first_used_bit;
- mask_state->y = 0;
- mask_state->first_used_bit = header->first_used_bit;
- mask_state->row_max_bit = sprite->width * sprite->mode.mask_width;
- mask_state->height = sprite->height;
- mask_state->bpp = sprite->mode.mask_width;
- mask_state->current_word = BTUINT(mask);
- mask_state->current_byte_index = 4;
-
- *result = mask_state;
-
- return ROSPRITE_OK;
-}
-
-/* Get the next mask byte.
- * Mask of 0xff denotes 100% opaque, 0x00 denotes 100% transparent
- */
-static uint32_t sprite_next_mask_pixel(uint8_t* mask, struct rosprite_mask_state* mask_state)
-{
- /* a 1bpp mask (for new mode sprites), each row is word aligned (therefore potential righthand wastage */
- const uint32_t bitmask = (1 << mask_state->bpp) - 1;
- const uint32_t offset_into_word = mask_state->x % 32;
- uint32_t pixel = (mask_state->current_word & (bitmask << offset_into_word)) >> offset_into_word;
-
- if (mask_state->x + mask_state->bpp < mask_state->row_max_bit && offset_into_word + mask_state->bpp == 32) {
- mask_state->current_word = BTUINT((mask + mask_state->current_byte_index));
- mask_state->current_byte_index += 4;
- }
-
- mask_state->x += mask_state->bpp;
- if (mask_state->x >= mask_state->row_max_bit) {
- mask_state->x = mask_state->first_used_bit;
-
- if (mask_state->y + 1 < mask_state->height) {
- mask_state->current_word = BTUINT((mask + mask_state->current_byte_index));
- mask_state->current_byte_index += 4;
- }
-
- mask_state->y++;
- }
-
- /*
- * if the mask is 1bpp, make sure the whole byte is 0x00 or 0xff
- */
- if (mask_state->bpp < 8) {
- pixel = (pixel & 1) ? 0xff : 0x00;
- }
-
- return pixel;
-}
-
-static rosprite_error sprite_load_high_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header)
+static rosprite_error rosprite_load_high_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header)
{
struct rosprite_mask_state* mask_state = NULL;
if (sprite->has_mask) {
- ERRCHK(sprite_init_mask_state(sprite, header, mask, &mask_state));
+ ERRCHK(rosprite_init_mask_state(sprite, header, mask, &mask_state));
}
sprite->image = malloc(sprite->width * sprite->height * 4); /* all image data is 32bpp going out */
@@ -513,12 +539,12 @@ static rosprite_error sprite_load_high_color(uint8_t* image_in, uint8_t* mask, s
}
bool old_has_alpha = has_alpha_pixel_data;
- pixel = sprite_upscale_color(pixel, &(sprite->mode), &has_alpha_pixel_data);
+ pixel = rosprite_upscale_color(pixel, &(sprite->mode), &has_alpha_pixel_data);
if (old_has_alpha != has_alpha_pixel_data) {
- sprite_fix_alpha(sprite->image, (y * sprite->width) + x_pixels - 1);
+ rosprite_fix_alpha(sprite->image, (y * sprite->width) + x_pixels - 1);
}
if (sprite->has_mask) {
- uint8_t mask_pixel = sprite_next_mask_pixel(mask, mask_state);
+ uint8_t mask_pixel = rosprite_next_mask_pixel(mask, mask_state);
pixel = (pixel & 0xffffff00) | mask_pixel;
}
sprite->image[y*sprite->width + x_pixels] = pixel;
@@ -535,11 +561,18 @@ static rosprite_error sprite_load_high_color(uint8_t* image_in, uint8_t* mask, s
return ROSPRITE_OK;
}
-static rosprite_error sprite_load_low_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header)
+static inline void rosprite_fix_alpha(uint32_t* image, uint32_t pixels)
+{
+ for (uint32_t i = 0; i <= pixels; i++) {
+ image[i] = image[i] & 0xffffff00;
+ }
+}
+
+static rosprite_error rosprite_load_low_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header)
{
struct rosprite_mask_state* mask_state = NULL;
if (sprite->has_mask) {
- ERRCHK(sprite_init_mask_state(sprite, header, mask, &mask_state));
+ ERRCHK(rosprite_init_mask_state(sprite, header, mask, &mask_state));
}
sprite->image = malloc(sprite->width * sprite->height * 4); /* all image data is 32bpp going out */
@@ -558,9 +591,9 @@ static rosprite_error sprite_load_low_color(uint8_t* image_in, uint8_t* mask, st
const uint32_t offset_into_word = x % 32;
uint32_t pixel = (currentword & (bitmask << offset_into_word)) >> offset_into_word;
- pixel = sprite_palette_lookup(sprite, pixel); /* lookup returns 32bpp */
+ pixel = rosprite_palette_lookup(sprite, pixel); /* lookup returns 32bpp */
if (sprite->has_mask) {
- uint8_t mask_pixel = sprite_next_mask_pixel(mask, mask_state);
+ uint8_t mask_pixel = rosprite_next_mask_pixel(mask, mask_state);
pixel = (pixel & 0xffffff00) | mask_pixel;
}
sprite->image[y*sprite->width + x_pixels] = pixel;
@@ -585,188 +618,170 @@ static rosprite_error sprite_load_low_color(uint8_t* image_in, uint8_t* mask, st
return ROSPRITE_OK;
}
-rosprite_error sprite_load_sprite(reader reader, void* ctx, struct rosprite** result)
+static uint32_t rosprite_palette_lookup(struct rosprite* sprite, uint32_t pixel)
{
- uint32_t nextSpriteOffset;
- sprite_read_word(reader, ctx, &nextSpriteOffset);
-
- struct rosprite* sprite = malloc(sizeof(struct rosprite));
- struct rosprite_header* header = malloc(sizeof(struct rosprite_header));
-
- reader(sprite->name, 12, ctx);
- sprite->name[12] = '\0';
-
- ERRCHK(sprite_read_word(reader, ctx, &header->width_words)); /* file has width - 1 and height - 1 */
- header->width_words += 1;
- ERRCHK(sprite_read_word(reader, ctx, &(sprite->height)));
- sprite->height += 1;
- ERRCHK(sprite_read_word(reader, ctx, &(header->first_used_bit))); /* old format only (spriteType = 0) */
- ERRCHK(sprite_read_word(reader, ctx, &(header->last_used_bit)));
-
- uint32_t imageOffset;
- ERRCHK(sprite_read_word(reader, ctx, &imageOffset));
- assert(imageOffset >= 44); /* should never be smaller than the size of the header) */
-
- uint32_t maskOffset, spriteModeWord;
- ERRCHK(sprite_read_word(reader, ctx, &maskOffset));
- ERRCHK(sprite_read_word(reader, ctx, &spriteModeWord));
-
- ERRCHK(sprite_get_mode(spriteModeWord, &(sprite->mode)));
-
- /* TODO left-hand wastage */
-
- assert((header->last_used_bit + 1) % sprite->mode.colorbpp == 0);
- /*assert(header->width_words % sprite->mode->colorbpp == 0);*/
- sprite->width = (header->width_words * 32 - header->first_used_bit - (31 - header->last_used_bit)) / sprite->mode.colorbpp;
-
- sprite->palettesize = imageOffset - 44;
- sprite->has_palette = (sprite->palettesize > 0);
-
- /* sprite has no mask if imageOffset == maskOffset */
- if (imageOffset == maskOffset) {
- sprite->has_mask = false;
- header->image_size = nextSpriteOffset - 44 - sprite->palettesize;
- header->mask_size = 0;
- } else {
- sprite->has_mask = true;
- header->image_size = maskOffset - imageOffset;
- header->mask_size = nextSpriteOffset - 44 - sprite->palettesize - header->image_size;
- }
-
+ uint32_t translated_pixel;
+ /* because we're dealing with 8bpp or less */
if (sprite->has_palette) {
- assert(sprite->palettesize % 8 == 0);
- sprite->palette = malloc(sizeof(uint32_t) * sprite->palettesize);
- uint32_t paletteEntries = sprite->palettesize / 8;
-
- /* Each palette entry is two words big
- * The second word is a duplicate of the first
- * I think this is in case you ever wanted flashing colours
- * PRM1-730
- */
- for (uint32_t j = 0; j < paletteEntries; j++) {
- uint32_t word1, word2;
- ERRCHK(sprite_read_word(reader, ctx, &word1));
- ERRCHK(sprite_read_word(reader, ctx, &word2));
- assert(word1 == word2); /* if they aren't equal, flashing colours are desired, which we don't support */
-
- /* swap rr and bb parts -- PRM1-731 */
- uint32_t entry = ((word1 & 0xff000000) >> 16) | (word1 & 0x00ff0000) | ((word1 & 0x0000ff00) << 16) | 0xff;
- sprite->palette[j] = entry;
+ assert(pixel <= sprite->palettesize); /* TODO: what to do if your color depth is bigger than palette? */
+ translated_pixel = sprite->palette[pixel];
+ } else {
+ switch (sprite->mode.colorbpp) {
+ case 8:
+ assert(pixel < 256);
+ translated_pixel = sprite_8bpp_palette[pixel];
+ break;
+ case 4:
+ assert(pixel < 16);
+ translated_pixel = sprite_4bpp_palette[pixel];
+ break;
+ case 2:
+ assert(pixel < 4);
+ translated_pixel = sprite_2bpp_palette[pixel];
+ break;
+ case 1:
+ assert(pixel < 2);
+ translated_pixel = sprite_1bpp_palette[pixel];
+ break;
+ default:
+ assert(false);
}
}
+ return translated_pixel;
+}
- uint8_t* image = malloc(header->image_size);
- reader(image, header->image_size, ctx);
-
- uint8_t* mask = NULL;
- if (sprite->has_mask) {
- mask = malloc(header->mask_size);
- reader(mask, header->mask_size, ctx);
- }
-
- /* sanity check image_size */
- assert((header->width_words) * 4 * (sprite->height) == header->image_size);
- /* TODO: sanity check mask_size */
- if (sprite->mode.colorbpp > 8) {
- ERRCHK(sprite_load_high_color(image, mask, sprite, header));
- } else {
- ERRCHK(sprite_load_low_color(image, mask, sprite, header));
- }
+static rosprite_error rosprite_init_mask_state(struct rosprite* sprite, struct rosprite_header* header, uint8_t* mask, struct rosprite_mask_state** result)
+{
+ struct rosprite_mask_state* mask_state = malloc(sizeof(struct rosprite_mask_state));
+ if (!mask_state) return ROSPRITE_NOMEM;
- free(image);
- free(mask);
- free(header);
+ mask_state->x = header->first_used_bit;
+ mask_state->y = 0;
+ mask_state->first_used_bit = header->first_used_bit;
+ mask_state->row_max_bit = sprite->width * sprite->mode.mask_width;
+ mask_state->height = sprite->height;
+ mask_state->bpp = sprite->mode.mask_width;
+ mask_state->current_word = BTUINT(mask);
+ mask_state->current_byte_index = 4;
- *result = sprite;
+ *result = mask_state;
return ROSPRITE_OK;
}
-rosprite_error rosprite_load(reader reader, void* ctx, struct rosprite_area** result)
+/* Get the next mask byte.
+ * Mask of 0xff denotes 100% opaque, 0x00 denotes 100% transparent
+ */
+static uint32_t rosprite_next_mask_pixel(uint8_t* mask, struct rosprite_mask_state* mask_state)
{
- struct rosprite_area* sprite_area = malloc(sizeof(struct rosprite_area));
+ /* a 1bpp mask (for new mode sprites), each row is word aligned (therefore potential righthand wastage */
+ const uint32_t bitmask = (1 << mask_state->bpp) - 1;
+ const uint32_t offset_into_word = mask_state->x % 32;
+ uint32_t pixel = (mask_state->current_word & (bitmask << offset_into_word)) >> offset_into_word;
- ERRCHK(sprite_read_word(reader, ctx, &(sprite_area->sprite_count)));
+ if (mask_state->x + mask_state->bpp < mask_state->row_max_bit && offset_into_word + mask_state->bpp == 32) {
+ mask_state->current_word = BTUINT((mask + mask_state->current_byte_index));
+ mask_state->current_byte_index += 4;
+ }
- uint32_t firstSpriteOffset, firstFreeWordOffset;
- ERRCHK(sprite_read_word(reader, ctx, &firstSpriteOffset));
- ERRCHK(sprite_read_word(reader, ctx, &firstFreeWordOffset)); /* TODO: use this for some sanity checking? */
- sprite_area->extension_size = 16 - firstSpriteOffset;
+ mask_state->x += mask_state->bpp;
+ if (mask_state->x >= mask_state->row_max_bit) {
+ mask_state->x = mask_state->first_used_bit;
- sprite_area->extension_words = NULL;
- if (sprite_area->extension_size > 0) {
- sprite_area->extension_words = malloc(sprite_area->extension_size);
- int bytes_read = reader(sprite_area->extension_words, (size_t) (sprite_area->extension_size), ctx);
- if (bytes_read < (signed long) sprite_area->extension_size) {
- return ROSPRITE_EOF;
+ if (mask_state->y + 1 < mask_state->height) {
+ mask_state->current_word = BTUINT((mask + mask_state->current_byte_index));
+ mask_state->current_byte_index += 4;
}
- }
- sprite_area->sprites = malloc(sizeof(struct rosprite*) * sprite_area->sprite_count); /* allocate array of pointers */
- for (uint32_t i = 0; i < sprite_area->sprite_count; i++) {
- struct rosprite* sprite;
- ERRCHK(sprite_load_sprite(reader, ctx, &sprite));
- sprite_area->sprites[i] = sprite;
+ mask_state->y++;
}
- *result = sprite_area;
-
- return ROSPRITE_OK;
-}
-
-void rosprite_destroy_sprite_area(struct rosprite_area* sprite_area)
-{
- for (uint32_t i = 0; i < sprite_area->sprite_count; i++) {
- struct rosprite* sprite = sprite_area->sprites[i];
- if (sprite->has_palette) free(sprite->palette);
- free(sprite->image);
- free(sprite);
+ /*
+ * if the mask is 1bpp, make sure the whole byte is 0x00 or 0xff
+ */
+ if (mask_state->bpp < 8) {
+ pixel = (pixel & 1) ? 0xff : 0x00;
}
- free(sprite_area->sprites);
- if (sprite_area->extension_size > 0) free(sprite_area->extension_words);
- free(sprite_area);
+ return pixel;
}
-rosprite_error rosprite_load_palette(reader reader, void* ctx, struct rosprite_palette** result)
+static uint32_t rosprite_upscale_color(uint32_t pixel, struct rosprite_mode* mode, bool* has_alpha_pixel_data)
{
- /* TODO: currently assume palette has linear entries (2nd byte in is 00, 01, 02 etc) */
- struct rosprite_palette* palette = malloc(sizeof(struct rosprite_palette));
-
- palette->palette = malloc(sizeof(uint32_t) * 256); /* allocate 256 whether we need them all or not */
+ switch (mode->colorbpp) {
+ case 32:
+ if (mode->color_model == ROSPRITE_RGB) {
+ /* swap from 0xAABBGGRR to 0xRRGGBBAA */
+ pixel = BSWAP(pixel);
- uint32_t c = 0;
- uint8_t b[6];
+ uint8_t alpha = pixel & 0xff;
+ if (alpha == 0x00) {
+ if (!(*has_alpha_pixel_data)) {
+ pixel = pixel | 0xff;
+ }
+ } else {
+ *has_alpha_pixel_data = true;
+ }
+ return pixel;
+ } else {
+ return rosprite_cmyk_to_rgb(pixel);
+ }
+ case 24:
+ /* reverse byte order */
+ return BSWAP(pixel);
+ case 16:
+ /* incoming format is b_00000000000000000bbbbbgggggrrrrr */
+ {
+ uint8_t red = pixel & 31;
+ uint8_t green = (pixel & (31 << 5)) >> 5;
+ uint8_t blue = (pixel & (31 << 10)) >> 10;
- unsigned int bytesRead = reader(b, 6, ctx);
- assert(bytesRead % 6 == 0);
- while (bytesRead == 6) {
- assert(b[0] == 19); /* VDU 19 */
+ /* sanity check */
+ assert(red < 32);
+ assert(green < 32);
+ assert(blue < 32);
- /* only process logical colours */
- if (b[2] == 16) {
- /*assert(c == b[1]);*/
-
- uint32_t entry = (b[3] << 24) | (b[4] << 16) | (b[5] << 8) | 0xff; /* last byte is alpha */
- palette->palette[c] = entry;
-
- c++;
- assert(c <= 256);
+ pixel = (sprite_16bpp_translate[red] << 24)
+ | (sprite_16bpp_translate[green] << 16)
+ | (sprite_16bpp_translate[blue] << 8);
+ return pixel;
}
-
- bytesRead = reader(b, 6, ctx);
- assert(bytesRead % 6 == 0);
+ case 8:
+ case 4:
+ case 2:
+ case 1:
+ assert(false); /* shouldn't need to call for <= 8bpp, since a palette lookup will return 32bpp */
+ default:
+ assert(false); /* unknown bpp */
}
+}
- palette->size = c;
-
- *result = palette;
-
- return ROSPRITE_OK;
+static inline uint32_t rosprite_cmyk_to_rgb(uint32_t cmyk)
+{
+ uint8_t c = cmyk & 0xff;
+ uint8_t m = (cmyk & 0xff00) >> 8;
+ uint8_t y = (cmyk & 0xff0000) >> 16;
+ uint8_t k = cmyk >> 24;
+
+ /* Convert to CMY colourspace */
+ uint8_t C = c + k;
+ uint8_t M = m + k;
+ uint8_t Y = y + k;
+
+ /* And to RGB */
+ uint8_t r = 255 - C;
+ uint8_t g = 255 - M;
+ uint8_t b = 255 - Y;
+
+ return r << 24 | g << 16 | b << 8;
}
-void rosprite_destroy_palette(struct rosprite_palette* palette)
+static inline rosprite_error rosprite_read_word(reader reader, void* ctx, uint32_t* result)
{
- free(palette->palette);
- free(palette);
+ unsigned char b[4];
+ if (reader(b, 4, ctx) < 4) {
+ return ROSPRITE_EOF;
+ }
+ *result = BTUINT(b);
+ return ROSPRITE_OK;
}
diff --git a/trunk/librosprite.h b/trunk/librosprite.h
index 442163c..21bd557 100644
--- a/trunk/librosprite.h
+++ b/trunk/librosprite.h
@@ -1,7 +1,7 @@
/**
* \mainpage
*
- * librosprite is a library for reading RISC OS sprite files. The following subformats are supported:
+ * librosprite is a library for reading RISC OS sprite and palette files. The following subformats are supported:
* <ul>
* <li>1-bit and 8-bit transparency masks</li>
* <li>Sprites with custom palettes</li>
@@ -10,10 +10,20 @@
* <li>32bpp CMYK</li>
* <li>Inline alpha channel, as used by Photodesk and Tinct</li>
* </ul>
+ *
*/
/**
* \file librosprite.h
+ *
+ * Sprite file reading is performed by rosprite_load(), and palette file reading by rosprite_load_palette().
+ *
+ * Retrieving sprite or palette data is performed by a reader.
+ * librosprite implements file and memory readers.
+ * To use a reader, create a context by calling the
+ * rosprite_create_file_context() or rosprite_create_mem_context().
+ * Pass the reader function, and the context you have created, to the load function,
+ * typically rosprite_load().
*/
#ifndef ROSPRITE_H
@@ -35,7 +45,7 @@ typedef int (*reader)(uint8_t* buf, size_t count, void* ctx);
struct rosprite_file_context;
/**
- * A sprite area comprises zero more rosprites. Optionally, it may also have an extension_words block.
+ * A sprite area comprises zero or more rosprites. Optionally, it may also have an extension_words block.
*/
struct rosprite_area {
uint32_t extension_size; /* size of extension_words in bytes */
@@ -111,11 +121,27 @@ struct rosprite {
};
struct rosprite_file_context;
-rosprite_error rosprite_create_file_context(FILE* f, struct rosprite_file_context** ctx);
+
+/**
+ * Create a file reader context using the specified file handle.
+ * Clients must call rosprite_destroy_file_context() to dispose of the context.
+ *
+ * \param[out] result
+ */
+rosprite_error rosprite_create_file_context(FILE* f, struct rosprite_file_context** result);
void rosprite_destroy_file_context(struct rosprite_file_context* ctx);
int rosprite_file_reader(uint8_t* buf, size_t count, void* ctx);
struct rosprite_mem_context;
+
+/**
+ * Create a memory reader context using the specified memory pointer.
+ * Clients must call rosprite_destroy_mem_context() to dispose of the context.
+ *
+ * \param[in] p pointer to the start of the memory block
+ * \param[in] total_size the size of the block pointed to by p, in bytes
+ * \param[out] result
+ */
rosprite_error rosprite_create_mem_context(uint8_t* p, unsigned long total_size, struct rosprite_mem_context** result);
void rosprite_destroy_mem_context(struct rosprite_mem_context* ctx);
int rosprite_mem_reader(uint8_t* buf, size_t count, void* ctx);
@@ -123,6 +149,7 @@ int rosprite_mem_reader(uint8_t* buf, size_t count, void* ctx);
/**
* Load a rosprite_area using the reader provided.
* Clients must call rosprite_destroy_sprite_area() to dispose of the rosprite_area.
+ *
* \param[out] result The pointer to be populated by this function.
*/
rosprite_error rosprite_load(reader reader, void* ctx, struct rosprite_area** result);