File: | librosprite.c |
Warning: | line 612, column 9 Potential leak of memory pointed to by 'mask_state' |
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
| ||||
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
| ||||
584 | x_pixels = 0; | ||||
585 | for (x = 0; x
| ||||
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
| ||||
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 | } |