openMSX
HQ2xLiteScaler.cc
Go to the documentation of this file.
1/*
2 * Lightweight version of the hq2x scaler (http://www.hiend3d.com/hq2x.html)
3 *
4 * The difference between this version and the full version of hq2x is the
5 * calculation of the distance between two colors. Here it's simplified to
6 * just color1 == color2
7 * Because of this simplification a lot of the interpolation code can also
8 * be simplified.
9 * For low color images the result is very close to the full hq2x image but
10 * it is calculated _a_lot_ faster.
11 */
12
13#include "HQ2xLiteScaler.hh"
14#include "HQCommon.hh"
15#include "LineScalers.hh"
16#include "unreachable.hh"
17#include "build-info.hh"
18#include <cstdint>
19
20namespace openmsx {
21
22template<std::unsigned_integral Pixel> struct HQLite_1x1on2x2
23{
24 void operator()(std::span<const Pixel> in0, std::span<const Pixel> in1, std::span<const Pixel> in2,
25 std::span<Pixel> out0, std::span<Pixel> out1,
26 std::span<uint16_t> edgeBuf, EdgeHQLite edgeOp) __restrict;
27};
28
29template<std::unsigned_integral Pixel> struct HQLite_1x1on1x2
30{
31 void operator()(std::span<const Pixel> in0, std::span<const Pixel> in1, std::span<const Pixel> in2,
32 std::span<Pixel> out0, std::span<Pixel> out1,
33 std::span<uint16_t> edgeBuf, EdgeHQLite edgeOp) __restrict;
34};
35
36template<std::unsigned_integral Pixel>
38 std::span<const Pixel> in0, std::span<const Pixel> in1, std::span<const Pixel> in2,
39 std::span<Pixel> out0, std::span<Pixel> out1,
40 std::span<uint16_t> edgeBuf,
41 EdgeHQLite /*edgeOp*/) __restrict
42{
43 auto srcWidth = edgeBuf.size();
44 assert(in0.size() == srcWidth);
45 assert(in1.size() == srcWidth);
46 assert(in2.size() == srcWidth);
47 assert(out0.size() == 2 * srcWidth);
48 assert(out1.size() == 2 * srcWidth);
49
50 auto c2 = readPixel(in0[0]);
51 auto c5 = readPixel(in1[0]); auto c6 = c5;
52 auto c8 = readPixel(in2[0]); auto c9 = c8;
53
54 unsigned pattern = 0;
55 if (c5 != c8) pattern |= 3 << 6;
56 if (c5 != c2) pattern |= 3 << 9;
57
58 for (auto x : xrange(srcWidth)) {
59 auto c4 = c5;
60 c5 = c6;
61 c8 = c9;
62 if (x != srcWidth - 1) {
63 c6 = readPixel(in1[x + 1]);
64 c9 = readPixel(in2[x + 1]);
65 }
66
67 pattern = (pattern >> 6) & 0x001F; // left overlap
68 // overlaps with left
69 //if (c8 != c4) pattern |= 1 << 0; // B - l: c5-c9 6
70 //if (c5 != c7) pattern |= 1 << 1; // B - l: c6-c8 7
71 //if (c5 != c4) pattern |= 1 << 2; // l: c5-c6 8
72 // overlaps with top and left
73 //if (c5 != c1) pattern |= 1 << 3; // l: c2-c6 9, t: c4-c8 0
74 //if (c4 != c2) pattern |= 1 << 4; // l: c5-c3 10, t: c5-c7 1
75 // non-overlapping pixels
76 if (c5 != c8) pattern |= 1 << 5; // B
77 if (c5 != c9) pattern |= 1 << 6; // BR
78 if (c6 != c8) pattern |= 1 << 7; // BR
79 if (c5 != c6) pattern |= 1 << 8; // R
80 // overlaps with top
81 //if (c2 != c6) pattern |= 1 << 9; // R - t: c5-c9 6
82 //if (c5 != c3) pattern |= 1 << 10; // R - t: c6-c8 7
83 //if (c5 != c2) pattern |= 1 << 11; // t: c5-c8 5
84 pattern |= ((edgeBuf[x] & (1 << 5) ) << 6) |
85 ((edgeBuf[x] & ((1 << 6) | (1 << 7))) << 3);
86 edgeBuf[x] = narrow_cast<uint16_t>(pattern);
87
88 unsigned pixel0, pixel1, pixel2, pixel3;
89
90#include "HQ2xLiteScaler-1x1to2x2.nn"
91
92 out0[2 * x + 0] = writePixel<Pixel>(pixel0);
93 out0[2 * x + 1] = writePixel<Pixel>(pixel1);
94 out1[2 * x + 0] = writePixel<Pixel>(pixel2);
95 out1[2 * x + 1] = writePixel<Pixel>(pixel3);
96 }
97}
98
99template<std::unsigned_integral Pixel>
101 std::span<const Pixel> in0, std::span<const Pixel> in1, std::span<const Pixel> in2,
102 std::span<Pixel> out0, std::span<Pixel> out1,
103 std::span<uint16_t> edgeBuf,
104 EdgeHQLite /*edgeOp*/) __restrict
105{
106 // +---+---+---+
107 // | 1 | 2 | 3 |
108 // +---+---+---+
109 // | 4 | 5 | 6 |
110 // +---+---+---+
111 // | 7 | 8 | 9 |
112 // +---+---+---+
113 auto srcWidth = edgeBuf.size();
114 assert(in0.size() == srcWidth);
115 assert(in1.size() == srcWidth);
116 assert(in2.size() == srcWidth);
117 assert(out0.size() == srcWidth);
118 assert(out1.size() == srcWidth);
119
120 auto c2 = readPixel(in0[0]);
121 auto c5 = readPixel(in1[0]); auto c6 = c5;
122 auto c8 = readPixel(in2[0]); auto c9 = c8;
123
124 unsigned pattern = 0;
125 if (c5 != c8) pattern |= 3 << 6;
126 if (c5 != c2) pattern |= 3 << 9;
127
128 for (auto x : xrange(srcWidth)) {
129 auto c4 = c5;
130 c5 = c6;
131 c8 = c9;
132 if (x != srcWidth - 1) {
133 c6 = readPixel(in1[x + 1]);
134 c9 = readPixel(in2[x + 1]);
135 }
136
137 pattern = (pattern >> 6) & 0x001F; // left overlap
138 // overlaps with left
139 //if (c8 != c4) pattern |= 1 << 0; // B - l: c5-c9 6
140 //if (c5 != c7) pattern |= 1 << 1; // B - l: c6-c8 7
141 //if (c5 != c4) pattern |= 1 << 2; // l: c5-c6 8
142 // overlaps with top and left
143 //if (c5 != c1) pattern |= 1 << 3; // l: c2-c6 9, t: c4-c8 0
144 //if (c4 != c2) pattern |= 1 << 4; // l: c5-c3 10, t: c5-c7 1
145 // non-overlapping pixels
146 if (c5 != c8) pattern |= 1 << 5; // B
147 if (c5 != c9) pattern |= 1 << 6; // BR
148 if (c6 != c8) pattern |= 1 << 7; // BR
149 if (c5 != c6) pattern |= 1 << 8; // R
150 // overlaps with top
151 //if (c2 != c6) pattern |= 1 << 9; // R - t: c5-c9 6
152 //if (c5 != c3) pattern |= 1 << 10; // R - t: c6-c8 7
153 //if (c5 != c2) pattern |= 1 << 11; // t: c5-c8 5
154 pattern |= ((edgeBuf[x] & (1 << 5) ) << 6) |
155 ((edgeBuf[x] & ((1 << 6) | (1 << 7))) << 3);
156 edgeBuf[x] = narrow_cast<uint16_t>(pattern);
157
158 unsigned pixel0, pixel1;
159
160#include "HQ2xLiteScaler-1x1to1x2.nn"
161
162 out0[x] = writePixel<Pixel>(pixel0);
163 out1[x] = writePixel<Pixel>(pixel1);
164 }
165}
166
167
168
169template<std::unsigned_integral Pixel>
171 : Scaler2<Pixel>(pixelOps_)
172 , pixelOps(pixelOps_)
173{
174}
175
176template<std::unsigned_integral Pixel>
178 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
179 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
180{
181 PolyScale<Pixel, Scale_2on3<Pixel>> postScale(pixelOps);
182 EdgeHQLite edgeOp;
183 doHQScale2<Pixel>(HQLite_1x1on2x2<Pixel>(), edgeOp, postScale,
184 src, srcStartY, srcEndY, srcWidth,
185 dst, dstStartY, dstEndY);
186}
187
188template<std::unsigned_integral Pixel>
190 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
191 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
192{
194 EdgeHQLite edgeOp;
195 doHQScale2<Pixel>(HQLite_1x1on2x2<Pixel>(), edgeOp, postScale,
196 src, srcStartY, srcEndY, srcWidth,
197 dst, dstStartY, dstEndY);
198}
199
200template<std::unsigned_integral Pixel>
202 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
203 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
204{
205 PolyScale<Pixel, Scale_4on3<Pixel>> postScale(pixelOps);
206 EdgeHQLite edgeOp;
207 doHQScale2<Pixel>(HQLite_1x1on2x2<Pixel>(), edgeOp, postScale,
208 src, srcStartY, srcEndY, srcWidth,
209 dst, dstStartY, dstEndY);
210}
211
212template<std::unsigned_integral Pixel>
214 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
215 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
216{
218 EdgeHQLite edgeOp;
219 doHQScale2<Pixel>(HQLite_1x1on1x2<Pixel>(), edgeOp, postScale,
220 src, srcStartY, srcEndY, srcWidth,
221 dst, dstStartY, dstEndY);
222}
223
224template<std::unsigned_integral Pixel>
226 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
227 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
228{
229 PolyScale<Pixel, Scale_4on3<Pixel>> postScale(pixelOps);
230 EdgeHQLite edgeOp;
231 doHQScale2<Pixel>(HQLite_1x1on1x2<Pixel>(), edgeOp, postScale,
232 src, srcStartY, srcEndY, srcWidth,
233 dst, dstStartY, dstEndY);
234}
235
236template<std::unsigned_integral Pixel>
238 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
239 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
240{
241 PolyScale<Pixel, Scale_2on1<Pixel>> postScale(pixelOps);
242 EdgeHQLite edgeOp;
243 doHQScale2<Pixel>(HQLite_1x1on1x2<Pixel>(), edgeOp, postScale,
244 src, srcStartY, srcEndY, srcWidth,
245 dst, dstStartY, dstEndY);
246}
247
248// Force template instantiation.
249#if HAVE_16BPP
250template class HQ2xLiteScaler<uint16_t>;
251#endif
252#if HAVE_32BPP
253template class HQ2xLiteScaler<uint32_t>;
254#endif
255
256} // namespace openmsx
Interface for getting lines from a video frame.
Definition: FrameSource.hh:20
void scale2x1to3x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale2x1to1x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale1x1to3x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
HQ2xLiteScaler(const PixelOperations< Pixel > &pixelOps)
void scale4x1to3x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale1x1to2x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scale1x1to1x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Polymorphic wrapper around another line scaler.
Definition: LineScalers.hh:313
Base class for 2x scalers.
Definition: Scaler2.hh:12
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint32_t Pixel
uint32_t readPixel(Pixel p)
Definition: HQCommon.hh:19
void operator()(std::span< const Pixel > in0, std::span< const Pixel > in1, std::span< const Pixel > in2, std::span< Pixel > out0, std::span< Pixel > out1, std::span< uint16_t > edgeBuf, EdgeHQLite edgeOp)
void operator()(std::span< const Pixel > in0, std::span< const Pixel > in1, std::span< const Pixel > in2, std::span< Pixel > out0, std::span< Pixel > out1, std::span< uint16_t > edgeBuf, EdgeHQLite edgeOp)
constexpr auto xrange(T e)
Definition: xrange.hh:132