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