OXYGENHUB
◆ main openmsx / src / video / scalers / HQCommon.hh
4.9 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef HQCOMMON_HH
2
#define HQCOMMON_HH
3
4
#include "endian.hh"
5
#include "narrow.hh"
6
#include "xrange.hh"
7
#include <cassert>
8
#include <cstdint>
9
#include <span>
10
11
namespace openmsx {
12
13
struct EdgeHQ
14
{
15
	[[nodiscard]] bool operator()(uint32_t c1, uint32_t c2) const
16
	{
17
		if (c1 == c2) return false;
18
19
		unsigned r1 = (c1 >>  0) & 0xFF;
20
		unsigned g1 = (c1 >>  8) & 0xFF;
21
		unsigned b1 = (c1 >> 16) & 0xFF;
22
23
		unsigned r2 = (c2 >>  0) & 0xFF;
24
		unsigned g2 = (c2 >>  8) & 0xFF;
25
		unsigned b2 = (c2 >> 16) & 0xFF;
26
27
		auto dr = narrow_cast<int>(r1 - r2);
28
		auto dg = narrow_cast<int>(g1 - g2);
29
		auto db = narrow_cast<int>(b1 - b2);
30
31
		int dy = dr + dg + db;
32
		if (dy < -0xC0 || dy > 0xC0) return true;
33
34
		if (int du = dr - db;
35
		    du < -0x1C || du > 0x1C) return true;
36
37
		if (int dv = 3 * dg - dy;
38
		    dv < -0x30 || dv > 0x30) return true;
39
40
		return false;
41
	}
42
};
43
44
template<typename EdgeOp>
45
void calcEdgesGL(std::span<const uint32_t> curr, std::span<const uint32_t> next,
46
                 std::span<Endian::L32> edges2, EdgeOp edgeOp)
47
{
48
	// Consider a grid of 3x3 pixels, numbered like this:
49
	//    1 | 2 | 3
50
	//   ---A---B---
51
	//    4 | 5 | 6
52
	//   ---C---D---
53
	//    7 | 8 | 9
54
	// Then we calculate 12 'edges':
55
	// * 8 star-edges, from the central pixel '5' to the 8 neighboring pixels.
56
	//   Let's call these edges 1, 2, 3, 4, 6, 7, 8, 9 (note: 5 is skipped).
57
	// * 4 cross-edges, between pixels (2,4), (2,6), (4,8), (6,8).
58
	//   Let's call these respectively A, B, C, D.
59
	// An edge between two pixels means the color of the two pixels is sufficiently distant.
60
	// * For the HQ scaler see 'EdgeHQ' for the definition of this distance function.
61
	// * The HQlite scaler uses a much simpler distance function.
62
	//
63
	// We store these 12 edges in a 16-bit value and order them like this:
64
	// (MSB (bit 15) on the left, LSB (bit 0) on the left, 'x' means bit is not used)
65
	//   || B 3 6 9 | D 2 x x || 8 1 A 4 | C 7 x x ||
66
	// This order has two important properties:
67
	// * The 12 bits are split in 2 groups of 6 bits and each group is MSB
68
	//   aligned within a byte. This allows to upload this data as a
69
	//   openGL texture and interpret each texel as a vec2 which
70
	//   represents a texture coordinate in another 64x64 texture.
71
	// * This order allows to calculate the edges incrementally:
72
	//   Suppose we already calculated the edges for the pixel immediate
73
	//   above and immediately to the left of the current pixel. Then the edges
74
	//        (1, 2, 3, A, B) can be calculated as:  (upper << 3) & 0xc460
75
	//    and (4, 7, C)       can be calculated as:  (left  >> 9) & 0x001C
76
	//   And only edges (6, 8, 9, D) must be newly calculated for this pixel.
77
	//   So only 4 new edges per pixel instead of all 12.
78
	//
79
	// This function takes as input:
80
	// * an in/out-array 'edges2':
81
	//   This contains the edge information for the upper row of pixels.
82
	//   And it gets update in-place to the edge information of the current
83
	//   row of pixels.
84
	// * 2 rows of input pixels: the middle and the lower pixel rows.
85
	// * An edge-function (to distinguish 'hq' from 'hqlite').
86
87
	auto size = curr.size();
88
	assert(next.size() == size);
89
	assert(2 * edges2.size() == size);
90
91
	using Pixel = uint32_t;
92
93
	uint32_t pattern = 0;
94
	Pixel c5 = curr[0];
95
	Pixel c8 = next[0];
96
	if (edgeOp(c5, c8)) pattern |= 0x1800'0000; // edges: 9,D (right pixel)
97
98
	auto size2 = size / 2;
99
	for (auto xx : xrange(size2 - 1)) {
100
		pattern = (pattern >> (16 + 9)) & 0x001C;   // edges: 6,D,9 -> 4,7,C        (left pixel)
101
		pattern |= (edges2[xx] << 3) & 0xC460'C460; // edges C,8,D,7,9 -> 1,2,3,A,B (left and right)
102
103
		if (edgeOp(c5, c8)) pattern |= 0x0000'0080; // edge: 8 (left)
104
		Pixel c6 = curr[2 * xx + 1];
105
		if (edgeOp(c6, c8)) pattern |= 0x0004'0800; // edge: D (left), 7 (right)
106
		if (edgeOp(c5, c6)) pattern |= 0x0010'2000; // edge: 6 (left), 4 (right)
107
		Pixel c9 = next[2 * xx + 1];
108
		if (edgeOp(c5, c9)) pattern |= 0x0008'1000; // edge: 9 (left), C (right)
109
110
		if (edgeOp(c6, c9)) pattern |= 0x0080'0000; // edge:           8 (right)
111
		c5 = curr[2 * xx + 2];
112
		if (edgeOp(c5, c9)) pattern |= 0x0800'0000; // edge:           D (right)
113
		if (edgeOp(c6, c5)) pattern |= 0x2000'0000; // edge:           6 (right)
114
		c8 = next[2 * xx + 2];
115
		if (edgeOp(c6, c8)) pattern |= 0x1000'0000; // edge:           9 (right)
116
117
		edges2[xx] = pattern;
118
	}
119
120
	pattern = (pattern >> (16 + 9)) & 0x001C;    // edges: 6,D,9 -> 4,7,C         (left pixel)
121
	pattern |= (edges2[size2 - 1] << 3) & 0xC460'C460; // edges: C,8,D,7,9 -> 1,2,3,A,B (left and right)
122
123
	if (edgeOp(c5, c8)) pattern |= 0x0000'0080;  // edge: 8 (left)
124
	Pixel c6 = curr[size - 1];
125
	if (edgeOp(c6, c8)) pattern |= 0x0004'0800;  // edge: D (left), 7 (right)
126
	if (edgeOp(c5, c6)) pattern |= 0x0010'2000;  // edge: 6 (left), 4 (right)
127
	Pixel c9 = next[size - 1];
128
	if (edgeOp(c5, c9)) pattern |= 0x0008'1000;  // edge: 9 (left), C (right)
129
130
	if (edgeOp(c6, c9)) pattern |= 0x1880'0000;  // edges:      8,9,D (right)
131
132
	edges2[size2 - 1] = pattern;
133
}
134
135
} // namespace openmsx
136
137
#endif