openMSX
SaI2xScaler.cc
Go to the documentation of this file.
1// 2xSaI is Copyright (c) 1999-2001 by Derek Liauw Kie Fa.
2// http://elektron.its.tudelft.nl/~dalikifa/
3// 2xSaI is free under GPL.
4//
5// Modified for use in openMSX by Maarten ter Huurne.
6
7#include "SaI2xScaler.hh"
8#include "FrameSource.hh"
9#include "ScalerOutput.hh"
10#include "narrow.hh"
11#include "vla.hh"
12#include "xrange.hh"
13#include "build-info.hh"
14#include <cassert>
15#include <cstdint>
16
17namespace openmsx {
18
19template<std::unsigned_integral Pixel>
21 : Scaler2<Pixel>(pixelOps_)
22 , pixelOps(pixelOps_)
23{
24}
25
26template<std::unsigned_integral Pixel>
28 FrameSource& src, unsigned srcStartY, unsigned srcEndY,
29 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
30{
31 unsigned dstHeight = dst.getHeight();
32 unsigned stopDstY = (dstEndY == dstHeight)
33 ? dstEndY : dstEndY - 2;
34 unsigned srcY = srcStartY, dstY = dstStartY;
35 for (/* */; dstY < stopDstY; srcY += 1, dstY += 2) {
36 auto color = src.getLineColor<Pixel>(srcY);
37 dst.fillLine(dstY + 0, color);
38 dst.fillLine(dstY + 1, color);
39 }
40 if (dstY != dstHeight) {
41 unsigned nextLineWidth = src.getLineWidth(srcY + 1);
42 assert(src.getLineWidth(srcY) == 1);
43 assert(nextLineWidth != 1);
44 this->dispatchScale(src, srcY, srcEndY, nextLineWidth,
45 dst, dstY, dstEndY);
46 }
47}
48
49template<std::unsigned_integral Pixel>
50inline Pixel SaI2xScaler<Pixel>::blend(Pixel p1, Pixel p2) const
51{
52 return pixelOps.template blend<1, 1>(p1, p2);
53}
54
55template<std::unsigned_integral Pixel>
56void SaI2xScaler<Pixel>::scaleLine1on2(
57 std::span<const Pixel> srcLine0, std::span<const Pixel> srcLine1,
58 std::span<const Pixel> srcLine2, std::span<const Pixel> srcLine3,
59 std::span<Pixel> dstUpper, std::span<Pixel> dstLower)
60{
61 auto srcWidth = srcLine0.size();
62 assert(srcLine0.size() == srcWidth);
63 assert(srcLine1.size() == srcWidth);
64 assert(srcLine2.size() == srcWidth);
65 assert(srcLine3.size() == srcWidth);
66 assert(dstUpper.size() == 2 * srcWidth);
67 assert(dstLower.size() == 2 * srcWidth);
68
69 // TODO: Scale border pixels as well.
70 for (auto x : xrange(srcWidth)) {
71 // Map of the pixels:
72 // I|E F|J
73 // G|A B|K
74 // H|C D|L
75 // M|N O|P
76
77 auto xl = x ? x - 1 : 0;
78 auto xr = std::min(x + 1, srcWidth - 1);
79 auto xrr = std::min(x + 2, srcWidth - 1);
80
81 // TODO: Possible performance improvements:
82 // - Play with order of fetching (effect on data cache).
83 // - Try not fetching at all (using srcLineN[x] in algorithm).
84 // - Try rotating the fetched colors (either in vars or in array).
85 Pixel colorI = srcLine0[xl];
86 Pixel colorE = srcLine0[x];
87 Pixel colorF = srcLine0[xr];
88 Pixel colorJ = srcLine0[xrr];
89
90 Pixel colorG = srcLine1[xl];
91 Pixel colorA = srcLine1[x];
92 Pixel colorB = srcLine1[xr];
93 Pixel colorK = srcLine1[xrr];
94
95 Pixel colorH = srcLine2[xl];
96 Pixel colorC = srcLine2[x];
97 Pixel colorD = srcLine2[xr];
98 Pixel colorL = srcLine2[xrr];
99
100 Pixel colorM = srcLine3[xl];
101 Pixel colorN = srcLine3[x];
102 Pixel colorO = srcLine3[xr];
103 //Pixel colorP = srcLine3[xrr];
104
105 Pixel product, product1, product2;
106 if (colorA == colorD && colorB != colorC) {
107 product = ((colorA == colorE && colorB == colorL) ||
108 (colorA == colorC && colorA == colorF &&
109 colorB != colorE && colorB == colorJ))
110 ? colorA
111 : blend(colorA, colorB);
112 product1 = ((colorA == colorG && colorC == colorO) ||
113 (colorA == colorB && colorA == colorH &&
114 colorG != colorC && colorC == colorM))
115 ? colorA
116 : blend(colorA, colorC);
117 product2 = colorA;
118 } else if (colorB == colorC && colorA != colorD) {
119 product = ((colorB == colorF && colorA == colorH) ||
120 (colorB == colorE && colorB == colorD &&
121 colorA != colorF && colorA == colorI))
122 ? colorB
123 : blend(colorA, colorB);
124 product1 = ((colorC == colorH && colorA == colorF) ||
125 (colorC == colorG && colorC == colorD &&
126 colorA != colorH && colorA == colorI))
127 ? colorC
128 : blend(colorA, colorC);
129 product2 = colorB;
130 } else if (colorA == colorD && colorB == colorC) {
131 if (colorA == colorB) {
132 product = product1 = product2 = colorA;
133 } else {
134 int r = 0;
135 if (colorE == colorG) {
136 if (colorA == colorE) r--;
137 else if (colorB == colorE) r++;
138 }
139 if (colorF == colorK) {
140 if (colorA == colorF) r--;
141 else if (colorB == colorF) r++;
142 }
143 if (colorH == colorN) {
144 if (colorA == colorH) r--;
145 else if (colorB == colorH) r++;
146 }
147 if (colorL == colorO) {
148 if (colorA == colorL) r--;
149 else if (colorB == colorL) r++;
150 }
151 product = product1 = blend(colorA, colorB);
152 product2 = r > 0 ? colorA : (r < 0 ? colorB : product);
153 }
154 } else {
155 product = (colorA == colorC && colorA == colorF &&
156 colorB != colorE && colorB == colorJ)
157 ? colorA
158 : ((colorB == colorE && colorB == colorD &&
159 colorA != colorF && colorA == colorI)
160 ? colorB
161 : blend(colorA, colorB));
162 product1 = (colorA == colorB && colorA == colorH &&
163 colorG != colorC && colorC == colorM)
164 ? colorA
165 : ((colorC == colorG && colorC == colorD &&
166 colorA != colorH && colorA == colorI)
167 ? colorC
168 : blend(colorA, colorC));
169 product2 = blend( // TODO: Quad-blend may be better?
170 blend(colorA, colorB),
171 blend(colorC, colorD));
172 }
173
174 dstUpper[x * 2 + 0] = colorA;
175 dstUpper[x * 2 + 1] = product;
176 dstLower[x * 2 + 0] = product1;
177 dstLower[x * 2 + 1] = product2;
178 }
179}
180
181template<std::unsigned_integral Pixel>
182void SaI2xScaler<Pixel>::scaleLine1on1(
183 std::span<const Pixel> srcLine0, std::span<const Pixel> srcLine1,
184 std::span<const Pixel> srcLine2, std::span<const Pixel> srcLine3,
185 std::span<Pixel> dstUpper, std::span<Pixel> dstLower)
186{
187 auto srcWidth = srcLine0.size();
188 assert(srcLine0.size() == srcWidth);
189 assert(srcLine1.size() == srcWidth);
190 assert(srcLine2.size() == srcWidth);
191 assert(srcLine3.size() == srcWidth);
192 assert(dstUpper.size() == srcWidth);
193 assert(dstLower.size() == srcWidth);
194
195 // Apply 2xSaI and keep the bottom-left pixel.
196 // It's not great, but at least it looks better than doubling the pixel
197 // like SimpleScaler does.
198 dstUpper[0] = srcLine1[0];
199 dstLower[0] = blend(srcLine1[0], srcLine2[0]);
200 for (auto x : xrange(1u, srcWidth - 1)) {
201 // Map of the pixels:
202 // I E F
203 // G A B
204 // H C D
205 // M N O
206
207 Pixel colorI = srcLine0[x - 1];
208 //Pixel colorE = srcLine0[x];
209 Pixel colorF = srcLine0[x + 1];
210
211 Pixel colorG = srcLine1[x - 1];
212 Pixel colorA = srcLine1[x];
213 Pixel colorB = srcLine1[x + 1];
214
215 Pixel colorH = srcLine2[x - 1];
216 Pixel colorC = srcLine2[x];
217 Pixel colorD = srcLine2[x + 1];
218
219 Pixel colorM = srcLine3[x - 1];
220 //Pixel colorN = srcLine3[x];
221 Pixel colorO = srcLine3[x + 1];
222
223 Pixel product1;
224 if (colorA == colorD && colorB != colorC) {
225 product1 = ((colorA == colorG && colorC == colorO) ||
226 (colorA == colorB && colorA == colorH &&
227 colorG != colorC && colorC == colorM))
228 ? colorA
229 : blend(colorA, colorC);
230 } else if (colorB == colorC && colorA != colorD) {
231 product1 = ((colorC == colorH && colorA == colorF) ||
232 (colorC == colorG && colorC == colorD &&
233 colorA != colorH && colorA == colorI))
234 ? colorC
235 : blend(colorA, colorC);
236 } else if (colorA == colorD && colorB == colorC) {
237 product1 = (colorA == colorC)
238 ? colorA
239 : blend(colorA, colorC);
240 } else {
241 product1 = (colorA == colorB && colorA == colorH &&
242 colorG != colorC && colorC == colorM)
243 ? colorA
244 : ((colorC == colorG && colorC == colorD &&
245 colorA != colorH && colorA == colorI)
246 ? colorC
247 : blend(colorA, colorC));
248 }
249
250 dstUpper[x] = colorA;
251 dstLower[x] = product1;
252 }
253 dstUpper[srcWidth - 1] = srcLine1[srcWidth - 1];
254 dstLower[srcWidth - 1] =
255 blend(srcLine1[srcWidth - 1], srcLine2[srcWidth - 1]);
256}
257
258template<std::unsigned_integral Pixel>
260 unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
261 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
262{
263 assert(dst.getWidth() == srcWidth * 2);
264
265 VLA_SSE_ALIGNED(Pixel, buf0, srcWidth);
266 VLA_SSE_ALIGNED(Pixel, buf1, srcWidth);
267 VLA_SSE_ALIGNED(Pixel, buf2, srcWidth);
268 VLA_SSE_ALIGNED(Pixel, buf3, srcWidth);
269
270 auto srcY = narrow<int>(srcStartY);
271 auto srcLine0 = src.getLine(srcY - 1, buf0);
272 auto srcLine1 = src.getLine(srcY + 0, buf1);
273 auto srcLine2 = src.getLine(srcY + 1, buf2);
274
275 for (unsigned dstY = dstStartY; dstY < dstEndY; srcY += 1, dstY += 2) {
276 auto srcLine3 = src.getLine(srcY + 2, buf3);
277 auto dstUpper = dst.acquireLine(dstY + 0);
278 auto dstLower = dst.acquireLine(dstY + 1);
279 scaleLine1on2(srcLine0, srcLine1, srcLine2, srcLine3,
280 dstUpper, dstLower);
281 dst.releaseLine(dstY + 0, dstUpper);
282 dst.releaseLine(dstY + 1, dstLower);
283 srcLine0 = srcLine1;
284 srcLine1 = srcLine2;
285 srcLine2 = srcLine3;
286 std::swap(buf0, buf1);
287 std::swap(buf1, buf2);
288 std::swap(buf2, buf3);
289 }
290}
291
292template<std::unsigned_integral Pixel>
294 unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
295 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
296{
297 assert(dst.getWidth() == srcWidth);
298
299 VLA_SSE_ALIGNED(Pixel, buf0, srcWidth);
300 VLA_SSE_ALIGNED(Pixel, buf1, srcWidth);
301 VLA_SSE_ALIGNED(Pixel, buf2, srcWidth);
302 VLA_SSE_ALIGNED(Pixel, buf3, srcWidth);
303
304 auto srcY = narrow<int>(srcStartY);
305 auto srcLine0 = src.getLine(srcY - 1, buf0);
306 auto srcLine1 = src.getLine(srcY + 0, buf1);
307 auto srcLine2 = src.getLine(srcY + 1, buf2);
308
309 for (unsigned dstY = dstStartY; dstY < dstEndY; srcY += 1, dstY += 2) {
310 auto srcLine3 = src.getLine(srcY + 2, buf3);
311 auto dstUpper = dst.acquireLine(dstY + 0);
312 auto dstLower = dst.acquireLine(dstY + 1);
313 scaleLine1on1(srcLine0, srcLine1, srcLine2, srcLine3,
314 dstUpper, dstLower);
315 dst.releaseLine(dstY + 0, dstUpper);
316 dst.releaseLine(dstY + 1, dstLower);
317 srcLine0 = srcLine1;
318 srcLine1 = srcLine2;
319 srcLine2 = srcLine3;
320 std::swap(buf0, buf1);
321 std::swap(buf1, buf2);
322 std::swap(buf2, buf3);
323 }
324}
325
326
327// Force template instantiation.
328#if HAVE_16BPP
329template class SaI2xScaler<uint16_t>;
330#endif
331#if HAVE_32BPP
332template class SaI2xScaler<uint32_t>;
333#endif
334
335} // namespace openmsx
Interface for getting lines from a video frame.
Definition: FrameSource.hh:20
Pixel getLineColor(unsigned line) const
Get the (single) color of the given line.
Definition: FrameSource.hh:79
virtual unsigned getLineWidth(unsigned line) const =0
Gets the number of display pixels on the given line.
std::span< const Pixel > getLine(int line, std::span< Pixel > buf) const
Gets a pointer to the pixels of the given line number.
Definition: FrameSource.hh:96
2xSaI algorithm: edge-detection which produces a rounded look.
Definition: SaI2xScaler.hh:15
SaI2xScaler(const PixelOperations< Pixel > &pixelOps)
Definition: SaI2xScaler.cc:20
void scale1x1to2x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Definition: SaI2xScaler.cc:259
void scaleBlank1to2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Definition: SaI2xScaler.cc:27
void scale1x1to1x2(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Definition: SaI2xScaler.cc:293
Base class for 2x scalers.
Definition: Scaler2.hh:12
virtual unsigned getWidth() const =0
virtual unsigned getHeight() const =0
virtual void fillLine(unsigned y, Pixel color)=0
virtual void releaseLine(unsigned y, std::span< Pixel > buf)=0
virtual std::span< Pixel > acquireLine(unsigned y)=0
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
Definition: gl_vec.hh:267
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint32_t Pixel
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
Definition: MemBuffer.hh:202
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:50
constexpr auto xrange(T e)
Definition: xrange.hh:132