diff options
author | James Shaw <jshaw@netsurf-browser.org> | 2008-03-27 21:24:20 +0000 |
---|---|---|
committer | James Shaw <jshaw@netsurf-browser.org> | 2008-03-27 21:24:20 +0000 |
commit | 270bf2ad00b469eac1e33eb82c3bfea27f015061 (patch) | |
tree | 3cf78700f86496ed75ffee2a0d162cba1d8c0e5a /trunk/librosprite.c | |
parent | 704f880692d5ac4597ec3cadbf3aeb48c8ba91cb (diff) | |
download | librosprite-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
Diffstat (limited to 'trunk/librosprite.c')
-rw-r--r-- | trunk/librosprite.c | 683 |
1 files changed, 349 insertions, 334 deletions
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; } |