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