7 static u8 const_vg_tex2d_err
[] ={
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,
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,
35 unsigned char channels
;
36 unsigned char colorspace
;
40 #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
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 */
50 #define QOI_MASK_2 0xc0 /* 11000000 */
52 #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
54 (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
55 ((unsigned int)'i') << 8 | ((unsigned int)'f'))
56 #define QOI_HEADER_SIZE 14
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)
65 struct { unsigned char r
, g
, b
, a
; } rgba
;
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
;
78 struct texture_load_info
{
80 u32 width
, height
, flags
;
84 static void async_vg_tex2d_upload( void *payload
, u32 size
)
86 if( vg_thread_purpose() != k_thread_purpose_main
){
87 vg_fatal_error( "Catastrophic programming error.\n" );
90 struct texture_load_info
*info
= payload
;
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
);
97 if( !(info
->flags
& VG_TEX2D_NOMIP
) ){
98 glGenerateMipmap( GL_TEXTURE_2D
);
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
);
106 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
,
107 GL_LINEAR_MIPMAP_LINEAR
);
109 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
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
);
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
);
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
);
128 void vg_tex2d_replace_with_error_async( GLuint
*dest
)
130 u32 hdr_size
= vg_align8(sizeof(struct texture_load_info
));
132 vg_async_item
*call
= vg_async_alloc( hdr_size
);
133 struct texture_load_info
*info
= call
->payload
;
136 info
->flags
= VG_TEX2D_NEAREST
|VG_TEX2D_REPEAT
|VG_TEX2D_NOMIP
;
139 info
->rgba
= const_vg_tex2d_err
;
141 vg_async_dispatch( call
, async_vg_tex2d_upload
);
144 void vg_tex2d_load_qoi_async( const u8
*bytes
, u32 size
,
145 u32 flags
, GLuint
*dest
)
148 qoi_rgba_t index
[64];
150 int px_len
, chunks_len
, px_pos
;
153 u32 channels
= 4; /* TODO */
159 (channels
!= 0 && channels
!= 3 && channels
!= 4) ||
160 size
< QOI_HEADER_SIZE
+ (int)sizeof(qoi_padding
)
162 vg_error( "Error while decoding qoi file: illegal parameters\n" );
163 vg_tex2d_replace_with_error_async( dest
);
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
++];
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
180 vg_error( "Error while decoding qoi file: invalid file\n" );
181 vg_tex2d_replace_with_error_async( dest
);
186 channels
= desc
.channels
;
189 px_len
= desc
.width
* desc
.height
* channels
;
191 /* allocate async call
192 * --------------------------
194 u32 hdr_size
= vg_align8(sizeof(struct texture_load_info
)),
195 tex_size
= vg_align8(px_len
);
197 vg_async_item
*call
= vg_async_alloc( hdr_size
+ tex_size
);
198 struct texture_load_info
*info
= call
->payload
;
202 info
->width
= desc
.width
;
203 info
->height
= desc
.height
;
204 info
->rgba
= ((u8
*)call
->payload
) + hdr_size
;
208 * ----------------------------
211 u8
*pixels
= info
->rgba
;
219 chunks_len
= size
- (int)sizeof(qoi_padding
);
220 for (px_pos
= 0; px_pos
< px_len
; px_pos
+= channels
) {
224 else if (p
< chunks_len
) {
227 if (b1
== QOI_OP_RGB
) {
228 px
.rgba
.r
= bytes
[p
++];
229 px
.rgba
.g
= bytes
[p
++];
230 px
.rgba
.b
= bytes
[p
++];
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
++];
238 else if ((b1
& QOI_MASK_2
) == QOI_OP_INDEX
) {
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;
246 else if ((b1
& QOI_MASK_2
) == QOI_OP_LUMA
) {
248 int vg
= (b1
& 0x3f) - 32;
249 px
.rgba
.r
+= vg
- 8 + ((b2
>> 4) & 0x0f);
251 px
.rgba
.b
+= vg
- 8 + (b2
& 0x0f);
253 else if ((b1
& QOI_MASK_2
) == QOI_OP_RUN
) {
257 index
[QOI_COLOR_HASH(px
) % 64] = px
;
260 pixels
[px_pos
+ 0] = px
.rgba
.r
;
261 pixels
[px_pos
+ 1] = px
.rgba
.g
;
262 pixels
[px_pos
+ 2] = px
.rgba
.b
;
265 pixels
[px_pos
+ 3] = px
.rgba
.a
;
271 * --------------------------
274 vg_async_dispatch( call
, async_vg_tex2d_upload
);
277 void vg_tex2d_load_qoi_async_file( const char *path
, u32 flags
, GLuint
*dest
)
279 if( vg_thread_purpose() != k_thread_purpose_loader
)
280 vg_fatal_error( "wrong thread\n" );
282 vg_linear_clear( vg_mem
.scratch
);
285 const void *data
= vg_file_read( vg_mem
.scratch
, path
, &size
);
286 vg_tex2d_load_qoi_async( data
, size
, flags
, dest
);