ef7f0e99bfb55db0350a3dbf0589d1bb3150bb32
1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
14 #define VG_TEX2D_LINEAR 0x1
15 #define VG_TEX2D_NEAREST 0x2
16 #define VG_TEX2D_REPEAT 0x4
17 #define VG_TEX2D_CLAMP 0x8
18 #define VG_TEX2D_NOMIP 0x10
20 static u8 const_vg_tex2d_err
[] =
22 0x00, 0xff, 0xff, 0xff,
23 0xff, 0xff, 0x00, 0xff,
24 0x00, 0x00, 0xff, 0xff,
25 0xff, 0xff, 0x00, 0xff
34 unsigned char channels
;
35 unsigned char colorspace
;
39 #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
42 #define QOI_OP_INDEX 0x00 /* 00xxxxxx */
43 #define QOI_OP_DIFF 0x40 /* 01xxxxxx */
44 #define QOI_OP_LUMA 0x80 /* 10xxxxxx */
45 #define QOI_OP_RUN 0xc0 /* 11xxxxxx */
46 #define QOI_OP_RGB 0xfe /* 11111110 */
47 #define QOI_OP_RGBA 0xff /* 11111111 */
49 #define QOI_MASK_2 0xc0 /* 11000000 */
51 #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
53 (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
54 ((unsigned int)'i') << 8 | ((unsigned int)'f'))
55 #define QOI_HEADER_SIZE 14
57 /* 2GB is the max file size that this implementation can safely handle. We guard
58 against anything larger than that, assuming the worst case with 5 bytes per
59 pixel, rounded down to a nice clean value. 400 million pixels ought to be
60 enough for anybody. */
61 #define QOI_PIXELS_MAX ((unsigned int)400000000)
64 struct { unsigned char r
, g
, b
, a
; } rgba
;
68 static const unsigned char qoi_padding
[8] = {0,0,0,0,0,0,0,1};
69 static u32
qoi_read_32( const u8
*bytes
, int *p
) {
70 u32 a
= bytes
[(*p
)++];
71 u32 b
= bytes
[(*p
)++];
72 u32 c
= bytes
[(*p
)++];
73 u32 d
= bytes
[(*p
)++];
74 return a
<< 24 | b
<< 16 | c
<< 8 | d
;
77 struct texture_load_info
{
79 u32 width
, height
, flags
;
83 VG_STATIC
void async_vg_tex2d_upload( void *payload
, u32 size
)
85 struct texture_load_info
*info
= payload
;
87 glGenTextures( 1, info
->dest
);
88 glBindTexture( GL_TEXTURE_2D
, *info
->dest
);
89 glTexImage2D( GL_TEXTURE_2D
, 0, GL_RGBA
, info
->width
, info
->height
,
90 0, GL_RGBA
, GL_UNSIGNED_BYTE
, info
->rgba
);
92 if( !(info
->flags
& VG_TEX2D_NOMIP
) ){
93 glGenerateMipmap( GL_TEXTURE_2D
);
96 if( info
->flags
& VG_TEX2D_LINEAR
){
97 if( info
->flags
& VG_TEX2D_NOMIP
){
98 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
);
101 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
,
102 GL_LINEAR_MIPMAP_LINEAR
);
104 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
);
107 if( info
->flags
& VG_TEX2D_NEAREST
){
108 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
);
109 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
);
112 if( info
->flags
& VG_TEX2D_CLAMP
){
113 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
);
114 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
);
117 if( info
->flags
& VG_TEX2D_REPEAT
){
118 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_REPEAT
);
119 glTexParameteri( GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_REPEAT
);
123 VG_STATIC
void vg_tex2d_replace_with_error( GLuint
*dest
)
125 u32 hdr_size
= vg_align8(sizeof(struct texture_load_info
));
127 vg_async_item
*call
= vg_async_alloc( hdr_size
);
128 struct texture_load_info
*info
= call
->payload
;
131 info
->flags
= VG_TEX2D_NEAREST
|VG_TEX2D_REPEAT
|VG_TEX2D_NOMIP
;
134 info
->rgba
= const_vg_tex2d_err
;
136 vg_async_dispatch( call
, async_vg_tex2d_upload
);
140 void vg_tex2d_load_qoi_async( const u8
*bytes
, u32 size
,
141 u32 flags
, GLuint
*dest
)
144 qoi_rgba_t index
[64];
146 int px_len
, chunks_len
, px_pos
;
149 u32 channels
= 4; /* TODO */
155 (channels
!= 0 && channels
!= 3 && channels
!= 4) ||
156 size
< QOI_HEADER_SIZE
+ (int)sizeof(qoi_padding
)
158 vg_error( "Error while decoding qoi file: illegal parameters\n" );
159 vg_tex2d_replace_with_error( dest
);
163 header_magic
= qoi_read_32(bytes
, &p
);
164 desc
.width
= qoi_read_32(bytes
, &p
);
165 desc
.height
= qoi_read_32(bytes
, &p
);
166 desc
.channels
= bytes
[p
++];
167 desc
.colorspace
= bytes
[p
++];
170 desc
.width
== 0 || desc
.height
== 0 ||
171 desc
.channels
< 3 || desc
.channels
> 4 ||
172 desc
.colorspace
> 1 ||
173 header_magic
!= QOI_MAGIC
||
174 desc
.height
>= QOI_PIXELS_MAX
/ desc
.width
176 vg_error( "Error while decoding qoi file: invalid file\n" );
177 vg_tex2d_replace_with_error( dest
);
182 channels
= desc
.channels
;
185 px_len
= desc
.width
* desc
.height
* channels
;
187 /* allocate async call
188 * --------------------------
190 u32 hdr_size
= vg_align8(sizeof(struct texture_load_info
)),
191 tex_size
= vg_align8(px_len
);
193 vg_async_item
*call
= vg_async_alloc( hdr_size
+ tex_size
);
194 struct texture_load_info
*info
= call
->payload
;
198 info
->width
= desc
.width
;
199 info
->height
= desc
.height
;
200 info
->rgba
= ((u8
*)call
->payload
) + hdr_size
;
204 * ----------------------------
207 u8
*pixels
= info
->rgba
;
215 chunks_len
= size
- (int)sizeof(qoi_padding
);
216 for (px_pos
= 0; px_pos
< px_len
; px_pos
+= channels
) {
220 else if (p
< chunks_len
) {
223 if (b1
== QOI_OP_RGB
) {
224 px
.rgba
.r
= bytes
[p
++];
225 px
.rgba
.g
= bytes
[p
++];
226 px
.rgba
.b
= bytes
[p
++];
228 else if (b1
== QOI_OP_RGBA
) {
229 px
.rgba
.r
= bytes
[p
++];
230 px
.rgba
.g
= bytes
[p
++];
231 px
.rgba
.b
= bytes
[p
++];
232 px
.rgba
.a
= bytes
[p
++];
234 else if ((b1
& QOI_MASK_2
) == QOI_OP_INDEX
) {
237 else if ((b1
& QOI_MASK_2
) == QOI_OP_DIFF
) {
238 px
.rgba
.r
+= ((b1
>> 4) & 0x03) - 2;
239 px
.rgba
.g
+= ((b1
>> 2) & 0x03) - 2;
240 px
.rgba
.b
+= ( b1
& 0x03) - 2;
242 else if ((b1
& QOI_MASK_2
) == QOI_OP_LUMA
) {
244 int vg
= (b1
& 0x3f) - 32;
245 px
.rgba
.r
+= vg
- 8 + ((b2
>> 4) & 0x0f);
247 px
.rgba
.b
+= vg
- 8 + (b2
& 0x0f);
249 else if ((b1
& QOI_MASK_2
) == QOI_OP_RUN
) {
253 index
[QOI_COLOR_HASH(px
) % 64] = px
;
256 pixels
[px_pos
+ 0] = px
.rgba
.r
;
257 pixels
[px_pos
+ 1] = px
.rgba
.g
;
258 pixels
[px_pos
+ 2] = px
.rgba
.b
;
261 pixels
[px_pos
+ 3] = px
.rgba
.a
;
267 * --------------------------
270 vg_async_dispatch( call
, async_vg_tex2d_upload
);
274 void vg_tex2d_load_qoi_async_file( const char *path
, u32 flags
, GLuint
*dest
)
276 vg_linear_clear( vg_mem
.scratch
);
279 const void *data
= vg_file_read( vg_mem
.scratch
, path
, &size
);
280 vg_tex2d_load_qoi_async( data
, size
, flags
, dest
);
283 #endif /* VG_TEX_H */