1 /*
2  * render2x2pal.c - 2x2 PAL renderers
3  *
4  * Written by
5  *  John Selck <graham@cruise.de>
6  *
7  * This file is part of VICE, the Versatile Commodore Emulator.
8  * See README for copyright notice.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307  USA.
24  *
25  */
26 
27 #include "vice.h"
28 
29 #include <stdio.h>
30 
31 #include "render2x2.h"
32 #include "render2x2pal.h"
33 #include "types.h"
34 #include "video-color.h"
35 
36 /*
37     YUV to RGB
38 
39     R = Y + V
40     G = Y - (0.1953 * U + 0.5078 * V)
41     B = Y + U
42 */
43 static inline
yuv_to_rgb(int32_t y,int32_t u,int32_t v,int16_t * red,int16_t * grn,int16_t * blu)44 void yuv_to_rgb(int32_t y, int32_t u, int32_t v, int16_t *red, int16_t *grn, int16_t *blu)
45 {
46 #ifdef _MSC_VER
47 # pragma warning( push )
48 # pragma warning( disable: 4244 )
49 #endif
50 
51     *red = (y + v) >> 16;
52     *blu = (y + u) >> 16;
53     *grn = (y - ((50 * u + 130 * v) >> 8)) >> 16;
54 
55 #ifdef _MSC_VER
56 # pragma warning( pop )
57 #endif
58 }
59 
60 /* Often required function that stores gamma-corrected pixel to current line,
61  * averages the current rgb with the contents of previous non-scanline-line,
62  * stores the gamma-corrected scanline, and updates the prevline rgb buffer.
63  * The variants 4, 3, 2 refer to pixel width of output. */
64 
65 static inline
store_line_and_scanline_2(uint8_t * const line,uint8_t * const scanline,int16_t * const prevline,const int shade,const int32_t y,const int32_t u,const int32_t v)66 void store_line_and_scanline_2(
67     uint8_t *const line, uint8_t *const scanline,
68     int16_t *const prevline, const int shade, /* ignored by RGB modes */
69     const int32_t y, const int32_t u, const int32_t v)
70 {
71     int16_t red, grn, blu;
72     uint16_t *tmp1, *tmp2;
73     yuv_to_rgb(y, u, v, &red, &grn, &blu);
74 
75     tmp1 = (uint16_t *) scanline;
76     tmp2 = (uint16_t *) line;
77 
78     *tmp1 = (uint16_t) (gamma_red_fac[512 + red + prevline[0]]
79                     | gamma_grn_fac[512 + grn + prevline[1]]
80                     | gamma_blu_fac[512 + blu + prevline[2]]);
81 
82     *tmp2 = (uint16_t) (gamma_red[256 + red] | gamma_grn[256 + grn] | gamma_blu[256 + blu]);
83 
84     prevline[0] = red;
85     prevline[1] = grn;
86     prevline[2] = blu;
87 }
88 
89 static inline
store_line_and_scanline_3(uint8_t * const line,uint8_t * const scanline,int16_t * const prevline,const int shade,const int32_t y,const int32_t u,const int32_t v)90 void store_line_and_scanline_3(
91     uint8_t *const line, uint8_t *const scanline,
92     int16_t *const prevline, const int shade, /* ignored by RGB modes */
93     const int32_t y, const int32_t u, const int32_t v)
94 {
95     uint32_t tmp1, tmp2;
96     int16_t red, grn, blu;
97     yuv_to_rgb(y, u, v, &red, &grn, &blu);
98 
99     tmp1 = gamma_red_fac[512 + red + prevline[0]]
100            | gamma_grn_fac[512 + grn + prevline[1]]
101            | gamma_blu_fac[512 + blu + prevline[2]];
102     tmp2 = gamma_red[256 + red] | gamma_grn[256 + grn] | gamma_blu[256 + blu];
103     scanline[0] = (uint8_t) tmp1;
104     tmp1 >>= 8;
105     scanline[1] = (uint8_t) tmp1;
106     tmp1 >>= 8;
107     scanline[2] = (uint8_t) tmp1;
108 
109     line[0] = (uint8_t) tmp2;
110     tmp2 >>= 8;
111     line[1] = (uint8_t) tmp2;
112     tmp2 >>= 8;
113     line[2] = (uint8_t) tmp2;
114 
115     prevline[0] = red;
116     prevline[1] = grn;
117     prevline[2] = blu;
118 }
119 
120 static inline
store_line_and_scanline_4(uint8_t * const line,uint8_t * const scanline,int16_t * const prevline,const int shade,const int32_t y,const int32_t u,const int32_t v)121 void store_line_and_scanline_4(
122     uint8_t *const line, uint8_t *const scanline,
123     int16_t *const prevline, const int shade, /* ignored by RGB modes */
124     const int32_t y, const int32_t u, const int32_t v)
125 {
126     int16_t red, grn, blu;
127     uint32_t *tmp1, *tmp2;
128     yuv_to_rgb(y, u, v, &red, &grn, &blu);
129 
130     tmp1 = (uint32_t *) scanline;
131     tmp2 = (uint32_t *) line;
132     *tmp1 = gamma_red_fac[512 + red + prevline[0]]
133             | gamma_grn_fac[512 + grn + prevline[1]]
134             | gamma_blu_fac[512 + blu + prevline[2]]
135             | alpha;
136     *tmp2 = gamma_red[256 + red] | gamma_grn[256 + grn] | gamma_blu[256 + blu]
137             | alpha;
138 
139     prevline[0] = red;
140     prevline[1] = grn;
141     prevline[2] = blu;
142 }
143 
144 static inline
store_line_and_scanline_UYVY(uint8_t * const line,uint8_t * const scanline,int16_t * const prevline,const int shade,int32_t y,int32_t u,int32_t v)145 void store_line_and_scanline_UYVY(
146     uint8_t *const line, uint8_t *const scanline,
147     int16_t *const prevline, const int shade,
148     int32_t y, int32_t u, int32_t v)
149 {
150 #ifdef _MSC_VER
151 # pragma warning( push )
152 # pragma warning( disable: 4244 )
153 #endif
154 
155     y >>= 16;
156     u >>= 16;
157     v >>= 16;
158 
159     line[0] = u + 128;
160     line[1] = y;
161     line[2] = v + 128;
162     line[3] = y;
163 
164     y = (y * shade) >> 8;
165     u = 128 + ((u * shade) >> 8);
166     v = 128 + ((v * shade) >> 8);
167 
168     scanline[0] = (u + prevline[1]) >> 1;
169     scanline[1] = (y + prevline[0]) >> 1;
170     scanline[2] = (v + prevline[2]) >> 1;
171     scanline[3] = (y + prevline[0]) >> 1;
172 
173     prevline[0] = y;
174     prevline[1] = u;
175     prevline[2] = v;
176 
177 #ifdef _MSC_VER
178 # pragma warning( pop )
179 #endif
180 }
181 
182 static inline
store_line_and_scanline_YUY2(uint8_t * const line,uint8_t * const scanline,int16_t * const prevline,const int shade,int32_t y,int32_t u,int32_t v)183 void store_line_and_scanline_YUY2(
184     uint8_t *const line, uint8_t *const scanline,
185     int16_t *const prevline, const int shade,
186     int32_t y, int32_t u, int32_t v)
187 {
188 #ifdef _MSC_VER
189 # pragma warning( push )
190 # pragma warning( disable: 4244 )
191 #endif
192 
193     y >>= 16;
194     u >>= 16;
195     v >>= 16;
196 
197     line[0] = y;
198     line[1] = u + 128;
199     line[2] = y;
200     line[3] = v + 128;
201 
202     y = (y * shade) >> 8;
203     u = 128 + ((u * shade) >> 8);
204     v = 128 + ((v * shade) >> 8);
205 
206     scanline[0] = (y + prevline[0]) >> 1;
207     scanline[1] = (u + prevline[1]) >> 1;
208     scanline[2] = (y + prevline[0]) >> 1;
209     scanline[3] = (v + prevline[2]) >> 1;
210 
211     prevline[0] = y;
212     prevline[1] = u;
213     prevline[2] = v;
214 
215 #ifdef _MSC_VER
216 # pragma warning( pop )
217 #endif
218 }
219 
220 static inline
store_line_and_scanline_YVYU(uint8_t * const line,uint8_t * const scanline,int16_t * const prevline,const int shade,int32_t y,int32_t u,int32_t v)221 void store_line_and_scanline_YVYU(
222     uint8_t *const line, uint8_t *const scanline,
223     int16_t *const prevline, const int shade,
224     int32_t y, int32_t u, int32_t v)
225 {
226 #ifdef _MSC_VER
227 # pragma warning( push )
228 # pragma warning( disable: 4244 )
229 #endif
230 
231     y >>= 16;
232     u >>= 16;
233     v >>= 16;
234 
235     line[0] = y;
236     line[1] = v + 128;
237     line[2] = y;
238     line[3] = u + 128;
239 
240     y = (y * shade) >> 8;
241     u = 128 + ((u * shade) >> 8);
242     v = 128 + ((v * shade) >> 8);
243 
244     scanline[0] = (y + prevline[0]) >> 1;
245     scanline[1] = (v + prevline[2]) >> 1;
246     scanline[2] = (y + prevline[0]) >> 1;
247     scanline[3] = (u + prevline[1]) >> 1;
248 
249     prevline[0] = y;
250     prevline[1] = u;
251     prevline[2] = v;
252 
253 #ifdef _MSC_VER
254 # pragma warning( pop )
255 #endif
256 }
257 
258 static inline
get_yuv_from_video(const int32_t unew,const int32_t vnew,int32_t * const line,const int off_flip,int32_t * const u,int32_t * const v)259 void get_yuv_from_video(
260     const int32_t unew, const int32_t vnew,
261     int32_t *const line, const int off_flip,
262     int32_t *const u, int32_t *const v)
263 {
264     *u = (unew + line[0]) * off_flip;
265     *v = (vnew + line[1]) * off_flip;
266     line[0] = unew;
267     line[1] = vnew;
268 }
269 
270 static inline
render_generic_2x2_pal(video_render_color_tables_t * color_tab,const uint8_t * src,uint8_t * trg,unsigned int width,const unsigned int height,unsigned int xs,const unsigned int ys,unsigned int xt,const unsigned int yt,const unsigned int pitchs,const unsigned int pitcht,viewport_t * viewport,unsigned int pixelstride,void (* store_func)(uint8_t * const line,uint8_t * const scanline,int16_t * const prevline,const int shade,int32_t l,int32_t u,int32_t v),const int write_interpolated_pixels,video_render_config_t * config)271 void render_generic_2x2_pal(video_render_color_tables_t *color_tab,
272                             const uint8_t *src, uint8_t *trg,
273                             unsigned int width, const unsigned int height,
274                             unsigned int xs, const unsigned int ys,
275                             unsigned int xt, const unsigned int yt,
276                             const unsigned int pitchs, const unsigned int pitcht,
277                             viewport_t *viewport, unsigned int pixelstride,
278                             void (*store_func)(
279                                 uint8_t *const line, uint8_t *const scanline,
280                                 int16_t *const prevline, const int shade,
281                                 int32_t l, int32_t u, int32_t v),
282                             const int write_interpolated_pixels, video_render_config_t *config)
283 {
284     int16_t *prevrgblineptr;
285     const int32_t *ytablel = color_tab->ytablel;
286     const int32_t *ytableh = color_tab->ytableh;
287     const uint8_t *tmpsrc;
288     uint8_t *tmptrg, *tmptrgscanline;
289     int32_t *line, *cbtable, *crtable;
290     uint32_t x, y, wfirst, wlast, yys;
291     int32_t l, l2, u, u2, unew, v, v2, vnew, off, off_flip, shade;
292     int first_line = viewport->first_line * 2;
293     int last_line = (viewport->last_line * 2) + 1;
294 
295     src = src + pitchs * ys + xs - 2;
296     trg = trg + pitcht * yt + xt * pixelstride;
297     yys = (ys << 1) | (yt & 1);
298     wfirst = xt & 1;
299     width -= wfirst;
300     wlast = width & 1;
301     width >>= 1;
302 
303     line = color_tab->line_yuv_0;
304     /* get previous line into buffer. */
305     tmpsrc = ys > 0 ? src - pitchs : src;
306 
307     if (ys & 1) {
308         cbtable = write_interpolated_pixels ? color_tab->cbtable : color_tab->cutable;
309         crtable = write_interpolated_pixels ? color_tab->crtable : color_tab->cvtable;
310     } else {
311         cbtable = write_interpolated_pixels ? color_tab->cbtable_odd : color_tab->cutable_odd;
312         crtable = write_interpolated_pixels ? color_tab->crtable_odd : color_tab->cvtable_odd;
313     }
314 
315     /* Initialize line */
316     unew = cbtable[tmpsrc[0]] + cbtable[tmpsrc[1]] + cbtable[tmpsrc[2]];
317     vnew = crtable[tmpsrc[0]] + crtable[tmpsrc[1]] + crtable[tmpsrc[2]];
318     for (x = 0; x < width + wfirst + 1; x++) {
319         unew += cbtable[tmpsrc[3]];
320         vnew += crtable[tmpsrc[3]];
321         line[0] = unew;
322         line[1] = vnew;
323         unew -= cbtable[tmpsrc[0]];
324         vnew -= crtable[tmpsrc[0]];
325         tmpsrc++;
326         line += 2;
327     }
328     /* That's all initialization we need for full lines. Unfortunately, for
329      * scanlines we also need to calculate the RGB color of the previous
330      * full line, and that requires initialization from 2 full lines above our
331      * rendering target. We just won't render the scanline above the target row,
332      * so you need to call us with 1 line before the desired rectangle, and
333      * for one full line after it! */
334 
335     /* Calculate odd line shading */
336     off = (int) (((float) config->video_resources.pal_oddlines_offset * (1.5f / 2000.0f) - (1.5f / 2.0f - 1.0f)) * (1 << 5));
337     shade = (int) ((float) config->video_resources.pal_scanlineshade / 1000.0f * 256.f);
338 
339     /* height & 1 == 0. */
340     for (y = yys; y < yys + height + 1; y += 2) {
341         /* when we are dealing with the last line, the rules change:
342          * we no longer write the main output to screen, we just put it into
343          * the scanline. */
344         if (y == yys + height) {
345             /* no place to put scanline in: we are outside viewport or still
346              * doing the first iteration (y == yys), height == 0 */
347             if (y == yys || y <= (unsigned int)first_line || y > (unsigned int)(last_line + 1)) {
348                 break;
349             }
350 
351             tmptrg = &color_tab->rgbscratchbuffer[0];
352             tmptrgscanline = trg - pitcht;
353             if (y == (unsigned int)(last_line + 1)) {
354                 /* src would point after the source area, so rewind one line */
355                 src -= pitchs;
356             }
357         } else {
358             /* pixel data to surface */
359             tmptrg = trg;
360             /* write scanline data to previous line if possible,
361              * otherwise we dump it to the scratch region... We must never
362              * render the scanline for the first row, because prevlinergb is not
363              * yet initialized and scanline data would be bogus! */
364             tmptrgscanline = y != yys && y > (unsigned int)first_line && y <= (unsigned int)last_line
365                              ? trg - pitcht
366                              : &color_tab->rgbscratchbuffer[0];
367         }
368 
369         /* current source image for YUV xform */
370         tmpsrc = src;
371         /* prev line's YUV-xformed data */
372         line = color_tab->line_yuv_0;
373 
374         if (y & 2) { /* odd sourceline */
375             off_flip = off;
376             cbtable = write_interpolated_pixels ? color_tab->cbtable_odd : color_tab->cutable_odd;
377             crtable = write_interpolated_pixels ? color_tab->crtable_odd : color_tab->cvtable_odd;
378         } else {
379             off_flip = 1 << 5;
380             cbtable = write_interpolated_pixels ? color_tab->cbtable : color_tab->cutable;
381             crtable = write_interpolated_pixels ? color_tab->crtable : color_tab->cvtable;
382         }
383 
384         l = ytablel[tmpsrc[1]] + ytableh[tmpsrc[2]] + ytablel[tmpsrc[3]];
385         unew = cbtable[tmpsrc[0]] + cbtable[tmpsrc[1]] + cbtable[tmpsrc[2]] + cbtable[tmpsrc[3]];
386         vnew = crtable[tmpsrc[0]] + crtable[tmpsrc[1]] + crtable[tmpsrc[2]] + crtable[tmpsrc[3]];
387         get_yuv_from_video(unew, vnew, line, off_flip, &u, &v);
388         unew -= cbtable[tmpsrc[0]];
389         vnew -= crtable[tmpsrc[0]];
390         tmpsrc += 1;
391         line += 2;
392 
393         /* actual line */
394         prevrgblineptr = &color_tab->prevrgbline[0];
395         if (wfirst) {
396             l2 = ytablel[tmpsrc[1]] + ytableh[tmpsrc[2]] + ytablel[tmpsrc[3]];
397             unew += cbtable[tmpsrc[3]];
398             vnew += crtable[tmpsrc[3]];
399             get_yuv_from_video(unew, vnew, line, off_flip, &u2, &v2);
400             unew -= cbtable[tmpsrc[0]];
401             vnew -= crtable[tmpsrc[0]];
402             tmpsrc += 1;
403             line += 2;
404 
405             if (write_interpolated_pixels) {
406                 store_func(tmptrg, tmptrgscanline, prevrgblineptr, shade, (l + l2) >> 1, (u + u2) >> 1, (v + v2) >> 1);
407                 tmptrgscanline += pixelstride;
408                 tmptrg += pixelstride;
409                 prevrgblineptr += 3;
410             }
411 
412             l = l2;
413             u = u2;
414             v = v2;
415         }
416         for (x = 0; x < width; x++) {
417             store_func(tmptrg, tmptrgscanline, prevrgblineptr, shade, l, u, v);
418             tmptrgscanline += pixelstride;
419             tmptrg += pixelstride;
420             prevrgblineptr += 3;
421 
422             l2 = ytablel[tmpsrc[1]] + ytableh[tmpsrc[2]] + ytablel[tmpsrc[3]];
423             unew += cbtable[tmpsrc[3]];
424             vnew += crtable[tmpsrc[3]];
425             get_yuv_from_video(unew, vnew, line, off_flip, &u2, &v2);
426             unew -= cbtable[tmpsrc[0]];
427             vnew -= crtable[tmpsrc[0]];
428             tmpsrc += 1;
429             line += 2;
430 
431             if (write_interpolated_pixels) {
432                 store_func(tmptrg, tmptrgscanline, prevrgblineptr, shade, (l + l2) >> 1, (u + u2) >> 1, (v + v2) >> 1);
433                 tmptrgscanline += pixelstride;
434                 tmptrg += pixelstride;
435                 prevrgblineptr += 3;
436             }
437 
438             l = l2;
439             u = u2;
440             v = v2;
441         }
442         if (wlast) {
443             store_func(tmptrg, tmptrgscanline, prevrgblineptr, shade, l, u, v);
444         }
445 
446         src += pitchs;
447         trg += pitcht * 2;
448     }
449 }
450 
render_UYVY_2x2_pal(video_render_color_tables_t * color_tab,const uint8_t * src,uint8_t * trg,unsigned int width,const unsigned int height,const unsigned int xs,const unsigned int ys,const unsigned int xt,const unsigned int yt,const unsigned int pitchs,const unsigned int pitcht,viewport_t * viewport,video_render_config_t * config)451 void render_UYVY_2x2_pal(video_render_color_tables_t *color_tab,
452                          const uint8_t *src, uint8_t *trg,
453                          unsigned int width, const unsigned int height,
454                          const unsigned int xs, const unsigned int ys,
455                          const unsigned int xt, const unsigned int yt,
456                          const unsigned int pitchs, const unsigned int pitcht,
457                          viewport_t *viewport, video_render_config_t *config)
458 {
459     render_generic_2x2_pal(color_tab, src, trg, width, height, xs, ys,
460                            xt, yt, pitchs, pitcht, viewport,
461                            4, store_line_and_scanline_UYVY, 0, config);
462 }
463 
render_YUY2_2x2_pal(video_render_color_tables_t * color_tab,const uint8_t * src,uint8_t * trg,unsigned int width,const unsigned int height,const unsigned int xs,const unsigned int ys,const unsigned int xt,const unsigned int yt,const unsigned int pitchs,const unsigned int pitcht,viewport_t * viewport,video_render_config_t * config)464 void render_YUY2_2x2_pal(video_render_color_tables_t *color_tab,
465                          const uint8_t *src, uint8_t *trg,
466                          unsigned int width, const unsigned int height,
467                          const unsigned int xs, const unsigned int ys,
468                          const unsigned int xt, const unsigned int yt,
469                          const unsigned int pitchs, const unsigned int pitcht,
470                          viewport_t *viewport, video_render_config_t *config)
471 {
472     render_generic_2x2_pal(color_tab, src, trg, width, height, xs, ys,
473                            xt, yt, pitchs, pitcht, viewport,
474                            4, store_line_and_scanline_YUY2, 0, config);
475 }
476 
render_YVYU_2x2_pal(video_render_color_tables_t * color_tab,const uint8_t * src,uint8_t * trg,unsigned int width,const unsigned int height,const unsigned int xs,const unsigned int ys,const unsigned int xt,const unsigned int yt,const unsigned int pitchs,const unsigned int pitcht,viewport_t * viewport,video_render_config_t * config)477 void render_YVYU_2x2_pal(video_render_color_tables_t *color_tab,
478                          const uint8_t *src, uint8_t *trg,
479                          unsigned int width, const unsigned int height,
480                          const unsigned int xs, const unsigned int ys,
481                          const unsigned int xt, const unsigned int yt,
482                          const unsigned int pitchs, const unsigned int pitcht,
483                          viewport_t *viewport, video_render_config_t *config)
484 {
485     render_generic_2x2_pal(color_tab, src, trg, width, height, xs, ys,
486                            xt, yt, pitchs, pitcht, viewport,
487                            4, store_line_and_scanline_YVYU, 0, config);
488 }
489 
render_16_2x2_pal(video_render_color_tables_t * color_tab,const uint8_t * src,uint8_t * trg,unsigned int width,const unsigned int height,const unsigned int xs,const unsigned int ys,const unsigned int xt,const unsigned int yt,const unsigned int pitchs,const unsigned int pitcht,viewport_t * viewport,video_render_config_t * config)490 void render_16_2x2_pal(video_render_color_tables_t *color_tab,
491                        const uint8_t *src, uint8_t *trg,
492                        unsigned int width, const unsigned int height,
493                        const unsigned int xs, const unsigned int ys,
494                        const unsigned int xt, const unsigned int yt,
495                        const unsigned int pitchs, const unsigned int pitcht,
496                        viewport_t *viewport, video_render_config_t *config)
497 {
498     render_generic_2x2_pal(color_tab, src, trg, width, height, xs, ys,
499                            xt, yt, pitchs, pitcht, viewport,
500                            2, store_line_and_scanline_2, 1, config);
501 }
502 
render_24_2x2_pal(video_render_color_tables_t * color_tab,const uint8_t * src,uint8_t * trg,unsigned int width,const unsigned int height,const unsigned int xs,const unsigned int ys,const unsigned int xt,const unsigned int yt,const unsigned int pitchs,const unsigned int pitcht,viewport_t * viewport,video_render_config_t * config)503 void render_24_2x2_pal(video_render_color_tables_t *color_tab,
504                        const uint8_t *src, uint8_t *trg,
505                        unsigned int width, const unsigned int height,
506                        const unsigned int xs, const unsigned int ys,
507                        const unsigned int xt, const unsigned int yt,
508                        const unsigned int pitchs, const unsigned int pitcht,
509                        viewport_t *viewport, video_render_config_t *config)
510 {
511     render_generic_2x2_pal(color_tab, src, trg, width, height, xs, ys,
512                            xt, yt, pitchs, pitcht, viewport,
513                            3, store_line_and_scanline_3, 1, config);
514 }
515 
render_32_2x2_pal(video_render_color_tables_t * color_tab,const uint8_t * src,uint8_t * trg,unsigned int width,const unsigned int height,const unsigned int xs,const unsigned int ys,const unsigned int xt,const unsigned int yt,const unsigned int pitchs,const unsigned int pitcht,viewport_t * viewport,video_render_config_t * config)516 void render_32_2x2_pal(video_render_color_tables_t *color_tab,
517                        const uint8_t *src, uint8_t *trg,
518                        unsigned int width, const unsigned int height,
519                        const unsigned int xs, const unsigned int ys,
520                        const unsigned int xt, const unsigned int yt,
521                        const unsigned int pitchs, const unsigned int pitcht,
522                        viewport_t *viewport, video_render_config_t *config)
523 {
524     render_generic_2x2_pal(color_tab, src, trg, width, height, xs, ys,
525                            xt, yt, pitchs, pitcht, viewport,
526                            4, store_line_and_scanline_4, 1, config);
527 }
528