1 /*
2 * render2x2crt.c - 2x2 CRT renderers
3 *
4 * Written by
5 * groepaz <groepaz@gmx.net> based on the pal renderers written by
6 * John Selck <graham@cruise.de>
7 *
8 * This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 #include "vice.h"
29
30 #include <stdio.h>
31
32 #include "render2x2.h"
33 #include "render2x2crt.h"
34 #include "types.h"
35 #include "video-color.h"
36
37 /*
38 this is the simpliest possible CRT emulation, meaning blur and scanlines only.
39
40 TODO: use RGB color space
41 */
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
259 static inline
get_yuv_from_video(const int32_t unew,const int32_t vnew,const int off_flip,int32_t * const u,int32_t * const v)260 void get_yuv_from_video(
261 const int32_t unew, const int32_t vnew,
262 const int off_flip,
263 int32_t *const u, int32_t *const v)
264 {
265 *u = (unew) * off_flip;
266 *v = (vnew) * off_flip;
267 }
268
269 static inline
render_generic_2x2_crt(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)270 void render_generic_2x2_crt(video_render_color_tables_t *color_tab,
271 const uint8_t *src, uint8_t *trg,
272 unsigned int width, const unsigned int height,
273 unsigned int xs, const unsigned int ys,
274 unsigned int xt, const unsigned int yt,
275 const unsigned int pitchs, const unsigned int pitcht,
276 viewport_t *viewport, unsigned int pixelstride,
277 void (*store_func)(
278 uint8_t *const line, uint8_t *const scanline,
279 int16_t *const prevline, const int shade,
280 int32_t l, int32_t u, int32_t v),
281 const int write_interpolated_pixels, video_render_config_t *config)
282 {
283 int16_t *prevrgblineptr;
284 const int32_t *ytablel = color_tab->ytablel;
285 const int32_t *ytableh = color_tab->ytableh;
286 const uint8_t *tmpsrc;
287 uint8_t *tmptrg, *tmptrgscanline;
288 int32_t *cbtable, *crtable;
289 uint32_t x, y, wfirst, wlast, yys;
290 int32_t l, l2, u, u2, unew, v, v2, vnew, off_flip, shade;
291 int first_line = viewport->first_line * 2;
292 int last_line = (viewport->last_line * 2) + 1;
293
294 src = src + pitchs * ys + xs - 2;
295 trg = trg + pitcht * yt + xt * pixelstride;
296 yys = (ys << 1) | (yt & 1);
297 wfirst = xt & 1;
298 width -= wfirst;
299 wlast = width & 1;
300 width >>= 1;
301
302 /* That's all initialization we need for full lines. Unfortunately, for
303 * scanlines we also need to calculate the RGB color of the previous
304 * full line, and that requires initialization from 2 full lines above our
305 * rendering target. We just won't render the scanline above the target row,
306 * so you need to call us with 1 line before the desired rectangle, and
307 * for one full line after it! */
308
309 /* Calculate odd line shading */
310 shade = (int) ((float) config->video_resources.pal_scanlineshade / 1000.0f * 256.f);
311 off_flip = 1 << 6;
312
313 /* height & 1 == 0. */
314 for (y = yys; y < yys + height + 1; y += 2) {
315 /* when we are dealing with the last line, the rules change:
316 * we no longer write the main output to screen, we just put it into
317 * the scanline. */
318 if (y == yys + height) {
319 /* no place to put scanline in: we are outside viewport or still
320 * doing the first iteration (y == yys), height == 0 */
321 if (y == yys || y <= (unsigned int)first_line || y > (unsigned int)(last_line + 1)) {
322 break;
323 }
324 tmptrg = &color_tab->rgbscratchbuffer[0];
325 tmptrgscanline = trg - pitcht;
326 if (y == (unsigned int)(last_line + 1)) {
327 /* src would point after the source area, so rewind one line */
328 src -= pitchs;
329 }
330 } else {
331 /* pixel data to surface */
332 tmptrg = trg;
333 /* write scanline data to previous line if possible,
334 * otherwise we dump it to the scratch region... We must never
335 * render the scanline for the first row, because prevlinergb is not
336 * yet initialized and scanline data would be bogus! */
337 tmptrgscanline = y != yys && y > (unsigned int)first_line && y <= (unsigned int)last_line
338 ? trg - pitcht
339 : &color_tab->rgbscratchbuffer[0];
340 }
341
342 /* current source image for YUV xform */
343 tmpsrc = src;
344
345 cbtable = write_interpolated_pixels ? color_tab->cbtable : color_tab->cutable;
346 crtable = write_interpolated_pixels ? color_tab->crtable : color_tab->cvtable;
347
348 l = ytablel[tmpsrc[1]] + ytableh[tmpsrc[2]] + ytablel[tmpsrc[3]];
349 unew = cbtable[tmpsrc[0]] + cbtable[tmpsrc[1]] + cbtable[tmpsrc[2]] + cbtable[tmpsrc[3]];
350 vnew = crtable[tmpsrc[0]] + crtable[tmpsrc[1]] + crtable[tmpsrc[2]] + crtable[tmpsrc[3]];
351 get_yuv_from_video(unew, vnew, off_flip, &u, &v);
352 unew -= cbtable[tmpsrc[0]];
353 vnew -= crtable[tmpsrc[0]];
354 tmpsrc += 1;
355
356 /* actual line */
357 prevrgblineptr = &color_tab->prevrgbline[0];
358 if (wfirst) {
359 l2 = ytablel[tmpsrc[1]] + ytableh[tmpsrc[2]] + ytablel[tmpsrc[3]];
360 unew += cbtable[tmpsrc[3]];
361 vnew += crtable[tmpsrc[3]];
362 get_yuv_from_video(unew, vnew, off_flip, &u2, &v2);
363 unew -= cbtable[tmpsrc[0]];
364 vnew -= crtable[tmpsrc[0]];
365 tmpsrc += 1;
366 #if 1
367 if (write_interpolated_pixels) {
368 store_func(tmptrg, tmptrgscanline, prevrgblineptr, shade, (l + l2) >> 1, (u + u2) >> 1, (v + v2) >> 1);
369 tmptrgscanline += pixelstride;
370 tmptrg += pixelstride;
371 prevrgblineptr += 3;
372 }
373 #endif
374 l = l2;
375 u = u2;
376 v = v2;
377 }
378 for (x = 0; x < width; x++) {
379 #if 1
380 store_func(tmptrg, tmptrgscanline, prevrgblineptr, shade, l, u, v);
381 tmptrgscanline += pixelstride;
382 tmptrg += pixelstride;
383 prevrgblineptr += 3;
384 #endif
385 l2 = ytablel[tmpsrc[1]] + ytableh[tmpsrc[2]] + ytablel[tmpsrc[3]];
386 unew += cbtable[tmpsrc[3]];
387 vnew += crtable[tmpsrc[3]];
388 get_yuv_from_video(unew, vnew, off_flip, &u2, &v2);
389 unew -= cbtable[tmpsrc[0]];
390 vnew -= crtable[tmpsrc[0]];
391 tmpsrc += 1;
392 #if 1
393 if (write_interpolated_pixels) {
394 store_func(tmptrg, tmptrgscanline, prevrgblineptr, shade, (l + l2) >> 1, (u + u2) >> 1, (v + v2) >> 1);
395 tmptrgscanline += pixelstride;
396 tmptrg += pixelstride;
397 prevrgblineptr += 3;
398 }
399 #endif
400 l = l2;
401 u = u2;
402 v = v2;
403 }
404 if (wlast) {
405 store_func(tmptrg, tmptrgscanline, prevrgblineptr, shade, l, u, v);
406 }
407
408 src += pitchs;
409 trg += pitcht * 2;
410 }
411 }
412
render_UYVY_2x2_crt(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)413 void render_UYVY_2x2_crt(video_render_color_tables_t *color_tab,
414 const uint8_t *src, uint8_t *trg,
415 unsigned int width, const unsigned int height,
416 const unsigned int xs, const unsigned int ys,
417 const unsigned int xt, const unsigned int yt,
418 const unsigned int pitchs, const unsigned int pitcht,
419 viewport_t *viewport, video_render_config_t *config)
420 {
421 render_generic_2x2_crt(color_tab, src, trg, width, height, xs, ys,
422 xt, yt, pitchs, pitcht, viewport,
423 4, store_line_and_scanline_UYVY, 0, config);
424 }
425
render_YUY2_2x2_crt(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)426 void render_YUY2_2x2_crt(video_render_color_tables_t *color_tab,
427 const uint8_t *src, uint8_t *trg,
428 unsigned int width, const unsigned int height,
429 const unsigned int xs, const unsigned int ys,
430 const unsigned int xt, const unsigned int yt,
431 const unsigned int pitchs, const unsigned int pitcht,
432 viewport_t *viewport, video_render_config_t *config)
433 {
434 render_generic_2x2_crt(color_tab, src, trg, width, height, xs, ys,
435 xt, yt, pitchs, pitcht, viewport,
436 4, store_line_and_scanline_YUY2, 0, config);
437 }
438
render_YVYU_2x2_crt(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)439 void render_YVYU_2x2_crt(video_render_color_tables_t *color_tab,
440 const uint8_t *src, uint8_t *trg,
441 unsigned int width, const unsigned int height,
442 const unsigned int xs, const unsigned int ys,
443 const unsigned int xt, const unsigned int yt,
444 const unsigned int pitchs, const unsigned int pitcht,
445 viewport_t *viewport, video_render_config_t *config)
446 {
447 render_generic_2x2_crt(color_tab, src, trg, width, height, xs, ys,
448 xt, yt, pitchs, pitcht, viewport,
449 4, store_line_and_scanline_YVYU, 0, config);
450 }
451
render_16_2x2_crt(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)452 void render_16_2x2_crt(video_render_color_tables_t *color_tab,
453 const uint8_t *src, uint8_t *trg,
454 unsigned int width, const unsigned int height,
455 const unsigned int xs, const unsigned int ys,
456 const unsigned int xt, const unsigned int yt,
457 const unsigned int pitchs, const unsigned int pitcht,
458 viewport_t *viewport, video_render_config_t *config)
459 {
460 render_generic_2x2_crt(color_tab, src, trg, width, height, xs, ys,
461 xt, yt, pitchs, pitcht, viewport,
462 2, store_line_and_scanline_2, 1, config);
463 }
464
render_24_2x2_crt(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)465 void render_24_2x2_crt(video_render_color_tables_t *color_tab,
466 const uint8_t *src, uint8_t *trg,
467 unsigned int width, const unsigned int height,
468 const unsigned int xs, const unsigned int ys,
469 const unsigned int xt, const unsigned int yt,
470 const unsigned int pitchs, const unsigned int pitcht,
471 viewport_t *viewport, video_render_config_t *config)
472 {
473 render_generic_2x2_crt(color_tab, src, trg, width, height, xs, ys,
474 xt, yt, pitchs, pitcht, viewport,
475 3, store_line_and_scanline_3, 1, config);
476 }
477
render_32_2x2_crt(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)478 void render_32_2x2_crt(video_render_color_tables_t *color_tab,
479 const uint8_t *src, uint8_t *trg,
480 unsigned int width, const unsigned int height,
481 const unsigned int xs, const unsigned int ys,
482 const unsigned int xt, const unsigned int yt,
483 const unsigned int pitchs, const unsigned int pitcht,
484 viewport_t *viewport, video_render_config_t *config)
485 {
486 render_generic_2x2_crt(color_tab, src, trg, width, height, xs, ys,
487 xt, yt, pitchs, pitcht, viewport,
488 4, store_line_and_scanline_4, 1, config);
489 }
490