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