Files
libretro-atari800/atari800/src/pal_blending.c
T
2015-12-14 14:00:35 +01:00

265 lines
7.9 KiB
C

/*
* pal_blending.c - blitting functions that emulate PAL delay line accurately
*
* Copyright (C) 2013 Tomasz Krasuski
* Copyright (C) 2013 Atari800 development team (see DOC/CREDITS)
*
* This file is part of the Atari800 emulator project which emulates
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
* Atari800 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* Atari800 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along
* with Atari800; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "pal_blending.h"
#include "artifact.h"
#include "atari.h"
#include "colours.h"
#include "colours_pal.h"
#include "platform.h"
#include "screen.h"
#if SUPPORTS_CHANGE_VIDEOMODE
#include "videomode.h"
#endif /* SUPPORTS_CHANGE_VIDEOMODE */
static union {
UWORD bpp16[2][256]; /* 16-bit palette */
ULONG bpp32[2][256]; /* 32-bit palette */
} palette;
static ULONG shift_mask;
void PAL_BLENDING_UpdateLookup(void)
{
if (ARTIFACT_mode == ARTIFACT_PAL_BLEND) {
double yuv_table[256*5];
int even_pal[256];
int odd_pal[256];
int i;
double *ptr = yuv_table;
PLATFORM_pixel_format_t format;
COLOURS_PAL_GetYUV(yuv_table);
for (i = 0; i < 256; ++i) {
double y = *ptr++;
double even_u = *ptr++;
double odd_u = *ptr++;
double even_v = *ptr++;
double odd_v = *ptr++;
double r, g, b;
Colours_YUV2RGB(y, even_u, even_v, &r, &g, &b);
if (!COLOURS_PAL_external.loaded || COLOURS_PAL_external.adjust) {
r = Colours_Gamma2Linear(r, COLOURS_PAL_setup.gamma);
g = Colours_Gamma2Linear(g, COLOURS_PAL_setup.gamma);
b = Colours_Gamma2Linear(b, COLOURS_PAL_setup.gamma);
r = Colours_Linear2sRGB(r);
g = Colours_Linear2sRGB(g);
b = Colours_Linear2sRGB(b);
}
Colours_SetRGB(i, (int) (r * 255), (int) (g * 255), (int) (b * 255), even_pal);
Colours_YUV2RGB(y, odd_u, odd_v, &r, &g, &b);
if (!COLOURS_PAL_external.loaded || COLOURS_PAL_external.adjust) {
r = Colours_Gamma2Linear(r, COLOURS_PAL_setup.gamma);
g = Colours_Gamma2Linear(g, COLOURS_PAL_setup.gamma);
b = Colours_Gamma2Linear(b, COLOURS_PAL_setup.gamma);
r = Colours_Linear2sRGB(r);
g = Colours_Linear2sRGB(g);
b = Colours_Linear2sRGB(b);
}
Colours_SetRGB(i, (int) (r * 255), (int) (g * 255), (int) (b * 255), odd_pal);
}
PLATFORM_GetPixelFormat(&format);
shift_mask = (format.rmask & ~(format.rmask << 1)) | (format.gmask & ~(format.gmask << 1)) | (format.bmask & ~(format.bmask << 1));
switch (format.bpp) {
case 16:
PLATFORM_MapRGB(palette.bpp16[0], even_pal, 256);
PLATFORM_MapRGB(palette.bpp16[1], odd_pal, 256);
shift_mask |= shift_mask << 16;
break;
case 32:
PLATFORM_MapRGB(palette.bpp32[0], even_pal, 256);
PLATFORM_MapRGB(palette.bpp32[1], odd_pal, 256);
}
shift_mask = ~shift_mask;
}
}
void PAL_BLENDING_Blit16(ULONG *dest, UBYTE *src, int pitch, int width, int height, int start_odd)
{
register ULONG quad, quad_prev;
register UBYTE c;
register int pos;
UBYTE *src_prev = src;
int odd_prev = start_odd ^ 1;
int width_32;
if (width & 0x01)
width_32 = width + 1;
else
width_32 = width;
while (height > 0) {
pos = width_32;
do {
pos--;
c = src[pos];
/* Make QUAD_PREV have the same Y component as the current line's pixel. */
quad_prev = palette.bpp16[odd_prev][(src_prev[pos] & 0xf0) | (c & 0x0f)] << 16;
quad = palette.bpp16[start_odd][c] << 16;
pos--;
c = src[pos];
quad_prev |= palette.bpp16[odd_prev][(src_prev[pos] & 0xf0) | (c & 0x0f)];
quad |= palette.bpp16[start_odd][c];
/* Since QUAD_PREV and QUAD have the same Y component, computing
averages of even U/V and odd U/V is equal to computing averages
of even and odd RGB components. */
/* dest[pos >> 1] = ((quad+quad_prev) & shift_mask)/2; */
dest[pos >> 1] = (quad & quad_prev) + (((quad ^ quad_prev) & shift_mask) >> 1);
} while (pos > 0);
src_prev = src;
src += Screen_WIDTH;
dest += pitch;
height--;
start_odd ^= 1;
odd_prev ^= 1;
}
}
void PAL_BLENDING_Blit32(ULONG *dest, UBYTE *src, int pitch, int width, int height, int start_odd)
{
register ULONG quad, quad_prev;
register UBYTE c;
register int pos;
UBYTE *src_prev = src;
int odd_prev = start_odd ^ 1;
while (height > 0) {
pos = width;
do {
pos--;
c = src[pos];
/* Make QUAD_PREV have the same Y component as the current line's pixel. */
quad_prev = palette.bpp32[odd_prev][(src_prev[pos] & 0xf0) | (c & 0x0f)];
quad = palette.bpp32[start_odd][c];
/* Since QUAD_PREV and QUAD have the same Y component, computing
averages of even U/V and odd U/V is equal to computing averages
of even and odd RGB components. */
/* dest[pos] = ((quad+quad_prev) & shift_mask)/2; */
dest[pos] = (quad & quad_prev) + (((quad ^ quad_prev) & shift_mask) >> 1);
} while (pos > 0);
src_prev = src;
src += Screen_WIDTH;
dest += pitch;
height--;
start_odd ^= 1;
odd_prev ^= 1;
}
}
void PAL_BLENDING_BlitScaled16(ULONG *dest, UBYTE *src, int pitch, int width, int height, int dest_width, int dest_height, int start_odd)
{
register ULONG quad, quad_prev;
register int x;
int y = 0x10000;
int w1 = dest_width / 2 - 1;
int w = width << 16;
int h = height << 16;
int pos;
register int dx = w / dest_width;
int dy = h / dest_height;
int init_x = (width << 16) - 0x4000;
UBYTE *src_prev = src;
int odd_prev = start_odd ^ 1;
UBYTE c;
while (dest_height > 0) {
x = init_x;
pos = w1;
while (pos >= 0) {
c = src[x >> 16];
/* Make QUAD_PREV have the same Y component as the current line's pixel. */
quad_prev = palette.bpp16[odd_prev][(src_prev[x >> 16] & 0xf0) | (c & 0x0f)] << 16;
quad = palette.bpp16[start_odd][c] << 16;
x -= dx;
c = src[x >> 16];
quad_prev |= palette.bpp16[odd_prev][(src_prev[x >> 16] & 0xf0) | (c & 0x0f)];
quad |= palette.bpp16[start_odd][c];
x -= dx;
/* Since QUAD_PREV and QUAD have the same Y component, computing
averages of even U/V and odd U/V is equal to computing averages
of even and odd RGB components. */
/* dest[pos] = ((quad+quad_prev) & shift_mask)/2; */
dest[pos] = (quad & quad_prev) + (((quad ^ quad_prev) & shift_mask) >> 1);
pos--;
}
dest += pitch;
y -= dy;
--dest_height;
if (y < 0) {
y += 0x10000;
src_prev = src;
src += Screen_WIDTH;
start_odd ^= 1;
odd_prev ^= 1;
}
}
}
void PAL_BLENDING_BlitScaled32(ULONG *dest, UBYTE *src, int pitch, int width, int height, int dest_width, int dest_height, int start_odd)
{
register ULONG quad, quad_prev;
register int x;
int y = 0x10000;
int w1 = dest_width - 1;
int w = width << 16;
int h = height << 16;
int pos;
register int dx = w / dest_width;
int dy = h / dest_height;
int init_x = w - 0x4000;
UBYTE *src_prev = src;
int odd_prev = start_odd ^ 1;
UBYTE c;
while (dest_height > 0) {
x = init_x;
pos = w1;
while (pos >= 0) {
c = src[x >> 16];
/* Make QUAD_PREV have the same Y component as the current line's pixel. */
quad_prev = palette.bpp32[odd_prev][(src_prev[x >> 16] & 0xf0) | (c & 0x0f)];
quad = palette.bpp32[start_odd][c];
x -= dx;
/* Since QUAD_PREV and QUAD have the same Y component, computing
averages of even U/V and odd U/V is equal to computing averages
of even and odd RGB components. */
/* dest[pos] = ((quad+quad_prev) & shift_mask)/2; */
dest[pos] = (quad & quad_prev) + (((quad ^ quad_prev) & shift_mask) >> 1);
pos--;
}
dest += pitch;
y -= dy;
--dest_height;
if (y < 0) {
y += 0x10000;
src_prev = src;
src += Screen_WIDTH;
start_odd ^= 1;
odd_prev ^= 1;
}
}
}