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