| File: | librosprite.c |
| Warning: | line 246, column 2 Potential leak of memory pointed to by 'sprite_area' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | |||
| 2 | * This file is part of librosprite. | |||
| 3 | * Licensed under the MIT License, | |||
| 4 | * http://www.opensource.org/licenses/mit-license.php | |||
| 5 | * Copyright 2008 James Shaw <js102@zepler.net> | |||
| 6 | */ | |||
| 7 | ||||
| 8 | /** | |||
| 9 | * \file | |||
| 10 | */ | |||
| 11 | ||||
| 12 | #include <assert.h> | |||
| 13 | #include <stdio.h> | |||
| 14 | #include <stdlib.h> | |||
| 15 | #include <stdint.h> | |||
| 16 | #include <stdbool.h> | |||
| 17 | #include <string.h> | |||
| 18 | ||||
| 19 | #include "librosprite.h" | |||
| 20 | ||||
| 21 | /** | |||
| 22 | * Reads four bytes, 00, 11, 22 and 33, of a byte array b to give 0x33221100. | |||
| 23 | */ | |||
| 24 | #define BTUINT(b)((unsigned)b[0] | ((unsigned)b[1] << 8) | ((unsigned)b[ 2] << 16) | ((unsigned)b[3] << 24)) \ | |||
| 25 | ((unsigned)b[0] | \ | |||
| 26 | ((unsigned)b[1] << 8) | \ | |||
| 27 | ((unsigned)b[2] << 16) | \ | |||
| 28 | ((unsigned)b[3] << 24)) | |||
| 29 | ||||
| 30 | /** | |||
| 31 | * Reverse the byte order of a word such that 0xAABBCCDD becomes 0xDDCCBBAA. | |||
| 32 | */ | |||
| 33 | #define BSWAP(word)(((word & (0x000000ff)) << 24) | ((word & 0x0000ff00 ) << 8) | ((word & 0x00ff0000) >> 8) | ((word & 0xff000000) >> 24)) (((word & (0x000000ff)) << 24) | ((word & 0x0000ff00) << 8) | ((word & 0x00ff0000) >> 8) | ((word & 0xff000000) >> 24)) | |||
| 34 | ||||
| 35 | #define ERRCHK(x)do { rosprite_error err = x; if (err != ROSPRITE_OK) return err ; } while(0) do { \ | |||
| 36 | rosprite_error err = x; \ | |||
| 37 | if (err != ROSPRITE_OK) return err; \ | |||
| 38 | } while(0) | |||
| 39 | ||||
| 40 | struct rosprite_header { | |||
| 41 | uint32_t width_words; /* width in words */ | |||
| 42 | /* height defined in sprite struct */ | |||
| 43 | uint32_t first_used_bit; /* old format only (spriteType = 0) */ | |||
| 44 | uint32_t last_used_bit; | |||
| 45 | ||||
| 46 | /** | |||
| 47 | * Image size in bytes | |||
| 48 | */ | |||
| 49 | uint32_t image_size; | |||
| 50 | ||||
| 51 | /** | |||
| 52 | * Mask size in bytes | |||
| 53 | */ | |||
| 54 | uint32_t mask_size; | |||
| 55 | }; | |||
| 56 | ||||
| 57 | struct rosprite_mask_state { | |||
| 58 | uint32_t x; | |||
| 59 | uint32_t y; | |||
| 60 | uint32_t first_used_bit; | |||
| 61 | uint32_t row_max_bit; | |||
| 62 | uint32_t height; | |||
| 63 | uint32_t current_byte_index; | |||
| 64 | uint32_t current_word; | |||
| 65 | uint32_t bpp; | |||
| 66 | }; | |||
| 67 | ||||
| 68 | struct rosprite_file_context { | |||
| 69 | FILE* f; | |||
| 70 | }; | |||
| 71 | ||||
| 72 | struct rosprite_mem_context { | |||
| 73 | uint8_t* base; | |||
| 74 | unsigned long offset; | |||
| 75 | bool_Bool known_size; | |||
| 76 | unsigned long size; | |||
| 77 | }; | |||
| 78 | ||||
| 79 | static const struct rosprite_mode oldmodes[] = { | |||
| 80 | /*0*/{ .colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 81 | /*1*/{ .colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 45, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 82 | /*2*/{ .colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 22, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 83 | ||||
| 84 | /*3*/{ .colorbpp = 0, .maskbpp = 0, .mask_width = 0, .xdpi = 0, .ydpi = 0, .color_model = ROSPRITE_RGB }, | |||
| 85 | ||||
| 86 | /*4*/{ .colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 45, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 87 | /*5*/{ .colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 22, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 88 | ||||
| 89 | /*6*/{ .colorbpp = 0, .maskbpp = 0, .mask_width = 0, .xdpi = 0, .ydpi = 0, .color_model = ROSPRITE_RGB }, | |||
| 90 | /*7*/{ .colorbpp = 0, .maskbpp = 0, .mask_width = 0, .xdpi = 0, .ydpi = 0, .color_model = ROSPRITE_RGB }, | |||
| 91 | ||||
| 92 | /*8*/{ .colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 93 | /*9*/{ .colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 45, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 94 | /*10*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 22, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 95 | /*11*/{.colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 96 | /*12*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 97 | /*13*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 45, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 98 | /*14*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 99 | /*15*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 100 | /*16*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 101 | /*17*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 102 | /*18*/{.colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 103 | /*19*/{.colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 104 | /*20*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 105 | /*21*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 106 | /*22*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi =180, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 107 | /*23*/{.colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 108 | /*24*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 109 | /*25*/{.colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 110 | /*26*/{.colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 111 | /*27*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 112 | /*28*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 113 | /*29*/{.colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 114 | /*30*/{.colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 115 | /*31*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 116 | /*32*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 90, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 117 | /*33*/{.colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 118 | /*34*/{.colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 119 | /*35*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 120 | /*36*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 121 | /*37*/{.colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 122 | /*38*/{.colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 123 | /*39*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 124 | /*40*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 125 | /*41*/{.colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 126 | /*42*/{.colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 127 | /*43*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 128 | /*44*/{.colorbpp = 1, .maskbpp = 1, .mask_width = 1, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 129 | /*45*/{.colorbpp = 2, .maskbpp = 1, .mask_width = 2, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 130 | /*46*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 90, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 131 | /*47*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 45, .ydpi = 45, .color_model = ROSPRITE_RGB }, | |||
| 132 | /*48*/{.colorbpp = 4, .maskbpp = 1, .mask_width = 4, .xdpi = 45, .ydpi = 90, .color_model = ROSPRITE_RGB }, | |||
| 133 | /*49*/{.colorbpp = 8, .maskbpp = 1, .mask_width = 8, .xdpi = 45, .ydpi = 90, .color_model = ROSPRITE_RGB } | |||
| 134 | }; | |||
| 135 | ||||
| 136 | /* table for converting a 5bit channel into an 8bit channel (used for 16bpp to 32bpp conversion) */ | |||
| 137 | static const uint8_t sprite_16bpp_translate[] = { | |||
| 138 | 0x00, 0x08, 0x10, 0x18, 0x20, 0x29, 0x31, 0x39, | |||
| 139 | 0x41, 0x4a, 0x52, 0x5a, 0x62, 0x6a, 0x73, 0x7b, | |||
| 140 | 0x83, 0x8b, 0x94, 0x9c, 0xa4, 0xac, 0xb4, 0xbd, | |||
| 141 | 0xc5, 0xcd, 0xd5, 0xde, 0xe6, 0xee, 0xf6, 0xff | |||
| 142 | }; | |||
| 143 | ||||
| 144 | /* palettes generated with palette2c.c | |||
| 145 | * which in turn requires rosprite_load_palette(FILE* f) | |||
| 146 | * defined in this file | |||
| 147 | */ | |||
| 148 | static const uint32_t sprite_1bpp_palette[] = { 0xffffffff, 0xff }; | |||
| 149 | ||||
| 150 | static const uint32_t sprite_2bpp_palette[] = { 0xffffffff, 0xbbbbbbff, 0x777777ff, 0xff }; | |||
| 151 | ||||
| 152 | static const uint32_t sprite_4bpp_palette[] = { | |||
| 153 | 0xffffffff, 0xddddddff, 0xbbbbbbff, 0x999999ff, | |||
| 154 | 0x777777ff, 0x555555ff, 0x333333ff, 0xff, | |||
| 155 | 0x4499ff, 0xeeee00ff, 0xcc00ff, 0xdd0000ff, | |||
| 156 | 0xeeeebbff, 0x558800ff, 0xffbb00ff, 0xbbffff | |||
| 157 | }; | |||
| 158 | ||||
| 159 | static const uint32_t sprite_8bpp_palette[] = { | |||
| 160 | 0xff, 0x111111ff, 0x222222ff, 0x333333ff, | |||
| 161 | 0x440000ff, 0x551111ff, 0x662222ff, 0x773333ff, | |||
| 162 | 0x44ff, 0x111155ff, 0x222266ff, 0x333377ff, | |||
| 163 | 0x440044ff, 0x551155ff, 0x662266ff, 0x773377ff, | |||
| 164 | 0x880000ff, 0x991111ff, 0xaa2222ff, 0xbb3333ff, | |||
| 165 | 0xcc0000ff, 0xdd1111ff, 0xee2222ff, 0xff3333ff, | |||
| 166 | 0x880044ff, 0x991155ff, 0xaa2266ff, 0xbb3377ff, | |||
| 167 | 0xcc0044ff, 0xdd1155ff, 0xee2266ff, 0xff3377ff, | |||
| 168 | 0x4400ff, 0x115511ff, 0x226622ff, 0x337733ff, | |||
| 169 | 0x444400ff, 0x555511ff, 0x666622ff, 0x777733ff, | |||
| 170 | 0x4444ff, 0x115555ff, 0x226666ff, 0x337777ff, | |||
| 171 | 0x444444ff, 0x555555ff, 0x666666ff, 0x777777ff, | |||
| 172 | 0x884400ff, 0x995511ff, 0xaa6622ff, 0xbb7733ff, | |||
| 173 | 0xcc4400ff, 0xdd5511ff, 0xee6622ff, 0xff7733ff, | |||
| 174 | 0x884444ff, 0x995555ff, 0xaa6666ff, 0xbb7777ff, | |||
| 175 | 0xcc4444ff, 0xdd5555ff, 0xee6666ff, 0xff7777ff, | |||
| 176 | 0x8800ff, 0x119911ff, 0x22aa22ff, 0x33bb33ff, | |||
| 177 | 0x448800ff, 0x559911ff, 0x66aa22ff, 0x77bb33ff, | |||
| 178 | 0x8844ff, 0x119955ff, 0x22aa66ff, 0x33bb77ff, | |||
| 179 | 0x448844ff, 0x559955ff, 0x66aa66ff, 0x77bb77ff, | |||
| 180 | 0x888800ff, 0x999911ff, 0xaaaa22ff, 0xbbbb33ff, | |||
| 181 | 0xcc8800ff, 0xdd9911ff, 0xeeaa22ff, 0xffbb33ff, | |||
| 182 | 0x888844ff, 0x999955ff, 0xaaaa66ff, 0xbbbb77ff, | |||
| 183 | 0xcc8844ff, 0xdd9955ff, 0xeeaa66ff, 0xffbb77ff, | |||
| 184 | 0xcc00ff, 0x11dd11ff, 0x22ee22ff, 0x33ff33ff, | |||
| 185 | 0x44cc00ff, 0x55dd11ff, 0x66ee22ff, 0x77ff33ff, | |||
| 186 | 0xcc44ff, 0x11dd55ff, 0x22ee66ff, 0x33ff77ff, | |||
| 187 | 0x44cc44ff, 0x55dd55ff, 0x66ee66ff, 0x77ff77ff, | |||
| 188 | 0x88cc00ff, 0x99dd11ff, 0xaaee22ff, 0xbbff33ff, | |||
| 189 | 0xcccc00ff, 0xdddd11ff, 0xeeee22ff, 0xffff33ff, | |||
| 190 | 0x88cc44ff, 0x99dd55ff, 0xaaee66ff, 0xbbff77ff, | |||
| 191 | 0xcccc44ff, 0xdddd55ff, 0xeeee66ff, 0xffff77ff, | |||
| 192 | 0x88ff, 0x111199ff, 0x2222aaff, 0x3333bbff, | |||
| 193 | 0x440088ff, 0x551199ff, 0x6622aaff, 0x7733bbff, | |||
| 194 | 0xccff, 0x1111ddff, 0x2222eeff, 0x3333ffff, | |||
| 195 | 0x4400ccff, 0x5511ddff, 0x6622eeff, 0x7733ffff, | |||
| 196 | 0x880088ff, 0x991199ff, 0xaa22aaff, 0xbb33bbff, | |||
| 197 | 0xcc0088ff, 0xdd1199ff, 0xee22aaff, 0xff33bbff, | |||
| 198 | 0x8800ccff, 0x9911ddff, 0xaa22eeff, 0xbb33ffff, | |||
| 199 | 0xcc00ccff, 0xdd11ddff, 0xee22eeff, 0xff33ffff, | |||
| 200 | 0x4488ff, 0x115599ff, 0x2266aaff, 0x3377bbff, | |||
| 201 | 0x444488ff, 0x555599ff, 0x6666aaff, 0x7777bbff, | |||
| 202 | 0x44ccff, 0x1155ddff, 0x2266eeff, 0x3377ffff, | |||
| 203 | 0x4444ccff, 0x5555ddff, 0x6666eeff, 0x7777ffff, | |||
| 204 | 0x884488ff, 0x995599ff, 0xaa66aaff, 0xbb77bbff, | |||
| 205 | 0xcc4488ff, 0xdd5599ff, 0xee66aaff, 0xff77bbff, | |||
| 206 | 0x8844ccff, 0x9955ddff, 0xaa66eeff, 0xbb77ffff, | |||
| 207 | 0xcc44ccff, 0xdd55ddff, 0xee66eeff, 0xff77ffff, | |||
| 208 | 0x8888ff, 0x119999ff, 0x22aaaaff, 0x33bbbbff, | |||
| 209 | 0x448888ff, 0x559999ff, 0x66aaaaff, 0x77bbbbff, | |||
| 210 | 0x88ccff, 0x1199ddff, 0x22aaeeff, 0x33bbffff, | |||
| 211 | 0x4488ccff, 0x5599ddff, 0x66aaeeff, 0x77bbffff, | |||
| 212 | 0x888888ff, 0x999999ff, 0xaaaaaaff, 0xbbbbbbff, | |||
| 213 | 0xcc8888ff, 0xdd9999ff, 0xeeaaaaff, 0xffbbbbff, | |||
| 214 | 0x8888ccff, 0x9999ddff, 0xaaaaeeff, 0xbbbbffff, | |||
| 215 | 0xcc88ccff, 0xdd99ddff, 0xeeaaeeff, 0xffbbffff, | |||
| 216 | 0xcc88ff, 0x11dd99ff, 0x22eeaaff, 0x33ffbbff, | |||
| 217 | 0x44cc88ff, 0x55dd99ff, 0x66eeaaff, 0x77ffbbff, | |||
| 218 | 0xccccff, 0x11ddddff, 0x22eeeeff, 0x33ffffff, | |||
| 219 | 0x44ccccff, 0x55ddddff, 0x66eeeeff, 0x77ffffff, | |||
| 220 | 0x88cc88ff, 0x99dd99ff, 0xaaeeaaff, 0xbbffbbff, | |||
| 221 | 0xcccc88ff, 0xdddd99ff, 0xeeeeaaff, 0xffffbbff, | |||
| 222 | 0x88ccccff, 0x99ddddff, 0xaaeeeeff, 0xbbffffff, | |||
| 223 | 0xccccccff, 0xddddddff, 0xeeeeeeff, 0xffffffff | |||
| 224 | }; | |||
| 225 | ||||
| 226 | static inline rosprite_error rosprite_read_word(reader reader, void* ctx, uint32_t* result); | |||
| 227 | static rosprite_error rosprite_get_mode(uint32_t spriteMode, struct rosprite_mode* result); | |||
| 228 | static uint32_t rosprite_palette_lookup(struct rosprite* sprite, uint32_t pixel); | |||
| 229 | static inline uint32_t rosprite_cmyk_to_rgb(uint32_t cmyk); | |||
| 230 | static uint32_t rosprite_next_mask_pixel(uint8_t* mask, struct rosprite_mask_state* mask_state); | |||
| 231 | static rosprite_error rosprite_load_high_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header); | |||
| 232 | static rosprite_error rosprite_load_low_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header); | |||
| 233 | rosprite_error rosprite_load_sprite(reader reader, void* ctx, struct rosprite** result); | |||
| 234 | static rosprite_error rosprite_init_mask_state(struct rosprite* sprite, struct rosprite_header* header, uint8_t* mask, struct rosprite_mask_state** result); | |||
| 235 | static uint32_t rosprite_upscale_color(uint32_t pixel, struct rosprite_mode* mode, bool_Bool* has_alpha_pixel_data); | |||
| 236 | static inline void rosprite_fix_alpha(uint32_t* image, unsigned long pixels); | |||
| 237 | ||||
| 238 | rosprite_error rosprite_load(reader reader, void* ctx, struct rosprite_area** result) | |||
| ||||
| 239 | { | |||
| 240 | uint32_t firstSpriteOffset, firstFreeWordOffset; | |||
| 241 | int bytes_read; | |||
| 242 | uint32_t i; | |||
| 243 | ||||
| 244 | struct rosprite_area* sprite_area = malloc(sizeof(struct rosprite_area)); | |||
| 245 | ||||
| 246 | ERRCHK(rosprite_read_word(reader, ctx, &(sprite_area->sprite_count)))do { rosprite_error err = rosprite_read_word(reader, ctx, & (sprite_area->sprite_count)); if (err != ROSPRITE_OK) return err; } while(0); | |||
| ||||
| 247 | ||||
| 248 | ERRCHK(rosprite_read_word(reader, ctx, &firstSpriteOffset))do { rosprite_error err = rosprite_read_word(reader, ctx, & firstSpriteOffset); if (err != ROSPRITE_OK) return err; } while (0); | |||
| 249 | ERRCHK(rosprite_read_word(reader, ctx, &firstFreeWordOffset))do { rosprite_error err = rosprite_read_word(reader, ctx, & firstFreeWordOffset); if (err != ROSPRITE_OK) return err; } while (0); /* TODO: use this for some sanity checking? */ | |||
| 250 | sprite_area->extension_size = 16 - firstSpriteOffset; | |||
| 251 | ||||
| 252 | sprite_area->extension_words = NULL((void*)0); | |||
| 253 | if (sprite_area->extension_size > 0) { | |||
| 254 | sprite_area->extension_words = malloc(sprite_area->extension_size); | |||
| 255 | bytes_read = reader(sprite_area->extension_words, (size_t) (sprite_area->extension_size), ctx); | |||
| 256 | if (bytes_read < (signed long) sprite_area->extension_size) { | |||
| 257 | return ROSPRITE_EOF; | |||
| 258 | } | |||
| 259 | } | |||
| 260 | ||||
| 261 | sprite_area->sprites = malloc(sizeof(struct rosprite*) * sprite_area->sprite_count); /* allocate array of pointers */ | |||
| 262 | for (i = 0; i < sprite_area->sprite_count; i++) { | |||
| 263 | struct rosprite* sprite; | |||
| 264 | ERRCHK(rosprite_load_sprite(reader, ctx, &sprite))do { rosprite_error err = rosprite_load_sprite(reader, ctx, & sprite); if (err != ROSPRITE_OK) return err; } while(0); | |||
| 265 | sprite_area->sprites[i] = sprite; | |||
| 266 | } | |||
| 267 | ||||
| 268 | *result = sprite_area; | |||
| 269 | ||||
| 270 | return ROSPRITE_OK; | |||
| 271 | } | |||
| 272 | ||||
| 273 | void rosprite_destroy_sprite_area(struct rosprite_area* sprite_area) | |||
| 274 | { | |||
| 275 | uint32_t i; | |||
| 276 | for (i = 0; i < sprite_area->sprite_count; i++) { | |||
| 277 | struct rosprite* sprite = sprite_area->sprites[i]; | |||
| 278 | if (sprite->has_palette) free(sprite->palette); | |||
| 279 | free(sprite->image); | |||
| 280 | free(sprite); | |||
| 281 | } | |||
| 282 | ||||
| 283 | free(sprite_area->sprites); | |||
| 284 | if (sprite_area->extension_size > 0) free(sprite_area->extension_words); | |||
| 285 | free(sprite_area); | |||
| 286 | } | |||
| 287 | ||||
| 288 | rosprite_error rosprite_load_palette(reader reader, void* ctx, struct rosprite_palette** result) | |||
| 289 | { | |||
| 290 | uint32_t c; | |||
| 291 | uint8_t b[6]; | |||
| 292 | unsigned int bytesRead; | |||
| 293 | ||||
| 294 | /* TODO: currently assume palette has linear entries (2nd byte in is 00, 01, 02 etc) */ | |||
| 295 | struct rosprite_palette* palette = malloc(sizeof(struct rosprite_palette)); | |||
| 296 | ||||
| 297 | palette->palette = malloc(sizeof(uint32_t) * 256); /* allocate 256 whether we need them all or not */ | |||
| 298 | ||||
| 299 | c = 0; | |||
| 300 | ||||
| 301 | bytesRead = reader(b, 6, ctx); | |||
| 302 | assert(bytesRead % 6 == 0)((bytesRead % 6 == 0) ? (void) (0) : __assert_fail ("bytesRead % 6 == 0" , "src/librosprite.c", 302, __extension__ __PRETTY_FUNCTION__ )); | |||
| 303 | while (bytesRead == 6) { | |||
| 304 | assert(b[0] == 19)((b[0] == 19) ? (void) (0) : __assert_fail ("b[0] == 19", "src/librosprite.c" , 304, __extension__ __PRETTY_FUNCTION__)); /* VDU 19 */ | |||
| 305 | ||||
| 306 | /* only process logical colours */ | |||
| 307 | if (b[2] == 16) { | |||
| 308 | /*assert(c == b[1]);*/ | |||
| 309 | ||||
| 310 | uint32_t entry = (b[3] << 24) | (b[4] << 16) | (b[5] << 8) | 0xff; /* last byte is alpha */ | |||
| 311 | palette->palette[c] = entry; | |||
| 312 | ||||
| 313 | c++; | |||
| 314 | assert(c <= 256)((c <= 256) ? (void) (0) : __assert_fail ("c <= 256", "src/librosprite.c" , 314, __extension__ __PRETTY_FUNCTION__)); | |||
| 315 | } | |||
| 316 | ||||
| 317 | bytesRead = reader(b, 6, ctx); | |||
| 318 | assert(bytesRead % 6 == 0)((bytesRead % 6 == 0) ? (void) (0) : __assert_fail ("bytesRead % 6 == 0" , "src/librosprite.c", 318, __extension__ __PRETTY_FUNCTION__ )); | |||
| 319 | } | |||
| 320 | ||||
| 321 | palette->size = c; | |||
| 322 | ||||
| 323 | *result = palette; | |||
| 324 | ||||
| 325 | return ROSPRITE_OK; | |||
| 326 | } | |||
| 327 | ||||
| 328 | void rosprite_destroy_palette(struct rosprite_palette* palette) | |||
| 329 | { | |||
| 330 | free(palette->palette); | |||
| 331 | free(palette); | |||
| 332 | } | |||
| 333 | ||||
| 334 | rosprite_error rosprite_create_file_context(FILE* f, struct rosprite_file_context** result) | |||
| 335 | { | |||
| 336 | struct rosprite_file_context* ctx = malloc(sizeof(struct rosprite_file_context)); | |||
| 337 | if (!ctx) return ROSPRITE_NOMEM; | |||
| 338 | ctx->f = f; | |||
| 339 | *result = ctx; | |||
| 340 | ||||
| 341 | return ROSPRITE_OK; | |||
| 342 | } | |||
| 343 | ||||
| 344 | void rosprite_destroy_file_context(struct rosprite_file_context* ctx) | |||
| 345 | { | |||
| 346 | free(ctx); | |||
| 347 | } | |||
| 348 | ||||
| 349 | int rosprite_file_reader(uint8_t* buf, size_t count, void* ctx) | |||
| 350 | { | |||
| 351 | return fread(buf, 1 /*size*/, count, ((struct rosprite_file_context*) ctx)->f); | |||
| 352 | } | |||
| 353 | ||||
| 354 | rosprite_error rosprite_create_mem_context(uint8_t* p, unsigned long total_size, struct rosprite_mem_context** result) | |||
| 355 | { | |||
| 356 | struct rosprite_mem_context* ctx = malloc(sizeof(struct rosprite_mem_context)); | |||
| 357 | if (!ctx) return ROSPRITE_NOMEM; | |||
| 358 | ||||
| 359 | ctx->base = p; | |||
| 360 | ctx->offset = 0; | |||
| 361 | ctx->size = total_size; | |||
| 362 | *result = ctx; | |||
| 363 | ||||
| 364 | return ROSPRITE_OK; | |||
| 365 | } | |||
| 366 | ||||
| 367 | void rosprite_destroy_mem_context(struct rosprite_mem_context* ctx) | |||
| 368 | { | |||
| 369 | free(ctx); | |||
| 370 | } | |||
| 371 | ||||
| 372 | int rosprite_mem_reader(uint8_t* buf, size_t count, void* ctx) | |||
| 373 | { | |||
| 374 | size_t copy_size; | |||
| 375 | struct rosprite_mem_context* memctx = ctx; | |||
| 376 | ||||
| 377 | /* if we're asked for more memory than the block contains, | |||
| 378 | * only copy as much as we can | |||
| 379 | */ | |||
| 380 | if ((memctx->offset + count) > memctx->size) { | |||
| 381 | copy_size = memctx->size - memctx->offset; | |||
| 382 | } else { | |||
| 383 | copy_size = count; | |||
| 384 | } | |||
| 385 | memcpy(buf, memctx->base + memctx->offset, copy_size); | |||
| 386 | memctx->offset += copy_size; | |||
| 387 | return copy_size; | |||
| 388 | } | |||
| 389 | ||||
| 390 | /** | |||
| 391 | * Load a single sprite. | |||
| 392 | * | |||
| 393 | * \param[out] result | |||
| 394 | */ | |||
| 395 | rosprite_error rosprite_load_sprite(reader reader, void* ctx, struct rosprite** result) | |||
| 396 | { | |||
| 397 | uint32_t nextSpriteOffset; | |||
| 398 | uint32_t imageOffset; | |||
| 399 | uint32_t maskOffset, spriteModeWord; | |||
| 400 | uint32_t paletteEntries; | |||
| 401 | uint8_t* image; | |||
| 402 | uint8_t* mask = NULL((void*)0); | |||
| 403 | ||||
| 404 | struct rosprite* sprite = malloc(sizeof(struct rosprite)); | |||
| 405 | struct rosprite_header* header = malloc(sizeof(struct rosprite_header)); | |||
| 406 | ||||
| 407 | ERRCHK(rosprite_read_word(reader, ctx, &nextSpriteOffset))do { rosprite_error err = rosprite_read_word(reader, ctx, & nextSpriteOffset); if (err != ROSPRITE_OK) return err; } while (0); | |||
| 408 | ||||
| 409 | reader(sprite->name, 12, ctx); | |||
| 410 | sprite->name[12] = '\0'; | |||
| 411 | ||||
| 412 | ERRCHK(rosprite_read_word(reader, ctx, &header->width_words))do { rosprite_error err = rosprite_read_word(reader, ctx, & header->width_words); if (err != ROSPRITE_OK) return err; } while(0); /* file has width - 1 and height - 1 */ | |||
| 413 | header->width_words += 1; | |||
| 414 | ERRCHK(rosprite_read_word(reader, ctx, &(sprite->height)))do { rosprite_error err = rosprite_read_word(reader, ctx, & (sprite->height)); if (err != ROSPRITE_OK) return err; } while (0); | |||
| 415 | sprite->height += 1; | |||
| 416 | ERRCHK(rosprite_read_word(reader, ctx, &(header->first_used_bit)))do { rosprite_error err = rosprite_read_word(reader, ctx, & (header->first_used_bit)); if (err != ROSPRITE_OK) return err ; } while(0); /* old format only (spriteType = 0) */ | |||
| 417 | ERRCHK(rosprite_read_word(reader, ctx, &(header->last_used_bit)))do { rosprite_error err = rosprite_read_word(reader, ctx, & (header->last_used_bit)); if (err != ROSPRITE_OK) return err ; } while(0); | |||
| 418 | ||||
| 419 | ERRCHK(rosprite_read_word(reader, ctx, &imageOffset))do { rosprite_error err = rosprite_read_word(reader, ctx, & imageOffset); if (err != ROSPRITE_OK) return err; } while(0); | |||
| 420 | assert(imageOffset >= 44)((imageOffset >= 44) ? (void) (0) : __assert_fail ("imageOffset >= 44" , "src/librosprite.c", 420, __extension__ __PRETTY_FUNCTION__ )); /* should never be smaller than the size of the header) */ | |||
| 421 | ||||
| 422 | ERRCHK(rosprite_read_word(reader, ctx, &maskOffset))do { rosprite_error err = rosprite_read_word(reader, ctx, & maskOffset); if (err != ROSPRITE_OK) return err; } while(0); | |||
| 423 | ERRCHK(rosprite_read_word(reader, ctx, &spriteModeWord))do { rosprite_error err = rosprite_read_word(reader, ctx, & spriteModeWord); if (err != ROSPRITE_OK) return err; } while( 0); | |||
| 424 | ||||
| 425 | ERRCHK(rosprite_get_mode(spriteModeWord, &(sprite->mode)))do { rosprite_error err = rosprite_get_mode(spriteModeWord, & (sprite->mode)); if (err != ROSPRITE_OK) return err; } while (0); | |||
| 426 | ||||
| 427 | /* TODO left-hand wastage */ | |||
| 428 | ||||
| 429 | assert((header->last_used_bit + 1) % sprite->mode.colorbpp == 0)(((header->last_used_bit + 1) % sprite->mode.colorbpp == 0) ? (void) (0) : __assert_fail ("(header->last_used_bit + 1) % sprite->mode.colorbpp == 0" , "src/librosprite.c", 429, __extension__ __PRETTY_FUNCTION__ )); | |||
| 430 | /*assert(header->width_words % sprite->mode->colorbpp == 0);*/ | |||
| 431 | sprite->width = (header->width_words * 32 - header->first_used_bit - (31 - header->last_used_bit)) / sprite->mode.colorbpp; | |||
| 432 | ||||
| 433 | sprite->palettesize = imageOffset - 44; | |||
| 434 | sprite->has_palette = (sprite->palettesize > 0); | |||
| 435 | ||||
| 436 | /* sprite has no mask if imageOffset == maskOffset */ | |||
| 437 | if (imageOffset == maskOffset) { | |||
| 438 | sprite->has_mask = false0; | |||
| 439 | header->image_size = nextSpriteOffset - 44 - sprite->palettesize; | |||
| 440 | header->mask_size = 0; | |||
| 441 | } else { | |||
| 442 | sprite->has_mask = true1; | |||
| 443 | header->image_size = maskOffset - imageOffset; | |||
| 444 | header->mask_size = nextSpriteOffset - 44 - sprite->palettesize - header->image_size; | |||
| 445 | } | |||
| 446 | ||||
| 447 | if (sprite->has_palette) { | |||
| 448 | uint32_t j, word1, word2, entry; | |||
| 449 | assert(sprite->palettesize % 8 == 0)((sprite->palettesize % 8 == 0) ? (void) (0) : __assert_fail ("sprite->palettesize % 8 == 0", "src/librosprite.c", 449 , __extension__ __PRETTY_FUNCTION__)); | |||
| 450 | sprite->palette = malloc(sizeof(uint32_t) * sprite->palettesize); | |||
| 451 | paletteEntries = sprite->palettesize / 8; | |||
| 452 | ||||
| 453 | /* Each palette entry is two words big | |||
| 454 | * The second word is a duplicate of the first | |||
| 455 | * I think this is in case you ever wanted flashing colours | |||
| 456 | * PRM1-730 | |||
| 457 | */ | |||
| 458 | for (j = 0; j < paletteEntries; j++) { | |||
| 459 | ERRCHK(rosprite_read_word(reader, ctx, &word1))do { rosprite_error err = rosprite_read_word(reader, ctx, & word1); if (err != ROSPRITE_OK) return err; } while(0); | |||
| 460 | ERRCHK(rosprite_read_word(reader, ctx, &word2))do { rosprite_error err = rosprite_read_word(reader, ctx, & word2); if (err != ROSPRITE_OK) return err; } while(0); | |||
| 461 | assert(word1 == word2)((word1 == word2) ? (void) (0) : __assert_fail ("word1 == word2" , "src/librosprite.c", 461, __extension__ __PRETTY_FUNCTION__ )); /* if they aren't equal, flashing colours are desired, which we don't support */ | |||
| 462 | ||||
| 463 | /* swap rr and bb parts -- PRM1-731 */ | |||
| 464 | entry = ((word1 & 0xff000000) >> 16) | (word1 & 0x00ff0000) | ((word1 & 0x0000ff00) << 16) | 0xff; | |||
| 465 | sprite->palette[j] = entry; | |||
| 466 | } | |||
| 467 | } | |||
| 468 | ||||
| 469 | image = malloc(header->image_size); | |||
| 470 | reader(image, header->image_size, ctx); | |||
| 471 | ||||
| 472 | if (sprite->has_mask) { | |||
| 473 | mask = malloc(header->mask_size); | |||
| 474 | reader(mask, header->mask_size, ctx); | |||
| 475 | } | |||
| 476 | ||||
| 477 | /* sanity check image_size */ | |||
| 478 | assert((header->width_words) * 4 * (sprite->height) == header->image_size)(((header->width_words) * 4 * (sprite->height) == header ->image_size) ? (void) (0) : __assert_fail ("(header->width_words) * 4 * (sprite->height) == header->image_size" , "src/librosprite.c", 478, __extension__ __PRETTY_FUNCTION__ )); | |||
| 479 | /* TODO: sanity check mask_size */ | |||
| 480 | if (sprite->mode.colorbpp > 8) { | |||
| 481 | ERRCHK(rosprite_load_high_color(image, mask, sprite, header))do { rosprite_error err = rosprite_load_high_color(image, mask , sprite, header); if (err != ROSPRITE_OK) return err; } while (0); | |||
| 482 | } else { | |||
| 483 | ERRCHK(rosprite_load_low_color(image, mask, sprite, header))do { rosprite_error err = rosprite_load_low_color(image, mask , sprite, header); if (err != ROSPRITE_OK) return err; } while (0); | |||
| 484 | } | |||
| 485 | ||||
| 486 | free(image); | |||
| 487 | free(mask); | |||
| 488 | free(header); | |||
| 489 | ||||
| 490 | *result = sprite; | |||
| 491 | ||||
| 492 | return ROSPRITE_OK; | |||
| 493 | } | |||
| 494 | ||||
| 495 | /** | |||
| 496 | * Determine the sprite_mode for the specified sprite_mode_word. | |||
| 497 | * | |||
| 498 | * \param[out] result | |||
| 499 | */ | |||
| 500 | static rosprite_error rosprite_get_mode(uint32_t sprite_mode_word, struct rosprite_mode* result) | |||
| 501 | { | |||
| 502 | struct rosprite_mode mode; | |||
| 503 | ||||
| 504 | uint32_t spriteType = (sprite_mode_word & (15 << 27)) >> 27; /* preserve bits 27-30 only */ | |||
| 505 | ||||
| 506 | if (spriteType != 0) { | |||
| 507 | bool_Bool hasEightBitAlpha = sprite_mode_word >> 31; /* bit 31 */ | |||
| 508 | /* new modes have 1bpp masks (PRM5a-111) | |||
| 509 | * unless bit 31 is set (http://select.riscos.com/prm/graphics/sprites/alphachannel.html) | |||
| 510 | */ | |||
| 511 | mode.maskbpp = (hasEightBitAlpha ? 8 : 1); | |||
| 512 | mode.mask_width = mode.maskbpp; | |||
| 513 | mode.xdpi = (sprite_mode_word & (8191 << 14)) >> 14; /* preserve bits 14-26 only */ | |||
| 514 | mode.ydpi = (sprite_mode_word & (8191 << 1)) >> 1; /* preserve bits 1-13 only */ | |||
| 515 | ||||
| 516 | mode.color_model = ROSPRITE_RGB; | |||
| 517 | switch (spriteType) { | |||
| 518 | case 1: | |||
| 519 | mode.colorbpp = 1; break; | |||
| 520 | case 2: | |||
| 521 | mode.colorbpp = 2; break; | |||
| 522 | case 3: | |||
| 523 | mode.colorbpp = 4; break; | |||
| 524 | case 4: | |||
| 525 | mode.colorbpp = 8; break; | |||
| 526 | case 5: | |||
| 527 | mode.colorbpp = 16; break; | |||
| 528 | case 6: | |||
| 529 | mode.colorbpp = 32; break; | |||
| 530 | case 7: | |||
| 531 | mode.colorbpp = 32; | |||
| 532 | mode.color_model = ROSPRITE_CMYK; | |||
| 533 | break; | |||
| 534 | case 8: | |||
| 535 | mode.colorbpp = 24; break; | |||
| 536 | default: | |||
| 537 | return ROSPRITE_BADMODE; | |||
| 538 | } | |||
| 539 | } else { | |||
| 540 | assert(sprite_mode_word < 256)((sprite_mode_word < 256) ? (void) (0) : __assert_fail ("sprite_mode_word < 256" , "src/librosprite.c", 540, __extension__ __PRETTY_FUNCTION__ )); /* don't think you can have modes over 255? */ | |||
| 541 | ||||
| 542 | mode = oldmodes[sprite_mode_word]; | |||
| 543 | } | |||
| 544 | ||||
| 545 | /* illegal mode check */ | |||
| 546 | if ((mode.colorbpp == 0) || (mode.xdpi == 0) || (mode.ydpi == 0)) { | |||
| 547 | return ROSPRITE_BADMODE; | |||
| 548 | } | |||
| 549 | ||||
| 550 | memcpy(result, &mode, sizeof(struct rosprite_mode)); | |||
| 551 | ||||
| 552 | return ROSPRITE_OK; | |||
| 553 | } | |||
| 554 | ||||
| 555 | /** | |||
| 556 | * Load a sprite image with 16 or more bpp. | |||
| 557 | * | |||
| 558 | * \param[out] sprite On exit, sprite.image will be populated | |||
| 559 | */ | |||
| 560 | static rosprite_error rosprite_load_high_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header) | |||
| 561 | { | |||
| 562 | struct rosprite_mask_state* mask_state = NULL((void*)0); | |||
| 563 | uint32_t currentByteIndex = 0; | |||
| 564 | uint32_t j, x, y, x_pixels, pixel; | |||
| 565 | bool_Bool has_alpha_pixel_data = false0; | |||
| 566 | uint32_t b; | |||
| 567 | bool_Bool old_has_alpha; | |||
| 568 | ||||
| 569 | if (sprite->has_mask) { | |||
| 570 | ERRCHK(rosprite_init_mask_state(sprite, header, mask, &mask_state))do { rosprite_error err = rosprite_init_mask_state(sprite, header , mask, &mask_state); if (err != ROSPRITE_OK) return err; } while(0); | |||
| 571 | } | |||
| 572 | ||||
| 573 | sprite->image = malloc(sprite->width * sprite->height * 4); /* all image data is 32bpp going out */ | |||
| 574 | ||||
| 575 | /* Spec says that there must be no left-hand wastage */ | |||
| 576 | assert(header->first_used_bit == 0)((header->first_used_bit == 0) ? (void) (0) : __assert_fail ("header->first_used_bit == 0", "src/librosprite.c", 576, __extension__ __PRETTY_FUNCTION__)); | |||
| 577 | ||||
| 578 | { | |||
| 579 | const uint32_t bpp = sprite->mode.colorbpp; | |||
| 580 | const uint32_t bytesPerPixel = bpp / 8; | |||
| 581 | const uint32_t row_max_bit = header->width_words * 32 - (31 - header->last_used_bit); /* Last used bit in row */ | |||
| 582 | ||||
| 583 | for (y = 0; y < sprite->height; y++) { | |||
| 584 | x_pixels = 0; | |||
| 585 | for (x = 0; x < row_max_bit; x += bpp) { | |||
| 586 | pixel = 0; | |||
| 587 | for (j = 0; j < bytesPerPixel; j++) { | |||
| 588 | b = image_in[currentByteIndex++]; | |||
| 589 | pixel = pixel | (b << (j * 8)); | |||
| 590 | } | |||
| 591 | ||||
| 592 | old_has_alpha = has_alpha_pixel_data; | |||
| 593 | pixel = rosprite_upscale_color(pixel, &(sprite->mode), &has_alpha_pixel_data); | |||
| 594 | if (old_has_alpha != has_alpha_pixel_data && (y > 0 || x_pixels > 0)) { | |||
| 595 | rosprite_fix_alpha(sprite->image, (y * sprite->width) + x_pixels - 1); | |||
| 596 | } | |||
| 597 | if (sprite->has_mask) { | |||
| 598 | uint8_t mask_pixel = rosprite_next_mask_pixel(mask, mask_state); | |||
| 599 | pixel = (pixel & 0xffffff00) | mask_pixel; | |||
| 600 | } | |||
| 601 | sprite->image[y*sprite->width + x_pixels] = pixel; | |||
| 602 | x_pixels++; | |||
| 603 | } | |||
| 604 | ||||
| 605 | /* Ensure byte index is pointing at start of next row */ | |||
| 606 | if (y + 1 < sprite->height) { | |||
| 607 | currentByteIndex = (currentByteIndex + 3) & ~3; /* Round up to next multiple of 4 */ | |||
| 608 | } | |||
| 609 | } | |||
| 610 | } | |||
| 611 | if (sprite->has_mask) free(mask_state); | |||
| 612 | return ROSPRITE_OK; | |||
| 613 | } | |||
| 614 | ||||
| 615 | /** | |||
| 616 | * Iterate over the specified number of pixels, starting at the image pointer, | |||
| 617 | * and set the alpha channel to 0x00. | |||
| 618 | */ | |||
| 619 | static inline void rosprite_fix_alpha(uint32_t* image, unsigned long pixels) | |||
| 620 | { | |||
| 621 | uint32_t i; | |||
| 622 | for (i = 0; i <= pixels; i++) { | |||
| 623 | image[i] = image[i] & 0xffffff00; | |||
| 624 | } | |||
| 625 | } | |||
| 626 | ||||
| 627 | /** | |||
| 628 | * Load a sprite image with 8 or fewer bpp. | |||
| 629 | * | |||
| 630 | * \param[out] sprite On exit, sprite.image will be populated | |||
| 631 | */ | |||
| 632 | static rosprite_error rosprite_load_low_color(uint8_t* image_in, uint8_t* mask, struct rosprite* sprite, struct rosprite_header* header) | |||
| 633 | { | |||
| 634 | uint32_t current_byte_index, currentword; | |||
| 635 | uint32_t x, y, x_pixels, pixel; | |||
| 636 | uint8_t mask_pixel; | |||
| 637 | ||||
| 638 | struct rosprite_mask_state* mask_state = NULL((void*)0); | |||
| 639 | if (sprite->has_mask) { | |||
| 640 | ERRCHK(rosprite_init_mask_state(sprite, header, mask, &mask_state))do { rosprite_error err = rosprite_init_mask_state(sprite, header , mask, &mask_state); if (err != ROSPRITE_OK) return err; } while(0); | |||
| 641 | } | |||
| 642 | ||||
| 643 | sprite->image = malloc(sprite->width * sprite->height * 4); /* all image data is 32bpp going out */ | |||
| 644 | ||||
| 645 | { | |||
| 646 | const uint32_t bpp = sprite->mode.colorbpp; | |||
| 647 | const uint32_t row_max_bit = header->width_words * 32 - (31 - header->last_used_bit); /* Last used bit in row */ | |||
| 648 | const uint32_t bitmask = (1 << bpp) - 1; /* creates a mask of 1s that is bpp bits wide */ | |||
| 649 | ||||
| 650 | current_byte_index = 0; | |||
| 651 | currentword = BTUINT((image_in + current_byte_index))((unsigned)(image_in + current_byte_index)[0] | ((unsigned)(image_in + current_byte_index)[1] << 8) | ((unsigned)(image_in + current_byte_index)[2] << 16) | ((unsigned)(image_in + current_byte_index)[3] << 24)); | |||
| 652 | current_byte_index += 4; | |||
| 653 | ||||
| 654 | for (y = 0; y < sprite->height; y++) { | |||
| 655 | x_pixels = 0; | |||
| 656 | for (x = header->first_used_bit; x < row_max_bit ; x += bpp) { | |||
| 657 | const uint32_t offset_into_word = x % 32; | |||
| 658 | ||||
| 659 | pixel = (currentword & (bitmask << offset_into_word)) >> offset_into_word; | |||
| 660 | pixel = rosprite_palette_lookup(sprite, pixel); /* lookup returns 32bpp */ | |||
| 661 | if (sprite->has_mask) { | |||
| 662 | mask_pixel = rosprite_next_mask_pixel(mask, mask_state); | |||
| 663 | pixel = (pixel & 0xffffff00) | mask_pixel; | |||
| 664 | } | |||
| 665 | sprite->image[y*sprite->width + x_pixels] = pixel; | |||
| 666 | x_pixels++; | |||
| 667 | ||||
| 668 | /* If we're not at the end of the row and we've processed all of this word, fetch the next one */ | |||
| 669 | if (x + bpp < row_max_bit && offset_into_word + bpp == 32) { | |||
| 670 | currentword = BTUINT((image_in + current_byte_index))((unsigned)(image_in + current_byte_index)[0] | ((unsigned)(image_in + current_byte_index)[1] << 8) | ((unsigned)(image_in + current_byte_index)[2] << 16) | ((unsigned)(image_in + current_byte_index)[3] << 24)); | |||
| 671 | current_byte_index += 4; | |||
| 672 | } | |||
| 673 | } | |||
| 674 | ||||
| 675 | /* Advance to first word of next row */ | |||
| 676 | if (y + 1 < sprite->height) { | |||
| 677 | currentword = BTUINT((image_in + current_byte_index))((unsigned)(image_in + current_byte_index)[0] | ((unsigned)(image_in + current_byte_index)[1] << 8) | ((unsigned)(image_in + current_byte_index)[2] << 16) | ((unsigned)(image_in + current_byte_index)[3] << 24)); | |||
| 678 | current_byte_index += 4; | |||
| 679 | } | |||
| 680 | } | |||
| 681 | } | |||
| 682 | if (sprite->has_mask) free(mask_state); | |||
| 683 | ||||
| 684 | return ROSPRITE_OK; | |||
| 685 | } | |||
| 686 | ||||
| 687 | static uint32_t rosprite_palette_lookup(struct rosprite* sprite, uint32_t pixel) | |||
| 688 | { | |||
| 689 | uint32_t translated_pixel; | |||
| 690 | /* because we're dealing with 8bpp or less */ | |||
| 691 | if (sprite->has_palette) { | |||
| 692 | ||||
| 693 | if(pixel < (sprite->palettesize / 8)) { | |||
| 694 | translated_pixel = sprite->palette[pixel]; | |||
| 695 | } else { | |||
| 696 | translated_pixel = sprite_8bpp_palette[pixel]; | |||
| 697 | } | |||
| 698 | } else { | |||
| 699 | switch (sprite->mode.colorbpp) { | |||
| 700 | case 8: | |||
| 701 | assert(pixel < 256)((pixel < 256) ? (void) (0) : __assert_fail ("pixel < 256" , "src/librosprite.c", 701, __extension__ __PRETTY_FUNCTION__ )); | |||
| 702 | translated_pixel = sprite_8bpp_palette[pixel]; | |||
| 703 | break; | |||
| 704 | case 4: | |||
| 705 | assert(pixel < 16)((pixel < 16) ? (void) (0) : __assert_fail ("pixel < 16" , "src/librosprite.c", 705, __extension__ __PRETTY_FUNCTION__ )); | |||
| 706 | translated_pixel = sprite_4bpp_palette[pixel]; | |||
| 707 | break; | |||
| 708 | case 2: | |||
| 709 | assert(pixel < 4)((pixel < 4) ? (void) (0) : __assert_fail ("pixel < 4", "src/librosprite.c", 709, __extension__ __PRETTY_FUNCTION__) ); | |||
| 710 | translated_pixel = sprite_2bpp_palette[pixel]; | |||
| 711 | break; | |||
| 712 | case 1: | |||
| 713 | assert(pixel < 2)((pixel < 2) ? (void) (0) : __assert_fail ("pixel < 2", "src/librosprite.c", 713, __extension__ __PRETTY_FUNCTION__) ); | |||
| 714 | translated_pixel = sprite_1bpp_palette[pixel]; | |||
| 715 | break; | |||
| 716 | default: | |||
| 717 | translated_pixel = 0; | |||
| 718 | assert(false)((0) ? (void) (0) : __assert_fail ("false", "src/librosprite.c" , 718, __extension__ __PRETTY_FUNCTION__)); | |||
| 719 | } | |||
| 720 | } | |||
| 721 | return translated_pixel; | |||
| 722 | } | |||
| 723 | ||||
| 724 | static rosprite_error rosprite_init_mask_state(struct rosprite* sprite, struct rosprite_header* header, uint8_t* mask, struct rosprite_mask_state** result) | |||
| 725 | { | |||
| 726 | struct rosprite_mask_state* mask_state = malloc(sizeof(struct rosprite_mask_state)); | |||
| 727 | if (!mask_state) return ROSPRITE_NOMEM; | |||
| 728 | ||||
| 729 | mask_state->x = header->first_used_bit; | |||
| 730 | mask_state->y = 0; | |||
| 731 | mask_state->first_used_bit = header->first_used_bit; | |||
| 732 | mask_state->row_max_bit = sprite->width * sprite->mode.mask_width; | |||
| 733 | mask_state->height = sprite->height; | |||
| 734 | mask_state->bpp = sprite->mode.mask_width; | |||
| 735 | mask_state->current_word = BTUINT(mask)((unsigned)mask[0] | ((unsigned)mask[1] << 8) | ((unsigned )mask[2] << 16) | ((unsigned)mask[3] << 24)); | |||
| 736 | mask_state->current_byte_index = 4; | |||
| 737 | ||||
| 738 | *result = mask_state; | |||
| 739 | ||||
| 740 | return ROSPRITE_OK; | |||
| 741 | } | |||
| 742 | ||||
| 743 | /** | |||
| 744 | * Get the next mask byte. | |||
| 745 | * A mask of 0xff denotes 100% opaque, 0x00 denotes 100% transparent. | |||
| 746 | * | |||
| 747 | * \param[in,out] mask_state | |||
| 748 | */ | |||
| 749 | static uint32_t rosprite_next_mask_pixel(uint8_t* mask, struct rosprite_mask_state* mask_state) | |||
| 750 | { | |||
| 751 | /* a 1bpp mask (for new mode sprites), each row is word aligned (therefore potential righthand wastage */ | |||
| 752 | const uint32_t bitmask = (1 << mask_state->bpp) - 1; | |||
| 753 | const uint32_t offset_into_word = mask_state->x % 32; | |||
| 754 | uint32_t pixel = (mask_state->current_word & (bitmask << offset_into_word)) >> offset_into_word; | |||
| 755 | ||||
| 756 | if (mask_state->x + mask_state->bpp < mask_state->row_max_bit && offset_into_word + mask_state->bpp == 32) { | |||
| 757 | mask_state->current_word = BTUINT((mask + mask_state->current_byte_index))((unsigned)(mask + mask_state->current_byte_index)[0] | (( unsigned)(mask + mask_state->current_byte_index)[1] << 8) | ((unsigned)(mask + mask_state->current_byte_index)[2 ] << 16) | ((unsigned)(mask + mask_state->current_byte_index )[3] << 24)); | |||
| 758 | mask_state->current_byte_index += 4; | |||
| 759 | } | |||
| 760 | ||||
| 761 | mask_state->x += mask_state->bpp; | |||
| 762 | if (mask_state->x >= mask_state->row_max_bit) { | |||
| 763 | mask_state->x = mask_state->first_used_bit; | |||
| 764 | ||||
| 765 | if (mask_state->y + 1 < mask_state->height) { | |||
| 766 | mask_state->current_word = BTUINT((mask + mask_state->current_byte_index))((unsigned)(mask + mask_state->current_byte_index)[0] | (( unsigned)(mask + mask_state->current_byte_index)[1] << 8) | ((unsigned)(mask + mask_state->current_byte_index)[2 ] << 16) | ((unsigned)(mask + mask_state->current_byte_index )[3] << 24)); | |||
| 767 | mask_state->current_byte_index += 4; | |||
| 768 | } | |||
| 769 | ||||
| 770 | mask_state->y++; | |||
| 771 | } | |||
| 772 | ||||
| 773 | /* | |||
| 774 | * if the mask is 1bpp, make sure the whole byte is 0x00 or 0xff | |||
| 775 | */ | |||
| 776 | if (mask_state->bpp < 8) { | |||
| 777 | pixel = (pixel & 1) ? 0xff : 0x00; | |||
| 778 | } | |||
| 779 | ||||
| 780 | return pixel; | |||
| 781 | } | |||
| 782 | ||||
| 783 | /** | |||
| 784 | * Upscale a 16, 24 or 32bpp sprite to the 0xRRGGBBAA representation. | |||
| 785 | * Do not call this function with a sprite with less than 16bpp, | |||
| 786 | * but use a palette lookup instead. | |||
| 787 | */ | |||
| 788 | static uint32_t rosprite_upscale_color(uint32_t pixel, struct rosprite_mode* mode, bool_Bool* has_alpha_pixel_data) | |||
| 789 | { | |||
| 790 | uint8_t alpha; | |||
| 791 | switch (mode->colorbpp) { | |||
| 792 | case 32: | |||
| 793 | if (mode->color_model == ROSPRITE_RGB) { | |||
| 794 | /* swap from 0xAABBGGRR to 0xRRGGBBAA */ | |||
| 795 | pixel = BSWAP(pixel)(((pixel & (0x000000ff)) << 24) | ((pixel & 0x0000ff00 ) << 8) | ((pixel & 0x00ff0000) >> 8) | ((pixel & 0xff000000) >> 24)); | |||
| 796 | } else { | |||
| 797 | pixel = rosprite_cmyk_to_rgb(pixel); | |||
| 798 | } | |||
| 799 | break; | |||
| 800 | case 24: | |||
| 801 | /* reverse byte order */ | |||
| 802 | pixel = BSWAP(pixel)(((pixel & (0x000000ff)) << 24) | ((pixel & 0x0000ff00 ) << 8) | ((pixel & 0x00ff0000) >> 8) | ((pixel & 0xff000000) >> 24)); | |||
| 803 | break; | |||
| 804 | case 16: | |||
| 805 | /* incoming format is b_00000000000000000bbbbbgggggrrrrr */ | |||
| 806 | { | |||
| 807 | uint8_t red = pixel & 31; | |||
| 808 | uint8_t green = (pixel & (31 << 5)) >> 5; | |||
| 809 | uint8_t blue = (pixel & (31 << 10)) >> 10; | |||
| 810 | ||||
| 811 | /* sanity check */ | |||
| 812 | assert(red < 32)((red < 32) ? (void) (0) : __assert_fail ("red < 32", "src/librosprite.c" , 812, __extension__ __PRETTY_FUNCTION__)); | |||
| 813 | assert(green < 32)((green < 32) ? (void) (0) : __assert_fail ("green < 32" , "src/librosprite.c", 813, __extension__ __PRETTY_FUNCTION__ )); | |||
| 814 | assert(blue < 32)((blue < 32) ? (void) (0) : __assert_fail ("blue < 32", "src/librosprite.c", 814, __extension__ __PRETTY_FUNCTION__) ); | |||
| 815 | ||||
| 816 | pixel = ((uint32_t)sprite_16bpp_translate[red] << 24)| | |||
| 817 | ((uint32_t)sprite_16bpp_translate[green] << 16)| | |||
| 818 | ((uint32_t)sprite_16bpp_translate[blue] << 8); | |||
| 819 | } | |||
| 820 | break; | |||
| 821 | case 8: | |||
| 822 | case 4: | |||
| 823 | case 2: | |||
| 824 | case 1: | |||
| 825 | assert(false)((0) ? (void) (0) : __assert_fail ("false", "src/librosprite.c" , 825, __extension__ __PRETTY_FUNCTION__)); /* shouldn't need to call for <= 8bpp, since a palette lookup will return 32bpp */ | |||
| 826 | default: | |||
| 827 | assert(false)((0) ? (void) (0) : __assert_fail ("false", "src/librosprite.c" , 827, __extension__ __PRETTY_FUNCTION__)); /* unknown bpp */ | |||
| 828 | } | |||
| 829 | ||||
| 830 | alpha = pixel & 0xff; | |||
| 831 | if (alpha == 0x00) { | |||
| 832 | if (!(*has_alpha_pixel_data)) { | |||
| 833 | pixel = pixel | 0xff; | |||
| 834 | } | |||
| 835 | } else { | |||
| 836 | *has_alpha_pixel_data = true1; | |||
| 837 | } | |||
| 838 | return pixel; | |||
| 839 | } | |||
| 840 | ||||
| 841 | static inline uint32_t rosprite_cmyk_to_rgb(uint32_t cmyk) | |||
| 842 | { | |||
| 843 | uint8_t c = cmyk & 0xff; | |||
| 844 | uint8_t m = (cmyk & 0xff00) >> 8; | |||
| 845 | uint8_t y = (cmyk & 0xff0000) >> 16; | |||
| 846 | uint8_t k = cmyk >> 24; | |||
| 847 | ||||
| 848 | /* Convert to CMY colourspace */ | |||
| 849 | uint8_t C = c + k; | |||
| 850 | uint8_t M = m + k; | |||
| 851 | uint8_t Y = y + k; | |||
| 852 | ||||
| 853 | /* And to RGB */ | |||
| 854 | uint8_t r = 255 - C; | |||
| 855 | uint8_t g = 255 - M; | |||
| 856 | uint8_t b = 255 - Y; | |||
| 857 | ||||
| 858 | return r << 24 | g << 16 | b << 8; | |||
| 859 | } | |||
| 860 | ||||
| 861 | static inline rosprite_error rosprite_read_word(reader reader, void* ctx, uint32_t* result) | |||
| 862 | { | |||
| 863 | unsigned char b[4]; | |||
| 864 | if (reader(b, 4, ctx) < 4) { | |||
| 865 | return ROSPRITE_EOF; | |||
| 866 | } | |||
| 867 | *result = BTUINT(b)((unsigned)b[0] | ((unsigned)b[1] << 8) | ((unsigned)b[ 2] << 16) | ((unsigned)b[3] << 24)); | |||
| 868 | return ROSPRITE_OK; | |||
| 869 | } |