1 /*
2  * libtilemcore - Graphing calculator emulation library
3  *
4  * Copyright (C) 2010-2011 Benjamin Moody
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include "tilem.h"
28 
29 /* Scale the input buffer, multiply by F * INCOUNT, and add to the
30    output buffer (which must be an exact multiple of the size of the
31    input buffer.) */
add_scale1d_exact(const byte * restrict in,int incount,unsigned int * restrict out,int outcount,int f)32 static inline void add_scale1d_exact(const byte * restrict in, int incount,
33 				     unsigned int * restrict out,
34 				     int outcount, int f)
35 {
36 	int i, j;
37 
38 	for (i = 0; i < incount; i++) {
39 		for (j = 0; j < outcount / incount; j++) {
40 			*out += *in * f * incount;
41 			out++;
42 		}
43 		in++;
44 	}
45 }
46 
47 /* Scale a 1-dimensional buffer, multiply by F * INCOUNT, and add to
48    the output buffer. */
add_scale1d_smooth(const byte * restrict in,int incount,unsigned int * restrict out,int outcount,int f)49 static inline void add_scale1d_smooth(const byte * restrict in, int incount,
50 				      unsigned int * restrict out,
51 				      int outcount, int f)
52 {
53 	int in_rem, out_rem;
54 	unsigned int outv;
55 	int i;
56 
57 	in_rem = outcount;
58 	out_rem = incount;
59 	outv = 0;
60 	i = outcount;
61 	while (i > 0) {
62 		if (in_rem < out_rem) {
63 			out_rem -= in_rem;
64 			outv += in_rem * *in * f;
65 			in++;
66 			in_rem = outcount;
67 		}
68 		else {
69 			in_rem -= out_rem;
70 			outv += out_rem * *in * f;
71 			*out += outv;
72 			outv = 0;
73 			out++;
74 			out_rem = incount;
75 			i--;
76 		}
77 	}
78 }
79 
80 /* Scale a 2-dimensional buffer, multiply by INWIDTH * INHEIGHT, and
81    store in the output buffer. */
scale2d_smooth(const byte * restrict in,int inwidth,int inheight,int inrowstride,unsigned int * restrict out,int outwidth,int outheight,int outrowstride)82 static void scale2d_smooth(const byte * restrict in,
83 			   int inwidth, int inheight, int inrowstride,
84 			   unsigned int * restrict out,
85 			   int outwidth, int outheight, int outrowstride)
86 {
87 	int in_rem, out_rem;
88 	int i;
89 
90 	memset(out, 0, outrowstride * outheight * sizeof(int));
91 
92 	in_rem = outheight;
93 	out_rem = inheight;
94 	i = outheight;
95 	while (i > 0) {
96 		if (in_rem < out_rem) {
97 			if (in_rem) {
98 				if (outwidth % inwidth)
99 					add_scale1d_smooth(in, inwidth, out,
100 							   outwidth, in_rem);
101 				else
102 					add_scale1d_exact(in, inwidth, out,
103 							  outwidth, in_rem);
104 			}
105 			out_rem -= in_rem;
106 			in += inrowstride;
107 			in_rem = outheight;
108 		}
109 		else {
110 			in_rem -= out_rem;
111 			if (outwidth % inwidth)
112 				add_scale1d_smooth(in, inwidth, out, outwidth,
113 						   out_rem);
114 			else
115 				add_scale1d_exact(in, inwidth, out, outwidth,
116 						  out_rem);
117 			out += outrowstride;
118 			out_rem = inheight;
119 			i--;
120 		}
121 	}
122 }
123 
124 /* Quickly scale a 1-dimensional buffer and store in the output
125    buffer. */
scale1d_fast(const byte * restrict in,int incount,byte * restrict out,int outcount)126 static inline void scale1d_fast(const byte * restrict in, int incount,
127 				byte * restrict out, int outcount)
128 {
129 	int i, e;
130 
131 	e = outcount - incount / 2;
132 	i = outcount;
133 	while (i > 0) {
134 		if (e >= 0) {
135 			*out = *in;
136 			out++;
137 			e -= incount;
138 			i--;
139 		}
140 		else {
141 			e += outcount;
142 			in++;
143 		}
144 	}
145 }
146 
147 /* Quickly scale a 2-dimensional buffer and store in the output
148    buffer. */
scale2d_fast(const byte * restrict in,int inwidth,int inheight,int inrowstride,byte * restrict out,int outwidth,int outheight,int outrowstride)149 static void scale2d_fast(const byte * restrict in,
150 			 int inwidth, int inheight, int inrowstride,
151 			 byte * restrict out,
152 			 int outwidth, int outheight, int outrowstride)
153 {
154 	int i, e;
155 
156 	e = outheight - inheight / 2;
157 	i = outheight;
158 	while (i > 0) {
159 		if (e >= 0) {
160 			scale1d_fast(in, inwidth, out, outwidth);
161 			out += outrowstride;
162 			e -= inheight;
163 			i--;
164 		}
165 		else {
166 			e += outheight;
167 			in += inrowstride;
168 		}
169 	}
170 }
171 
172 /* Determine range of linear pixel values corresponding to a given
173    contrast level. */
get_contrast_settings(unsigned int contrast,int * cbase,int * cfact)174 static void get_contrast_settings(unsigned int contrast,
175                                   int *cbase, int *cfact)
176 {
177 	if (contrast < 32) {
178 		*cbase = 0;
179 		*cfact = contrast * 8;
180 	}
181 	else {
182 		*cbase = (contrast - 32) * 8;
183 		*cfact = 255 - *cbase;
184 	}
185 }
186 
187 #define GETSCALEBUF(ttt, www, hhh) \
188 	((ttt *) alloc_scalebuf(buf, (www) * (hhh) * sizeof(ttt)))
189 
alloc_scalebuf(TilemLCDBuffer * buf,unsigned int size)190 static void* alloc_scalebuf(TilemLCDBuffer *buf, unsigned int size)
191 {
192 	if (TILEM_UNLIKELY(size > buf->tmpbufsize)) {
193 		buf->tmpbufsize = size;
194 		tilem_free(buf->tmpbuf);
195 		buf->tmpbuf = tilem_malloc_atomic(size);
196 	}
197 
198 	return buf->tmpbuf;
199 }
200 
tilem_draw_lcd_image_indexed(TilemLCDBuffer * restrict buf,byte * restrict buffer,int imgwidth,int imgheight,int rowstride,int scaletype)201 void tilem_draw_lcd_image_indexed(TilemLCDBuffer * restrict buf,
202                                   byte * restrict buffer,
203                                   int imgwidth, int imgheight,
204                                   int rowstride, int scaletype)
205 {
206 	int dwidth = buf->width;
207 	int dheight = buf->height;
208 	int i, j, v;
209 	unsigned int * restrict ibuf;
210 	int cbase, cfact;
211 	byte cindex[129];
212 
213 	if (dwidth == 0 || dheight == 0 || buf->contrast == 0) {
214 		for (i = 0; i < imgheight; i++) {
215 			for (j = 0; j < imgwidth; j++)
216 				buffer[j] = 0;
217 			buffer += rowstride;
218 		}
219 		return;
220 	}
221 
222 	get_contrast_settings(buf->contrast, &cbase, &cfact);
223 
224 	for (i = 0; i <= 128; i++)
225 		cindex[i] = ((i * cfact) >> 7) + cbase;
226 
227 	if (scaletype == TILEM_SCALE_FAST
228 	    || (imgwidth % dwidth == 0 && imgheight % dheight == 0)) {
229 		scale2d_fast(buf->data, dwidth, dheight, buf->rowstride,
230 		             buffer, imgwidth, imgheight, rowstride);
231 
232 		for (i = 0; i < imgwidth * imgheight; i++)
233 			buffer[i] = cindex[buffer[i]];
234 	}
235 	else {
236 		ibuf = GETSCALEBUF(unsigned int, imgwidth, imgheight);
237 
238 		scale2d_smooth(buf->data, dwidth, dheight, buf->rowstride,
239 		               ibuf, imgwidth, imgheight, imgwidth);
240 
241 		for (i = 0; i < imgheight; i++) {
242 			for (j = 0; j < imgwidth; j++) {
243 				v = ibuf[j] / (dwidth * dheight);
244 				buffer[j] = cindex[v];
245 			}
246 			ibuf += imgwidth;
247 			buffer += rowstride;
248 		}
249 	}
250 }
251 
tilem_draw_lcd_image_rgb(TilemLCDBuffer * restrict buf,byte * restrict buffer,int imgwidth,int imgheight,int rowstride,int pixbytes,const dword * restrict palette,int scaletype)252 void tilem_draw_lcd_image_rgb(TilemLCDBuffer * restrict buf,
253                               byte * restrict buffer,
254                               int imgwidth, int imgheight, int rowstride,
255                               int pixbytes, const dword * restrict palette,
256                               int scaletype)
257 {
258 	int dwidth = buf->width;
259 	int dheight = buf->height;
260 	int i, j, v;
261 	int padbytes = rowstride - (imgwidth * pixbytes);
262 	byte * restrict bbuf;
263 	unsigned int * restrict ibuf;
264 	int cbase, cfact;
265 	dword cpalette[129];
266 
267 	if (dwidth == 0 || dheight == 0 || buf->contrast == 0) {
268 		for (i = 0; i < imgheight; i++) {
269 			for (j = 0; j < imgwidth; j++) {
270 				buffer[0] = palette[0] >> 16;
271 				buffer[1] = palette[0] >> 8;
272 				buffer[2] = palette[0];
273 				buffer += pixbytes;
274 			}
275 			buffer += padbytes;
276 		}
277 		return;
278 	}
279 
280 	get_contrast_settings(buf->contrast, &cbase, &cfact);
281 
282 	for (i = 0; i <= 128; i++) {
283 		v = ((i * cfact) >> 7) + cbase;
284 		cpalette[i] = palette[v];
285 	}
286 
287 	if (scaletype == TILEM_SCALE_FAST
288 	    || (imgwidth % dwidth == 0 && imgheight % dheight == 0)) {
289 		bbuf = GETSCALEBUF(byte, imgwidth, imgheight);
290 
291 		scale2d_fast(buf->data, dwidth, dheight, buf->rowstride,
292 		             bbuf, imgwidth, imgheight, imgwidth);
293 
294 		for (i = 0; i < imgheight; i++) {
295 			for (j = 0; j < imgwidth; j++) {
296 				v = bbuf[j];
297 				buffer[0] = cpalette[v] >> 16;
298 				buffer[1] = cpalette[v] >> 8;
299 				buffer[2] = cpalette[v];
300 				buffer += pixbytes;
301 			}
302 			bbuf += imgwidth;
303 			buffer += padbytes;
304 		}
305 	}
306 	else {
307 		ibuf = GETSCALEBUF(unsigned int, imgwidth, imgheight);
308 
309 		scale2d_smooth(buf->data, dwidth, dheight, buf->rowstride,
310 			       ibuf, imgwidth, imgheight, imgwidth);
311 
312 		for (i = 0; i < imgheight; i++) {
313 			for (j = 0; j < imgwidth; j++) {
314 				v = (ibuf[j] / (dwidth * dheight));
315 				buffer[0] = cpalette[v] >> 16;
316 				buffer[1] = cpalette[v] >> 8;
317 				buffer[2] = cpalette[v];
318 				buffer += pixbytes;
319 			}
320 			ibuf += imgwidth;
321 			buffer += padbytes;
322 		}
323 	}
324 }
325