20100212
[gdash.git] / src / gfxutil.c
blob3c401de2c450ab70b3e422ac3fd1d2c4b71e72d1
1 /*
2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 #include <glib.h>
17 #include <math.h>
18 #include <stdlib.h>
19 #include "settings.h"
20 #include "config.h"
21 #include "gfxutil.h"
23 /* pal emulation for 32-bit rgba images. */
25 /* used:
26 y=0.299r+0.587g+0.114b
27 u=b-y=-0.299r-0.587g+0.886b
28 v=r-y=0.701r-0.587g-0.114b
30 r=(r-y)+y=v+y
31 b=(b-y)+y=u+y
32 g=(y-0.299r-0.114b)/0.587=...=y-0.509v-0.194u
34 we multiply every floating point value with 256:
36 y=77r+150g+29b
37 u=g-y=-77r-150g+227b
38 v=r-y=179r-150g-29b
40 256*r=v+y
41 65536*g=256y-130v-50u
42 256*b=u+y
44 #define CROSSTALK_SIZE 16
47 typedef struct _yuv {
48 /* we use values *256 here for fixed point math, so 8bits is not enough */
49 int y, u, v;
50 } YUV;
52 static void
53 luma_blur (YUV **yuv, YUV **work, int width, int height)
55 /* convolution "matrices" could be 5 numbers, ie. x-2, x-1, x, x+1, x+2... */
56 /* but the output already has problems for x-1 and x+1. as the game only
57 pal_emus cells, not complete screens */
58 /* convolution "matrix" for luminance */
59 static const int lconv[] = {
60 1, 3, 1, /* 2 4 2 */
62 /* for left edge of image. */
63 static const int lconv_left[] = {
64 1, 5, 3,
66 /* for right edge of image. */
67 static const int lconv_right[] = {
68 3, 5, 1,
70 static int ldiv=0, ldiv_left=0, ldiv_right=0;
71 static gboolean first_run_done=FALSE;
73 int y, x;
75 if (!first_run_done) {
76 first_run_done=TRUE;
78 ldiv=lconv[0]+lconv[1]+lconv[2];
79 ldiv_left=lconv_left[0]+lconv_left[1]+lconv_left[2];
80 ldiv_right=lconv_right[0]+lconv_right[1]+lconv_right[2];
83 /* apply convolution matrix */
84 for (y=0; y<height; y++) {
85 for (x=0; x<width; x++) {
86 int xm, xp;
87 YUV n; /* new value of this pixel in yuv */
89 /* turnaround coordinates */
90 xm=x==0?width-1:x-1;
91 xp=x==width-1?0:x+1;
93 /* luma blur */
94 if (x==0)
95 n.y=(yuv[y][xm].y*lconv_left[0]+yuv[y][x].y*lconv_left[1]+yuv[y][xp].y*lconv_left[2])/ldiv_left;
96 else if (x==width-1)
97 n.y=(yuv[y][xm].y*lconv_right[0]+yuv[y][x].y*lconv_right[1]+yuv[y][xp].y*lconv_right[2])/ldiv_right;
98 else
99 n.y=(yuv[y][xm].y*lconv[0]+yuv[y][x].y*lconv[1]+yuv[y][xp].y*lconv[2])/ldiv;
101 work[y][x].y=n.y;
105 for (y=0; y<height; y++)
106 for (x=0; x<width; x++)
107 yuv[y][x].y=work[y][x].y;
111 static void
112 chroma_blur(YUV **yuv, YUV **work, int width, int height)
114 /* convolution "matrix" for chrominance */
115 /* x-2, x-1, x, x+1, x+2 */
116 static const int cconv[] = {
117 1, 1, 1, 1, 1,
119 static int cdiv;
120 static gboolean first_run_done=FALSE;
122 int y, x;
124 if (!first_run_done) {
125 first_run_done=TRUE;
127 /* calculate divisors from convolution matrices */
128 cdiv=cconv[0]+cconv[1]+cconv[2]+cconv[3]+cconv[4];
131 /* apply convolution matrix */
132 for (y=0; y<height; y++) {
133 for (x=0; x<width; x++) {
134 int xm, xm2, xp, xp2;
136 /* turnaround coordinates */
137 xm2=(x-2+width)%width;
138 xm=(x-1+width)%width;
139 xp=(x+1)%width;
140 xp2=(x+1)%width;
142 /* chroma blur */
143 work[y][x].u=(yuv[y][xm2].u*cconv[0]+yuv[y][xm].u*cconv[1]+yuv[y][x].u*cconv[2]+yuv[y][xp].u*cconv[3]+yuv[y][xp2].u*cconv[4])/cdiv;
144 work[y][x].v=(yuv[y][xm2].v*cconv[0]+yuv[y][xm].v*cconv[1]+yuv[y][x].v*cconv[2]+yuv[y][xp].v*cconv[3]+yuv[y][xp2].v*cconv[4])/cdiv;
148 for (y=0; y<height; y++)
149 for (x=0; x<width; x++) {
150 yuv[y][x].u=work[y][x].u;
151 yuv[y][x].v=work[y][x].v;
155 static void
156 chroma_crosstalk_to_luma(YUV **yuv, YUV **work, int width, int height)
158 /* arrays to store things */
159 static int crosstalk_sin[CROSSTALK_SIZE];
160 static int crosstalk_cos[CROSSTALK_SIZE];
161 /* crosstalk will be amplitude/div; we use these two to have integer arithmetics */
162 const int crosstalk_amplitude=192;
163 const int crosstalk_div=256;
164 const int crosstalk_div_edge=384;
165 static gboolean crosstalk_calculated=FALSE;
167 if (!crosstalk_calculated) {
168 crosstalk_calculated=TRUE;
169 int i;
171 for (i=0; i<CROSSTALK_SIZE; i++) {
172 double f;
174 f=(double)i/CROSSTALK_SIZE*2.0*G_PI*2;
175 crosstalk_sin[i]=crosstalk_amplitude*sin(f);
176 crosstalk_cos[i]=crosstalk_amplitude*cos(f);
180 int y, x;
182 /* apply edge detection matrix */
183 for (y=0; y<height; y++) {
184 for (x=0; x<width; x++) {
185 const int conv[]={-1, 1, 0, 0, 0,};
186 int xm, xm2, xp, xp2;
188 /* turnaround coordinates */
189 xm2=(x-2+width)%width;
190 xm=(x-1+width)%width;
191 xp=(x+1)%width;
192 xp2=(x+2)%width;
194 /* edge detect */
195 work[y][x].u=yuv[y][xm2].u*conv[0]+yuv[y][xm].u*conv[1]+yuv[y][x].u*conv[2]+yuv[y][xp].u*conv[3]+yuv[y][xp2].u*conv[4];
196 work[y][x].v=yuv[y][xm2].v*conv[0]+yuv[y][xm].v*conv[1]+yuv[y][x].v*conv[2]+yuv[y][xp].v*conv[3]+yuv[y][xp2].v*conv[4];
200 for (y=0; y<height; y++)
201 for (x=0; x<width; x++) {
202 int div=(x==0 || x==width-1)?crosstalk_div_edge:crosstalk_div;
203 if (y/2%2==1) /* y/2: for interlacing. */
204 yuv[y][x].y+=(crosstalk_sin[x%CROSSTALK_SIZE]*work[y][x].u-crosstalk_cos[x%CROSSTALK_SIZE]*work[y][x].v)/div; /* odd lines (/2) */
205 else
206 yuv[y][x].y+=(crosstalk_sin[x%CROSSTALK_SIZE]*work[y][x].u+crosstalk_cos[x%CROSSTALK_SIZE]*work[y][x].v)/div; /* even lines (/2) */
211 static void
212 scanline_shade(YUV **yuv, YUV **work, int width, int height)
214 int y, x;
215 int shade;
217 if (gd_pal_emu_scanline_shade<0)
218 gd_pal_emu_scanline_shade=0;
219 if (gd_pal_emu_scanline_shade>100)
220 gd_pal_emu_scanline_shade=100;
221 shade=gd_pal_emu_scanline_shade*256/100;
223 /* apply shade for every second row */
224 for (y=0; y<height; y++)
225 if (y%2==1)
226 for (x=0; x<width; x++)
227 yuv[y][x].y=yuv[y][x].y*shade/256;
231 static inline int
232 clamp(int value, int min, int max)
234 if (value<min)
235 return min;
236 if (value>max)
237 return max;
238 return value;
243 void
244 gd_pal_emulate_raw(gpointer pixels, int width, int height, int pitch, int rshift, int gshift, int bshift, int ashift)
246 int y, x;
247 guint8* srcpix = (guint8*)pixels;
248 YUV **yuv, **alpha; /* alpha is not really yuv, only y will be used. luma blur touches only y. */
249 YUV **work;
251 /* memory for yuv images */
252 yuv=g_new(YUV *, height);
253 for (y=0; y<height; y++)
254 yuv[y]=g_new(YUV, width);
255 work=g_new(YUV *, height);
256 for (y=0; y<height; y++)
257 work[y]=g_new(YUV, width);
258 alpha=g_new(YUV *, height);
259 for (y=0; y<height; y++)
260 alpha[y]=g_new(YUV, width);
262 /* convert to yuv */
263 for (y=0; y<height; y++) {
264 guint32 *row=(guint32 *)(srcpix+y*pitch);
266 for (x=0; x<width; x++) {
267 int r, g, b;
269 r=(row[x]>>rshift)&0xff;
270 g=(row[x]>>gshift)&0xff;
271 b=(row[x]>>bshift)&0xff;
273 /* now y, u, v will contain values * 256 */
274 yuv[y][x].y= 77*r+150*g+ 29*b;
275 yuv[y][x].u=-37*r -74*g+111*b;
276 yuv[y][x].v=157*r-131*g -26*b;
278 /* alpha is copied as is, and is not *256 */
279 alpha[y][x].y=(row[x]>>ashift)&0xff;
283 /* we give them an array to "work" in, so that is not free()d and malloc() four times */
284 luma_blur(yuv, work, width, height);
285 chroma_blur(yuv, work, width, height);
286 chroma_crosstalk_to_luma(yuv, work, width, height);
287 scanline_shade(yuv, work, width, height);
289 luma_blur(alpha, work, width, height);
291 /* convert back to rgb */
292 for (y=0; y<height; y++) {
293 guint32 *row=(guint32 *)(srcpix+y*pitch);
295 for (x=0; x<width; x++) {
296 int r, g, b;
298 /* back to rgb */
299 r=clamp((256*yuv[y][x].y +292*yuv[y][x].v+32768)/65536, 0, 255);
300 g=clamp((256*yuv[y][x].y-101*yuv[y][x].u-149*yuv[y][x].v+32768)/65536, 0, 255);
301 b=clamp((256*yuv[y][x].y+519*yuv[y][x].u +32768)/65536, 0, 255);
303 /* alpha channel is preserved, others are converted back from yuv */
304 row[x]=(alpha[y][x].y<<ashift) | (r<<rshift) | (g<<gshift) | (b<<bshift);
309 /* free array */
310 for (y=0; y<height; y++)
311 g_free(yuv[y]);
312 g_free(yuv);
313 for (y=0; y<height; y++)
314 g_free(alpha[y]);
315 g_free(alpha);
316 for (y=0; y<height; y++)
317 g_free(work[y]);
318 g_free(work);
320 #undef CROSSTALK_SIZE
326 /* somewhat optimized implementation of the Scale2x algorithm. */
327 /* http://scale2x.sourceforge.net */
328 void
329 gd_scale2x_raw(guint8 *srcpix, int width, int height, int srcpitch, guint8 *dstpix, int dstpitch)
331 guint32 E0, E1, E2, E3, B, D, E, F, H;
332 int y, x;
334 for (y=0; y<height; ++y) {
335 int ym, yp;
337 ym=MAX(y-1, 0);
338 yp=MIN(y+1, height-1);
340 for (x=0; x<width; ++x) {
341 int xm, xp;
343 xm=MAX(x-1, 0);
344 xp=MIN(x+1, width-1);
346 B = *(guint32*)(srcpix + (ym*srcpitch) + (4*x));
347 D = *(guint32*)(srcpix + (y*srcpitch) + (4*xm));
348 E = *(guint32*)(srcpix + (y*srcpitch) + (4*x));
349 F = *(guint32*)(srcpix + (y*srcpitch) + (4*xp));
350 H = *(guint32*)(srcpix + (yp*srcpitch) + (4*x));
352 if (B != H && D != F) {
353 E0 = D == B ? D : E;
354 E1 = B == F ? F : E;
355 E2 = D == H ? D : E;
356 E3 = H == F ? F : E;
357 } else {
358 E0 = E;
359 E1 = E;
360 E2 = E;
361 E3 = E;
364 *(guint32*)(dstpix + y*2*dstpitch + x*2*4) = E0;
365 *(guint32*)(dstpix + y*2*dstpitch + (x*2+1)*4) = E1;
366 *(guint32*)(dstpix + (y*2+1)*dstpitch + x*2*4) = E2;
367 *(guint32*)(dstpix + (y*2+1)*dstpitch + (x*2+1)*4) = E3;
374 void
375 gd_scale3x_raw(guint8 *srcpix, int width, int height, int srcpitch, guint8 *dstpix, int dstpitch)
377 int y, x;
378 guint32 E0, E1, E2, E3, E4, E5, E6, E7, E8, A, B, C, D, E, F, G, H, I;
380 for (y=0; y<height; ++y) {
381 int ym, yp;
382 int ny=y*3; /* new coordinate */
384 ym=MAX(y-1, 0);
385 yp=MIN(y+1, height-1);
387 for (x=0; x<width; ++ x) {
388 int xm, xp;
389 int nx=x*3; /* new coordinate */
391 xm=MAX(x-1, 0);
392 xp=MIN(x+1, width-1);
394 A = *(guint32*)(srcpix + (ym*srcpitch + 4*xm));
395 B = *(guint32*)(srcpix + (ym*srcpitch + 4*x));
396 C = *(guint32*)(srcpix + (ym*srcpitch + 4*xp));
397 D = *(guint32*)(srcpix + (y*srcpitch + 4*xm));
398 E = *(guint32*)(srcpix + (y*srcpitch + 4*x));
399 F = *(guint32*)(srcpix + (y*srcpitch + 4*xp));
400 G = *(guint32*)(srcpix + (yp*srcpitch + 4*xm));
401 H = *(guint32*)(srcpix + (yp*srcpitch + 4*x));
402 I = *(guint32*)(srcpix + (yp*srcpitch + 4*xp));
404 if (B != H && D != F) {
405 E0 = D == B ? D : E;
406 E1 = (D == B && E != C) || (B == F && E != A) ? B : E;
407 E2 = B == F ? F : E;
408 E3 = (D == B && E != G) || (D == H && E != A) ? D : E;
409 E4 = E;
410 E5 = (B == F && E != I) || (H == F && E != C) ? F : E;
411 E6 = D == H ? D : E;
412 E7 = (D == H && E != I) || (H == F && E != G) ? H : E;
413 E8 = H == F ? F : E;
414 } else {
415 E0 = E;
416 E1 = E;
417 E2 = E;
418 E3 = E;
419 E4 = E;
420 E5 = E;
421 E6 = E;
422 E7 = E;
423 E8 = E;
426 *(guint32*)(dstpix + ny*dstpitch + nx*4) = E0;
427 *(guint32*)(dstpix + ny*dstpitch + (nx+1)*4) = E1;
428 *(guint32*)(dstpix + ny*dstpitch + (nx+2)*4) = E2;
429 *(guint32*)(dstpix + (ny+1)*dstpitch + nx*4) = E3;
430 *(guint32*)(dstpix + (ny+1)*dstpitch + (nx+1)*4) = E4;
431 *(guint32*)(dstpix + (ny+1)*dstpitch + (nx+2)*4) = E5;
432 *(guint32*)(dstpix + (ny+2)*dstpitch + nx*4) = E6;
433 *(guint32*)(dstpix + (ny+2)*dstpitch + (nx+1)*4) = E7;
434 *(guint32*)(dstpix + (ny+2)*dstpitch + (nx+2)*4) = E8;