bad char
[vg.git] / vg_tex.c
1 #include "vg_tex.h"
2 #include "vg_engine.h"
3 #include "vg_async.h"
4 #include "vg_io.h"
5 #include <string.h>
6
7 static u8 const_vg_tex2d_err[] ={
8 #ifdef VG_DEBUG
9 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
10 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
11 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff,
12 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff,
13 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
14 0xff,0x00,0xff,0xff, 0x00,0x00,0x00,0xff,
15 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff,
16 0x00,0x00,0x00,0xff, 0xff,0x00,0xff,0xff,
17 #else
18 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff,
19 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff,
20 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff,
21 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff,
22 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff,
23 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff,
24 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff,
25 0x00,0x00,0x00,0xff, 0x00,0x00,0x00,0xff,
26 #endif
27 };
28
29 #define QOI_SRGB 0
30 #define QOI_LINEAR 1
31
32 typedef struct {
33 unsigned int width;
34 unsigned int height;
35 unsigned char channels;
36 unsigned char colorspace;
37 } qoi_desc;
38
39 #ifndef QOI_ZEROARR
40 #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
41 #endif
42
43 #define QOI_OP_INDEX 0x00 /* 00xxxxxx */
44 #define QOI_OP_DIFF 0x40 /* 01xxxxxx */
45 #define QOI_OP_LUMA 0x80 /* 10xxxxxx */
46 #define QOI_OP_RUN 0xc0 /* 11xxxxxx */
47 #define QOI_OP_RGB 0xfe /* 11111110 */
48 #define QOI_OP_RGBA 0xff /* 11111111 */
49
50 #define QOI_MASK_2 0xc0 /* 11000000 */
51
52 #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
53 #define QOI_MAGIC \
54 (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
55 ((unsigned int)'i') << 8 | ((unsigned int)'f'))
56 #define QOI_HEADER_SIZE 14
57
58 /* 2GB is the max file size that this implementation can safely handle. We guard
59 against anything larger than that, assuming the worst case with 5 bytes per
60 pixel, rounded down to a nice clean value. 400 million pixels ought to be
61 enough for anybody. */
62 #define QOI_PIXELS_MAX ((unsigned int)400000000)
63
64 typedef union {
65 struct { unsigned char r, g, b, a; } rgba;
66 unsigned int v;
67 } qoi_rgba_t;
68
69 static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
70 static u32 qoi_read_32( const u8 *bytes, int *p ) {
71 u32 a = bytes[(*p)++];
72 u32 b = bytes[(*p)++];
73 u32 c = bytes[(*p)++];
74 u32 d = bytes[(*p)++];
75 return a << 24 | b << 16 | c << 8 | d;
76 }
77
78 struct texture_load_info{
79 GLuint *dest;
80 u32 width, height, flags;
81 u8 *rgba;
82 };
83
84 static void async_vg_tex2d_upload( void *payload, u32 size )
85 {
86 if( vg_thread_purpose() != k_thread_purpose_main ){
87 vg_fatal_error( "Catastrophic programming error.\n" );
88 }
89
90 struct texture_load_info *info = payload;
91
92 glGenTextures( 1, info->dest );
93 glBindTexture( GL_TEXTURE_2D, *info->dest );
94 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, info->width, info->height,
95 0, GL_RGBA, GL_UNSIGNED_BYTE, info->rgba );
96
97 if( !(info->flags & VG_TEX2D_NOMIP) ){
98 glGenerateMipmap( GL_TEXTURE_2D );
99 }
100
101 if( info->flags & VG_TEX2D_LINEAR ){
102 if( info->flags & VG_TEX2D_NOMIP ){
103 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
104 }
105 else{
106 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
107 GL_LINEAR_MIPMAP_LINEAR );
108 }
109 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
110 }
111
112 if( info->flags & VG_TEX2D_NEAREST ){
113 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
114 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
115 }
116
117 if( info->flags & VG_TEX2D_CLAMP ){
118 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
119 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
120 }
121
122 if( info->flags & VG_TEX2D_REPEAT ){
123 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
124 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
125 }
126 }
127
128 void vg_tex2d_replace_with_error_async( GLuint *dest )
129 {
130 u32 hdr_size = vg_align8(sizeof(struct texture_load_info));
131
132 vg_async_item *call = vg_async_alloc( hdr_size );
133 struct texture_load_info *info = call->payload;
134
135 info->dest = dest;
136 info->flags = VG_TEX2D_NEAREST|VG_TEX2D_REPEAT|VG_TEX2D_NOMIP;
137 info->width = 4;
138 info->height = 4;
139 info->rgba = const_vg_tex2d_err;
140
141 vg_async_dispatch( call, async_vg_tex2d_upload );
142 }
143
144 void vg_tex2d_load_qoi_async( const u8 *bytes, u32 size,
145 u32 flags, GLuint *dest )
146 {
147 u32 header_magic;
148 qoi_rgba_t index[64];
149 qoi_rgba_t px;
150 int px_len, chunks_len, px_pos;
151 int p = 0, run = 0;
152
153 u32 channels = 4; /* TODO */
154
155 qoi_desc desc;
156
157 if (
158 bytes == NULL ||
159 (channels != 0 && channels != 3 && channels != 4) ||
160 size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)
161 ) {
162 vg_error( "Error while decoding qoi file: illegal parameters\n" );
163 vg_tex2d_replace_with_error_async( dest );
164 return;
165 }
166
167 header_magic = qoi_read_32(bytes, &p);
168 desc.width = qoi_read_32(bytes, &p);
169 desc.height = qoi_read_32(bytes, &p);
170 desc.channels = bytes[p++];
171 desc.colorspace = bytes[p++];
172
173 if (
174 desc.width == 0 || desc.height == 0 ||
175 desc.channels < 3 || desc.channels > 4 ||
176 desc.colorspace > 1 ||
177 header_magic != QOI_MAGIC ||
178 desc.height >= QOI_PIXELS_MAX / desc.width
179 ) {
180 vg_error( "Error while decoding qoi file: invalid file\n" );
181 vg_tex2d_replace_with_error_async( dest );
182 return;
183 }
184
185 if (channels == 0) {
186 channels = desc.channels;
187 }
188
189 px_len = desc.width * desc.height * channels;
190
191 /* allocate async call
192 * --------------------------
193 */
194 u32 hdr_size = vg_align8(sizeof(struct texture_load_info)),
195 tex_size = vg_align8(px_len);
196
197 vg_async_item *call = vg_async_alloc( hdr_size + tex_size );
198 struct texture_load_info *info = call->payload;
199
200 info->dest = dest;
201 info->flags = flags;
202 info->width = desc.width;
203 info->height = desc.height;
204 info->rgba = ((u8*)call->payload) + hdr_size;
205
206 /*
207 * Decode
208 * ----------------------------
209 */
210
211 u8 *pixels = info->rgba;
212
213 QOI_ZEROARR(index);
214 px.rgba.r = 0;
215 px.rgba.g = 0;
216 px.rgba.b = 0;
217 px.rgba.a = 255;
218
219 chunks_len = size - (int)sizeof(qoi_padding);
220 for (px_pos = 0; px_pos < px_len; px_pos += channels) {
221 if (run > 0) {
222 run--;
223 }
224 else if (p < chunks_len) {
225 int b1 = bytes[p++];
226
227 if (b1 == QOI_OP_RGB) {
228 px.rgba.r = bytes[p++];
229 px.rgba.g = bytes[p++];
230 px.rgba.b = bytes[p++];
231 }
232 else if (b1 == QOI_OP_RGBA) {
233 px.rgba.r = bytes[p++];
234 px.rgba.g = bytes[p++];
235 px.rgba.b = bytes[p++];
236 px.rgba.a = bytes[p++];
237 }
238 else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
239 px = index[b1];
240 }
241 else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
242 px.rgba.r += ((b1 >> 4) & 0x03) - 2;
243 px.rgba.g += ((b1 >> 2) & 0x03) - 2;
244 px.rgba.b += ( b1 & 0x03) - 2;
245 }
246 else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
247 int b2 = bytes[p++];
248 int vg = (b1 & 0x3f) - 32;
249 px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
250 px.rgba.g += vg;
251 px.rgba.b += vg - 8 + (b2 & 0x0f);
252 }
253 else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
254 run = (b1 & 0x3f);
255 }
256
257 index[QOI_COLOR_HASH(px) % 64] = px;
258 }
259
260 pixels[px_pos + 0] = px.rgba.r;
261 pixels[px_pos + 1] = px.rgba.g;
262 pixels[px_pos + 2] = px.rgba.b;
263
264 if (channels == 4) {
265 pixels[px_pos + 3] = px.rgba.a;
266 }
267 }
268
269 /*
270 * Complete the call
271 * --------------------------
272 */
273
274 vg_async_dispatch( call, async_vg_tex2d_upload );
275 }
276
277 void vg_tex2d_load_qoi_async_file( const char *path, u32 flags, GLuint *dest )
278 {
279 if( vg_thread_purpose() != k_thread_purpose_loader )
280 vg_fatal_error( "wrong thread\n" );
281
282 vg_linear_clear( vg_mem.scratch );
283
284 u32 size;
285 const void *data = vg_file_read( vg_mem.scratch, path, &size );
286 vg_tex2d_load_qoi_async( data, size, flags, dest );
287 }