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.
23 /* pal emulation for 32-bit rgba images. */
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
32 g=(y-0.299r-0.114b)/0.587=...=y-0.509v-0.194u
34 we multiply every floating point value with 256:
44 #define CROSSTALK_SIZE 16
48 /* we use values *256 here for fixed point math, so 8bits is not enough */
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
[] = {
62 /* for left edge of image. */
63 static const int lconv_left
[] = {
66 /* for right edge of image. */
67 static const int lconv_right
[] = {
70 static int ldiv
=0, ldiv_left
=0, ldiv_right
=0;
71 static gboolean first_run_done
=FALSE
;
75 if (!first_run_done
) {
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
++) {
87 YUV n
; /* new value of this pixel in yuv */
89 /* turnaround coordinates */
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
;
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
;
99 n
.y
=(yuv
[y
][xm
].y
*lconv
[0]+yuv
[y
][x
].y
*lconv
[1]+yuv
[y
][xp
].y
*lconv
[2])/ldiv
;
105 for (y
=0; y
<height
; y
++)
106 for (x
=0; x
<width
; x
++)
107 yuv
[y
][x
].y
=work
[y
][x
].y
;
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
[] = {
120 static gboolean first_run_done
=FALSE
;
124 if (!first_run_done
) {
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
;
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
;
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
;
171 for (i
=0; i
<CROSSTALK_SIZE
; i
++) {
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
);
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
;
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) */
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) */
212 scanline_shade(YUV
**yuv
, YUV
**work
, int width
, int height
)
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
++)
226 for (x
=0; x
<width
; x
++)
227 yuv
[y
][x
].y
=yuv
[y
][x
].y
*shade
/256;
232 clamp(int value
, int min
, int max
)
244 gd_pal_emulate_raw(gpointer pixels
, int width
, int height
, int pitch
, int rshift
, int gshift
, int bshift
, int ashift
)
247 guint8
* srcpix
= (guint8
*)pixels
;
248 YUV
**yuv
, **alpha
; /* alpha is not really yuv, only y will be used. luma blur touches only y. */
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
);
263 for (y
=0; y
<height
; y
++) {
264 guint32
*row
=(guint32
*)(srcpix
+y
*pitch
);
266 for (x
=0; x
<width
; x
++) {
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
++) {
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
);
310 for (y
=0; y
<height
; y
++)
313 for (y
=0; y
<height
; y
++)
316 for (y
=0; y
<height
; y
++)
320 #undef CROSSTALK_SIZE
326 /* somewhat optimized implementation of the Scale2x algorithm. */
327 /* http://scale2x.sourceforge.net */
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
;
334 for (y
=0; y
<height
; ++y
) {
338 yp
=MIN(y
+1, height
-1);
340 for (x
=0; x
<width
; ++x
) {
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
) {
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
;
375 gd_scale3x_raw(guint8
*srcpix
, int width
, int height
, int srcpitch
, guint8
*dstpix
, int dstpitch
)
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
) {
382 int ny
=y
*3; /* new coordinate */
385 yp
=MIN(y
+1, height
-1);
387 for (x
=0; x
<width
; ++ x
) {
389 int nx
=x
*3; /* new coordinate */
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
) {
406 E1
= (D
== B
&& E
!= C
) || (B
== F
&& E
!= A
) ? B
: E
;
408 E3
= (D
== B
&& E
!= G
) || (D
== H
&& E
!= A
) ? D
: E
;
410 E5
= (B
== F
&& E
!= I
) || (H
== F
&& E
!= C
) ? F
: E
;
412 E7
= (D
== H
&& E
!= I
) || (H
== F
&& E
!= G
) ? H
: 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
;