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 | } |