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