1 /*
2 * Copyright 2013-2016, Björn Ståhl
3 * License: 3-Clause BSD, see COPYING file in arcan source repository.
4 * Reference: http://arcan-fe.com
5 */
6
7 #include <stdlib.h>
8 #include <unistd.h>
9 #include <stdint.h>
10 #include <assert.h>
11 #include <stdio.h>
12 #include <inttypes.h>
13 #include <stdbool.h>
14 #include <string.h>
15
16 #include "arcan_math.h"
17 #include "arcan_general.h"
18 #include "arcan_img.h"
19 #include "arcan_video.h"
20
21 #define STBI_MALLOC(sz) (arcan_alloc_mem(sz, ARCAN_MEM_VBUFFER, \
22 ARCAN_MEM_NONFATAL, ARCAN_MEMALIGN_PAGE))
23
24 #define STBI_FREE(ptr) (arcan_mem_free(ptr))
25 #define STBI_REALLOC(p,sz) realloc(p,sz)
26
27 #define STBI_ONLY_PNG
28 #define STBI_ONLY_JPEG
29 #define STBI_IMAGE_STATIC
30 #define STBI_NO_FAILURE_STRINGS
31 #define STB_IMAGE_WRITE_STATIC
32 #define STB_IMAGE_WRITE_IMPLEMENTATION
33 #define STB_IMAGE_IMPLEMENTATION
34
35 #include "external/stb_image.h"
36 #include "external/stb_image_write.h"
37
38 struct png_readstr
39 {
40 char* inbuf;
41 off_t inbuf_ofs;
42 size_t inbuf_sz;
43 };
44
arcan_img_outpng(FILE * dst,av_pixel * inbuf,size_t inw,size_t inh,bool vflip)45 arcan_errc arcan_img_outpng(FILE* dst,
46 av_pixel* inbuf, size_t inw, size_t inh, bool vflip)
47 {
48 int outln = 0;
49 bool dynout = false;
50 unsigned char* outbuf = (unsigned char*) inbuf;
51
52 /* repack if the platform defines a different format than LE RGBA */
53 if (sizeof(av_pixel) != 4 || RGBA(0xff, 0xaa, 0x77, 0x55) != 0x5577aaff){
54 av_pixel* work = inbuf;
55
56 uint32_t* repack = arcan_alloc_mem(inw * inh * 4, ARCAN_MEM_VBUFFER,
57 ARCAN_MEM_TEMPORARY | ARCAN_MEM_NONFATAL, ARCAN_MEMALIGN_PAGE);
58 if (!repack)
59 return ARCAN_ERRC_OUT_OF_SPACE;
60 outbuf = (unsigned char*) repack;
61
62 #define RPACK(r, g, b, a)( ((uint32_t)(a) << 24) | ((uint32_t) (b) << 16) |\
63 ((uint32_t) (g) << 8) | ((uint32_t) (r)) )
64 if (vflip){
65 for (ssize_t y = inh-1; y >= 0; y--)
66 for (size_t x = 0; x < inw; x++){
67 uint8_t r, g, b, a;
68 RGBA_DECOMP(work[y*inw+x], &r, &g, &b, &a);
69 *repack++ = RPACK(r, g, b, a);
70 }
71 }
72 else{
73 size_t count = inw * inh;
74 while(count--){
75 uint8_t r, g, b, a;
76 RGBA_DECOMP(*work++, &r, &g, &b, &a);
77 *repack++ = RPACK(r, g, b, a);
78 }
79 }
80 #undef RPACK
81 dynout = true;
82 }
83 else if (vflip){
84 size_t stride = inw * 4;
85 outbuf = arcan_alloc_mem(stride * inh, ARCAN_MEM_VBUFFER,
86 ARCAN_MEM_TEMPORARY | ARCAN_MEM_NONFATAL, ARCAN_MEMALIGN_PAGE);
87
88 if (!outbuf)
89 return ARCAN_ERRC_OUT_OF_SPACE;
90
91 for (ssize_t row = inh-1, step = 0; row >= 0; row--, step++)
92 memcpy(&outbuf[step * stride], &inbuf[row*inw], stride);
93 }
94
95 unsigned char* png = stbi_write_png_to_mem(
96 (unsigned char*) outbuf, 0, inw, inh, 4, &outln);
97
98 fwrite(png, 1, outln, dst);
99 arcan_mem_free(png);
100
101 if (vflip || dynout)
102 arcan_mem_free(outbuf);
103
104 return ARCAN_OK;
105 }
106
arcan_img_repack(uint32_t * inbuf,size_t inw,size_t inh)107 av_pixel* arcan_img_repack(uint32_t* inbuf, size_t inw, size_t inh)
108 {
109 if (sizeof(av_pixel) == 4 && RGBA(0x00, 0x00, 0xff, 0x00) == 0x00ff0000)
110 return (av_pixel*) inbuf;
111
112 av_pixel* imgbuf = arcan_alloc_mem(sizeof(av_pixel) * inw * inh,
113 ARCAN_MEM_VBUFFER | ARCAN_MEM_NONFATAL, 0, ARCAN_MEMALIGN_PAGE);
114
115 if (!imgbuf)
116 goto done;
117
118 uint32_t* in_work = inbuf;
119 av_pixel* work = imgbuf;
120 for (size_t count = inw * inh; count > 0; count--){
121 uint32_t val = *in_work++;
122 *work++ = RGBA(
123 ((val & 0x000000ff) >> 0),
124 ((val & 0x0000ff00) >> 8),
125 ((val & 0x00ff0000) >> 16),
126 ((val & 0xff000000) >> 24)
127 );
128 }
129
130 done:
131 arcan_mem_free(inbuf);
132 return imgbuf;
133 }
134
arcan_pkm_raw(const uint8_t * inbuf,size_t inbuf_sz,uint32_t ** outbuf,size_t * outw,size_t * outh,struct arcan_img_meta * meta)135 arcan_errc arcan_pkm_raw(const uint8_t* inbuf, size_t inbuf_sz,
136 uint32_t** outbuf, size_t* outw, size_t* outh, struct arcan_img_meta* meta)
137 {
138 #ifdef ETC1_SUPPORT
139 /* extract header fields */
140 int pwidth = (inbuf[ 8] << 8) | inbuf[ 9];
141 int pheight = (inbuf[10] << 8) | inbuf[11];
142 int width = (inbuf[12] << 8) | inbuf[13];
143 int height = (inbuf[14] << 8) | inbuf[15];
144
145 /* strip header */
146 *outbuf = arcan_mem_alloc(inbuf_sz - 16,
147 ARCAN_MEM_VBUFFER, ARCAN_MEMALIGN_PAGE);
148
149 memcpy(*outbuf, inbuf + 16, inbuf_sz - 16);
150 meta->compressed = true;
151 meta->pwidth = pwidth;
152 meta->pheight = pheight;
153 meta->c_size = (pwidth * pheight) >> 1;
154 *outw = width;
155 *outh = height;
156
157 return ARCAN_OK;
158 #else
159 return ARCAN_ERRC_UNSUPPORTED_FORMAT;
160 #endif
161 }
162
163 /*
164 struct dds_data_fmt
165 {
166 GLsizei width;
167 GLsizei height;
168 GLint components;
169 GLenum format;
170 int numMipMaps;
171 GLubyte *pixels;
172 };
173 */
174
arcan_dds_raw(const uint8_t * inbuf,size_t inbuf_sz,uint32_t ** outbuf,size_t * outw,size_t * outh,struct arcan_img_meta * meta)175 arcan_errc arcan_dds_raw(const uint8_t* inbuf, size_t inbuf_sz,
176 uint32_t** outbuf, size_t* outw, size_t* outh, struct arcan_img_meta* meta)
177 {
178 #ifdef DDS_SUPPORT
179 struct dds_dsta_fmt* dds_img_data;
180 DDSurfacedesc2 ddsd;
181
182 strncmp(inbuf, "DDS ", 4) == 0;
183
184 /* read DDSD */
185 /* alloc outbuf as DDSIMAGEDATA */
186 /* check FourCC from DDSD for supported formats;
187 * FOURCC_DXT1,3,5 (factor 2, 4, 4 as CR)
188 * something weird with dwLinearSize?
189 * check mipmapcount (linearsize * factor from fourCC)
190 * now we have raw-data
191 * FOURCC DXT1 only gives RGB, no alpha channel
192 * DXT1 has a block size of 8, otherwise 16
193 * foreach mipmap level;
194 * (1, 1 dimension if 0 0,
195 * glCompressedTexImage(level, formatv, cw, ch, 0,
196 * (cw+3)/4 * (ch+3)/4 * blocksize)
197 * slide buffer offset with size
198 * shift down width / height
199 */
200 #else
201 return ARCAN_ERRC_UNSUPPORTED_FORMAT;
202 #endif
203 }
204
arcan_img_init()205 void arcan_img_init()
206 {
207 static bool initialized;
208 if (initialized)
209 return;
210
211 initialized = true;
212 }
213
arcan_img_decode(const char * hint,char * inbuf,size_t inbuf_sz,uint32_t ** outbuf,size_t * outw,size_t * outh,struct arcan_img_meta * meta,bool vflip)214 arcan_errc arcan_img_decode(const char* hint, char* inbuf, size_t inbuf_sz,
215 uint32_t** outbuf, size_t* outw, size_t* outh,
216 struct arcan_img_meta* meta, bool vflip)
217 {
218 arcan_errc rv = ARCAN_ERRC_BAD_RESOURCE;
219 int len = strlen(hint);
220
221 if (len >= 3){
222 if (strcasecmp(hint + (len - 3), "PNG") == 0 ||
223 strcasecmp(hint + (len - 3), "JPG") == 0 ||
224 (len >= 4 && strcasecmp(hint + (len - 4), "JPEG") == 0)
225 ){
226 int outf;
227 int w, h;
228 /* three things to note here,
229 * 1. PNG Z-Lib state is not thread safe in current stbi-, neither is the
230 * flip-on-load toggle. This should be fixed upstream - now we hang on
231 * to the __thread gcc/clang extension.
232 * 2. stbi uses arcan_alloc_mem with arguments that guarantee alignment
233 * 3. this does not handle hi/norm/lo- quality re-packing or anti-repack
234 * protection, but rather assumes that the buffer contents has the right
235 * format, should be adressed when we go through all functions to try push
236 * for 10-bit and 16-bit support.
237 */
238 stbi_set_flip_vertically_on_load(vflip);
239 uint32_t* buf = (uint32_t*) stbi_load_from_memory(
240 (stbi_uc const*) inbuf, inbuf_sz, &w, &h, &outf, 4);
241
242 if (buf){
243 *outbuf = buf;
244 *outw = w;
245 *outh = h;
246 return ARCAN_OK;
247 }
248 }
249 else if (strcasecmp(hint + (len - 3), "PKM") == 0){
250 return arcan_pkm_raw((uint8_t*)inbuf, inbuf_sz,
251 outbuf, outw, outh, meta);
252 }
253 else if (strcasecmp(hint + (len - 3), "DDS") == 0){
254 return arcan_dds_raw((uint8_t*)inbuf, inbuf_sz,
255 outbuf, outw, outh, meta);
256 }
257 }
258
259 return rv;
260 }
261