Files
86Box/src/video/vid_cga_comp.c

386 lines
11 KiB
C
Raw Normal View History

/*
2023-01-06 15:36:05 -05:00
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
2023-01-06 15:36:05 -05:00
* This file is part of the 86Box distribution.
*
2023-01-06 15:36:05 -05:00
* IBM CGA composite filter, borrowed from reenigne's DOSBox
* patch and ported to C.
*
2020-03-25 00:46:02 +02:00
*
*
2023-01-06 15:36:05 -05:00
* Authors: reenigne,
* Miran Grca, <mgrca8@gmail.com>
*
2023-01-06 15:36:05 -05:00
* Copyright 2015-2019 reenigne.
* Copyright 2015-2019 Miran Grca.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <math.h>
#include <86box/86box.h>
#include <86box/timer.h>
#include <86box/mem.h>
#include <86box/vid_cga.h>
#include <86box/vid_cga_comp.h>
int CGA_Composite_Table[1024];
static double brightness = 0;
2022-08-31 19:19:29 -04:00
static double contrast = 100;
static double saturation = 100;
2022-08-31 19:19:29 -04:00
static double sharpness = 0;
static double hue_offset = 0;
/* New algorithm by reenigne
Works in all CGA modes/color settings and can simulate older and newer CGA revisions */
static const double tau = 6.28318531; /* == 2*pi */
static unsigned char chroma_multiplexer[256] = {
2022-08-31 19:19:29 -04:00
// clang-format off
2023-08-11 20:32:56 -04:00
2, 2, 2, 2, 114,174, 4, 3, 2, 1,133,135, 2,113,150, 4,
133, 2, 1, 99, 151,152, 2, 1, 3, 2, 96,136, 151,152,151,152,
2, 56, 62, 4, 111,250,118, 4, 0, 51,207,137, 1,171,209, 5,
140, 50, 54,100, 133,202, 57, 4, 2, 50,153,149, 128,198,198,135,
32, 1, 36, 81, 147,158, 1, 42, 33, 1,210,254, 34,109,169, 77,
177, 2, 0,165, 189,154, 3, 44, 33, 0, 91,197, 178,142,144,192,
4, 2, 61, 67, 117,151,112, 83, 4, 0,249,255, 3,107,249,117,
147, 1, 50,162, 143,141, 52, 54, 3, 0,145,206, 124,123,192,193,
72, 78, 2, 0, 159,208, 4, 0, 53, 58,164,159, 37,159,171, 1,
248,117, 4, 98, 212,218, 5, 2, 54, 59, 93,121, 176,181,134,130,
1, 61, 31, 0, 160,255, 34, 1, 1, 58,197,166, 0,177,194, 2,
162,111, 34, 96, 205,253, 32, 1, 1, 57,123,125, 119,188,150,112,
78, 4, 0, 75, 166,180, 20, 38, 78, 1,143,246, 42,113,156, 37,
252, 4, 1,188, 175,129, 1, 37, 118, 4, 88,249, 202,150,145,200,
61, 59, 60, 60, 228,252,117, 77, 60, 58,248,251, 81,212,254,107,
198, 59, 58,169, 250,251, 81, 80, 100, 58,154,250, 251,252,252,252
2022-08-31 19:19:29 -04:00
// clang-format on
};
static double intensity[4] = {
2022-08-31 19:19:29 -04:00
77.175381, 88.654656, 166.564623, 174.228438
};
2022-08-31 19:19:29 -04:00
#define NEW_CGA(c, i, r, g, b) (((c) / 0.72) * 0.29 + ((i) / 0.28) * 0.32 + ((r) / 0.28) * 0.1 + ((g) / 0.28) * 0.22 + ((b) / 0.28) * 0.07)
double mode_brightness;
double mode_contrast;
double mode_hue;
double min_v;
double max_v;
2023-06-01 18:32:25 -04:00
double video_ri;
double video_rq;
double video_gi;
double video_gq;
double video_bi;
double video_bq;
2022-08-31 19:19:29 -04:00
int video_sharpness;
int tandy_mode_control = 0;
static bool new_cga = 0;
2022-08-31 19:19:29 -04:00
void
update_cga16_color(uint8_t cgamode)
{
2023-06-01 18:32:25 -04:00
double c;
double i;
double v;
double q;
double a;
double s;
double r;
double iq_adjust_i;
double iq_adjust_q;
double i0;
double i3;
double mode_saturation;
2022-08-31 19:19:29 -04:00
static const double ri = 0.9563;
static const double rq = 0.6210;
static const double gi = -0.2721;
static const double gq = -0.6474;
static const double bi = -1.1069;
static const double bq = 1.7046;
if (!new_cga) {
min_v = chroma_multiplexer[0] + intensity[0];
max_v = chroma_multiplexer[255] + intensity[3];
} else {
i0 = intensity[0];
i3 = intensity[3];
min_v = NEW_CGA(chroma_multiplexer[0], i0, i0, i0, i0);
max_v = NEW_CGA(chroma_multiplexer[255], i3, i3, i3, i3);
}
mode_contrast = 256 / (max_v - min_v);
mode_brightness = -min_v * mode_contrast;
if ((cgamode & 3) == 1)
mode_hue = 14;
else
mode_hue = 4;
mode_contrast *= contrast * (new_cga ? 1.2 : 1) / 100; /* new CGA: 120% */
mode_brightness += (new_cga ? brightness - 10 : brightness) * 5; /* new CGA: -10 */
mode_saturation = (new_cga ? 4.35 : 2.9) * saturation / 100; /* new CGA: 150% */
2023-06-01 18:32:25 -04:00
for (uint16_t x = 0; x < 1024; ++x) {
2022-08-31 19:19:29 -04:00
int phase = x & 3;
int right = (x >> 2) & 15;
int left = (x >> 6) & 15;
int rc = right;
int lc = left;
if ((cgamode & 4) != 0) {
rc = (right & 8) | ((right & 7) != 0 ? 7 : 0);
lc = (left & 8) | ((left & 7) != 0 ? 7 : 0);
}
2022-08-31 19:19:29 -04:00
c = chroma_multiplexer[((lc & 7) << 5) | ((rc & 7) << 2) | phase];
i = intensity[(left >> 3) | ((right >> 2) & 2)];
if (!new_cga)
v = c + i;
else {
2022-08-31 19:19:29 -04:00
double r = intensity[((left >> 2) & 1) | ((right >> 1) & 2)];
double g = intensity[((left >> 1) & 1) | (right & 2)];
double b = intensity[(left & 1) | ((right << 1) & 2)];
v = NEW_CGA(c, i, r, g, b);
}
2022-08-31 19:19:29 -04:00
CGA_Composite_Table[x] = (int) (v * mode_contrast + mode_brightness);
}
i = CGA_Composite_Table[6 * 68] - CGA_Composite_Table[6 * 68 + 2];
q = CGA_Composite_Table[6 * 68 + 1] - CGA_Composite_Table[6 * 68 + 3];
a = tau * (33 + 90 + hue_offset + mode_hue) / 360.0;
c = cos(a);
s = sin(a);
r = 256 * mode_saturation / sqrt(i * i + q * q);
iq_adjust_i = -(i * c + q * s) * r;
iq_adjust_q = (q * c - i * s) * r;
video_ri = (int) (ri * iq_adjust_i + rq * iq_adjust_q);
video_rq = (int) (-ri * iq_adjust_q + rq * iq_adjust_i);
video_gi = (int) (gi * iq_adjust_i + gq * iq_adjust_q);
video_gq = (int) (-gi * iq_adjust_q + gq * iq_adjust_i);
video_bi = (int) (bi * iq_adjust_i + bq * iq_adjust_q);
video_bq = (int) (-bi * iq_adjust_q + bq * iq_adjust_i);
video_sharpness = (int) (sharpness * 256 / 100);
}
2023-07-30 19:33:29 -04:00
static uint8_t
2022-08-31 19:19:29 -04:00
byte_clamp(int v)
{
v >>= 13;
return v < 0 ? 0 : (v > 255 ? 255 : v);
}
/* 2048x1536 is the maximum we can possibly support. */
#define SCALER_MAXWIDTH 2048
2022-08-31 19:19:29 -04:00
static int temp[SCALER_MAXWIDTH + 10] = { 0 };
static int atemp[SCALER_MAXWIDTH + 2] = { 0 };
static int btemp[SCALER_MAXWIDTH + 2] = { 0 };
2023-07-30 19:33:29 -04:00
uint32_t *
Composite_Process(uint8_t cgamode, uint8_t border, uint32_t blocks /*, bool doublewidth*/, uint32_t *TempLine)
{
2023-07-30 19:33:29 -04:00
uint32_t x2;
2022-08-31 19:19:29 -04:00
int w = blocks * 4;
2023-07-30 19:33:29 -04:00
int *o;
const uint32_t *rgbi;
const int *b;
int *i;
uint32_t *srgb;
int *ap;
int *bp;
2022-08-31 19:19:29 -04:00
#define COMPOSITE_CONVERT(I, Q) \
do { \
i[1] = (i[1] << 3) - ap[1]; \
a = ap[0]; \
b = bp[0]; \
c = i[0] + i[0]; \
d = i[-1] + i[1]; \
y = ((c + d) << 8) + video_sharpness * (c - d); \
rr = y + video_ri * (I) + video_rq * (Q); \
gg = y + video_gi * (I) + video_gq * (Q); \
bb = y + video_bi * (I) + video_bq * (Q); \
++i; \
++ap; \
++bp; \
*srgb = (byte_clamp(rr) << 16) | (byte_clamp(gg) << 8) | byte_clamp(bb); \
++srgb; \
} while (0)
#define OUT(v) \
do { \
*o = (v); \
++o; \
} while (0)
/* Simulate CGA composite output */
o = temp;
rgbi = TempLine;
b = &CGA_Composite_Table[border * 68];
2023-07-30 19:33:29 -04:00
for (uint8_t x = 0; x < 4; ++x)
2022-08-31 19:19:29 -04:00
OUT(b[(x + 3) & 3]);
OUT(CGA_Composite_Table[(border << 6) | ((*rgbi & 0x0f) << 2) | 3]);
2023-07-30 19:33:29 -04:00
for (int x = 0; x < w - 1; ++x) {
2022-08-31 19:19:29 -04:00
OUT(CGA_Composite_Table[((rgbi[0] & 0x0f) << 6) | ((rgbi[1] & 0x0f) << 2) | (x & 3)]);
++rgbi;
}
OUT(CGA_Composite_Table[((*rgbi & 0x0f) << 6) | (border << 2) | 3]);
2023-07-30 19:33:29 -04:00
for (uint8_t x = 0; x < 5; ++x)
2022-08-31 19:19:29 -04:00
OUT(b[x & 3]);
if ((cgamode & 4) != 0) {
/* Decode */
i = temp + 5;
2023-06-09 23:46:54 -04:00
srgb = TempLine;
2022-08-31 19:19:29 -04:00
for (x2 = 0; x2 < blocks * 4; ++x2) {
int c = (i[0] + i[0]) << 3;
int d = (i[-1] + i[1]) << 3;
int y = ((c + d) << 8) + video_sharpness * (c - d);
++i;
*srgb = byte_clamp(y) * 0x10101;
++srgb;
}
2022-08-31 19:19:29 -04:00
} else {
/* Store chroma */
i = temp + 4;
ap = atemp + 1;
bp = btemp + 1;
2023-07-30 19:33:29 -04:00
for (int x = -1; x < w + 1; ++x) {
2022-08-31 19:19:29 -04:00
ap[x] = i[-4] - ((i[-2] - i[0] + i[2]) << 1) + i[4];
bp[x] = (i[-3] - i[-1] + i[1] - i[3]) << 1;
++i;
}
2022-08-31 19:19:29 -04:00
/* Decode */
i = temp + 5;
i[-1] = (i[-1] << 3) - ap[-1];
i[0] = (i[0] << 3) - ap[0];
2023-06-09 23:46:54 -04:00
srgb = TempLine;
2022-08-31 19:19:29 -04:00
for (x2 = 0; x2 < blocks; ++x2) {
2023-06-01 18:32:25 -04:00
int y;
int a;
int b;
int c;
int d;
int rr;
int gg;
int bb;
2022-08-31 19:19:29 -04:00
COMPOSITE_CONVERT(a, b);
COMPOSITE_CONVERT(-b, a);
COMPOSITE_CONVERT(-a, -b);
COMPOSITE_CONVERT(b, -a);
}
2022-08-31 19:19:29 -04:00
}
#undef COMPOSITE_CONVERT
#undef OUT
2022-08-31 19:19:29 -04:00
return TempLine;
}
2022-08-31 19:19:29 -04:00
void
IncreaseHue(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
hue_offset += 5.0;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
DecreaseHue(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
hue_offset -= 5.0;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
IncreaseSaturation(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
saturation += 5;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
DecreaseSaturation(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
saturation -= 5;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
IncreaseContrast(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
contrast += 5;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
DecreaseContrast(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
contrast -= 5;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
IncreaseBrightness(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
brightness += 5;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
DecreaseBrightness(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
brightness -= 5;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
IncreaseSharpness(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
sharpness += 10;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
DecreaseSharpness(uint8_t cgamode)
{
2022-08-31 19:19:29 -04:00
sharpness -= 10;
2022-08-31 19:19:29 -04:00
update_cga16_color(cgamode);
}
2022-08-31 19:19:29 -04:00
void
cga_comp_init(int revision)
{
2022-08-31 19:19:29 -04:00
new_cga = revision;
2022-08-31 19:19:29 -04:00
/* Making sure this gets reset after reset. */
brightness = 0;
contrast = 100;
saturation = 100;
sharpness = 0;
hue_offset = 0;
2022-08-31 19:19:29 -04:00
update_cga16_color(0);
}