18template<std::
unsigned_
integral Pixel>
25template<std::
unsigned_
integral Pixel>
27 FrameSource& src,
unsigned srcStartY,
unsigned srcEndY,
31 unsigned stopDstY = (dstEndY == dstHeight)
32 ? dstEndY : dstEndY - 3;
33 unsigned srcY = srcStartY, dstY = dstStartY;
34 for (; dstY < stopDstY; srcY += 1, dstY += 3) {
40 if (dstY != dstHeight) {
43 assert(nextLineWidth != 1);
44 this->dispatchScale(src, srcY, srcEndY, nextLineWidth,
49template<std::
unsigned_
integral Pixel>
52 return pixelOps.template blend<1, 1>(p1, p2);
55static constexpr unsigned redBlueMask = 0xF81F;
56static constexpr unsigned greenMask = 0x7E0;
59template<std::
unsigned_
integral Pixel>
60[[nodiscard]]
static Pixel bilinear(
unsigned a,
unsigned b,
unsigned x);
64 if (a == b)
return narrow_cast<uint16_t>(a);
66 const unsigned areaB = x >> 11;
67 const unsigned areaA = 0x20 - areaB;
69 a = (a & redBlueMask) | ((a & greenMask) << 16);
70 b = (b & redBlueMask) | ((b & greenMask) << 16);
71 const unsigned result = ((areaA * a) + (areaB * b)) >> 5;
72 return (result & redBlueMask) | ((result >> 16) & greenMask);
79 const unsigned areaB = x >> 8;
80 const unsigned areaA = 0x100 - areaB;
82 const unsigned result0 =
83 ((a & 0x00FF00FF) * areaA + (b & 0x00FF00FF) * areaB) >> 8;
84 const unsigned result1 =
85 ((a & 0xFF00FF00) >> 8) * areaA + ((b & 0xFF00FF00) >> 8) * areaB;
86 return (result0 & 0x00FF00FF) | (result1 & 0xFF00FF00);
90template<std::
unsigned_
integral Pixel> [[nodiscard]]
static Pixel bilinear4(
91 unsigned a,
unsigned b,
unsigned c,
unsigned d,
unsigned x,
unsigned y);
94 unsigned a,
unsigned b,
unsigned c,
unsigned d,
unsigned x,
unsigned y)
98 const unsigned xy = (x * y) >> 5;
100 const unsigned areaA = 0x20 + xy - x - y;
101 const unsigned areaB = x - xy;
102 const unsigned areaC = y - xy;
103 const unsigned areaD = xy;
105 a = (a & redBlueMask) | ((a & greenMask) << 16);
106 b = (b & redBlueMask) | ((b & greenMask) << 16);
107 c = (c & redBlueMask) | ((c & greenMask) << 16);
108 d = (d & redBlueMask) | ((d & greenMask) << 16);
110 (areaA * a) + (areaB * b) + (areaC * c) + (areaD * d)
112 return (result & redBlueMask) | ((result >> 16) & greenMask);
116 unsigned a,
unsigned b,
unsigned c,
unsigned d,
unsigned x,
unsigned y)
120 const unsigned xy = (x * y) >> 8;
122 const unsigned areaA = (1 << 8) + xy - x - y;
123 const unsigned areaB = x - xy;
124 const unsigned areaC = y - xy;
125 const unsigned areaD = xy;
127 const unsigned result0 =
128 ((a & 0x00FF00FF) * areaA + (b & 0x00FF00FF) * areaB +
129 (c & 0x00FF00FF) * areaC + (d & 0x00FF00FF) * areaD) >> 8;
130 const unsigned result1 =
131 ((a & 0xFF00FF00) >> 8) * areaA + ((b & 0xFF00FF00) >> 8) * areaB +
132 ((c & 0xFF00FF00) >> 8) * areaC + ((d & 0xFF00FF00) >> 8) * areaD;
133 return (result0 & 0x00FF00FF) | (result1 & 0xFF00FF00);
136template<std::
unsigned_
integral Pixel>
141 [[nodiscard]]
inline static Pixel blend(
unsigned a,
unsigned b);
143 template<
unsigned x,
unsigned y>
144 [[nodiscard]]
inline static Pixel blend(
unsigned a,
unsigned b,
unsigned c,
unsigned d);
147template<
unsigned X,
unsigned OLD,
unsigned NEW>
149 static_assert(OLD > NEW);
151 (X >> (OLD - NEW)) + ((X >> (OLD - NEW - 1)) & 1);
154template<std::
unsigned_
integral Pixel>
158 if (a == b)
return narrow_cast<Pixel>(a);
160 const unsigned bits = (
sizeof(
Pixel) == 2) ? 5 : 8;
162 const unsigned areaA = (1 << bits) - areaB;
164 if constexpr (
sizeof(
Pixel) == 2) {
165 a = (a & redBlueMask) | ((a & greenMask) << 16);
166 b = (b & redBlueMask) | ((b & greenMask) << 16);
167 const unsigned result = ((areaA * a) + (areaB * b)) >> bits;
168 return (result & redBlueMask) | ((result >> 16) & greenMask);
170 const unsigned result0 =
171 ((a & 0x00FF00FF) * areaA +
172 (b & 0x00FF00FF) * areaB) >> bits;
173 const unsigned result1 =
174 ((a & 0xFF00FF00) >> bits) * areaA +
175 ((b & 0xFF00FF00) >> bits) * areaB;
176 return (result0 & 0x00FF00FF) | (result1 & 0xFF00FF00);
180template<std::
unsigned_
integral Pixel>
181template<
unsigned wx,
unsigned wy>
183 unsigned a,
unsigned b,
unsigned c,
unsigned d)
185 const unsigned bits = (
sizeof(
Pixel) == 2) ? 5 : 8;
186 const unsigned xy = (wx * wy) >> 16;
187 const unsigned areaB =
Round<wx - xy, 16, bits>::result;
188 const unsigned areaC =
Round<wy - xy, 16, bits>::result;
190 const unsigned areaA = (1 << bits) - areaB - areaC - areaD;
192 if constexpr (
sizeof(
Pixel) == 2) {
193 a = (a & redBlueMask) | ((a & greenMask) << 16);
194 b = (b & redBlueMask) | ((b & greenMask) << 16);
195 c = (c & redBlueMask) | ((c & greenMask) << 16);
196 d = (d & redBlueMask) | ((d & greenMask) << 16);
198 (areaA * a) + (areaB * b) + (areaC * c) + (areaD * d)
200 return (result & redBlueMask) | ((result >> 16) & greenMask);
202 const unsigned result0 =
203 ((a & 0x00FF00FF) * areaA +
204 (b & 0x00FF00FF) * areaB +
205 (c & 0x00FF00FF) * areaC +
206 (d & 0x00FF00FF) * areaD) >> bits;
207 const unsigned result1 =
208 ((a & 0xFF00FF00) >> bits) * areaA +
209 ((b & 0xFF00FF00) >> bits) * areaB +
210 ((c & 0xFF00FF00) >> bits) * areaC +
211 ((d & 0xFF00FF00) >> bits) * areaD;
212 return (result0 & 0x00FF00FF) | (result1 & 0xFF00FF00);
220 template<std::
unsigned_
integral Pixel>
226 template<
unsigned NX,
unsigned y, std::
unsigned_
integral Pixel>
229 unsigned sa,
unsigned sb,
unsigned sc,
unsigned sd,
230 unsigned se,
unsigned sg,
unsigned sj,
unsigned sl)
233 const unsigned x1 = ((NX - i) << 16) / NX;
234 const unsigned y1 = y;
235 const unsigned f1 = (x1 >> 1) + (0x10000 >> 2);
236 const unsigned f2 = (y1 >> 1) + (0x10000 >> 2);
237 if (y1 <= f1 && sa == sj && sa != se) {
239 }
else if (y1 >= f1 && sa == sg && sa != sl) {
241 }
else if (x1 >= f2 && sa == se && sa != sj) {
243 }
else if (x1 <= f2 && sa == sl && sa != sg) {
245 }
else if (y1 >= x1) {
247 }
else if (y1 <= x1) {
251 dp, sa, sb, sc, sd, se, sg, sj, sl);
254 template<
unsigned NX,
unsigned y, std::
unsigned_
integral Pixel>
257 unsigned sa,
unsigned sb,
unsigned sc,
unsigned sd,
258 unsigned sf,
unsigned sh,
unsigned si,
unsigned sk)
261 const unsigned x1 = ((NX - i) << 16) / NX;
262 const unsigned x2 = 0x10000 - x1;
263 const unsigned y1 = y;
264 const unsigned y2 = 0x10000 - y1;
265 const unsigned f1 = (x1 >> 1) + (0x10000 >> 2);
266 const unsigned f2 = (y1 >> 1) + (0x10000 >> 2);
267 if (y2 >= f1 && sb == sh && sb != sf) {
269 }
else if (y2 <= f1 && sb == si && sb != sk) {
271 }
else if (x2 >= f2 && sb == sf && sb != sh) {
273 }
else if (x2 <= f2 && sb == sk && sb != si) {
275 }
else if (y2 >= x1) {
277 }
else if (y2 <= x1) {
281 dp, sa, sb, sc, sd, sf, sh, si, sk);
284 template<
unsigned NX,
unsigned y, std::
unsigned_
integral Pixel>
286 Pixel*& dp,
unsigned sa,
unsigned sb,
unsigned sc,
unsigned sd)
288 const unsigned x = ((NX - i) << 16) / NX;
297 template<std::
unsigned_
integral Pixel>
300 template<
unsigned NX,
unsigned y, std::
unsigned_
integral Pixel>
302 Pixel*& ,
unsigned ,
unsigned ,
303 unsigned ,
unsigned ,
unsigned ,
304 unsigned ,
unsigned ,
unsigned ) { }
306 template<
unsigned NX,
unsigned y, std::
unsigned_
integral Pixel>
308 Pixel*& ,
unsigned ,
unsigned ,
309 unsigned ,
unsigned ,
unsigned ,
310 unsigned ,
unsigned ,
unsigned ) { }
312 template<
unsigned NX,
unsigned y, std::
unsigned_
integral Pixel>
314 unsigned ,
unsigned ,
unsigned ) { }
321 template<
unsigned NX,
unsigned NY, std::
unsigned_
integral Pixel>
323 std::span<const Pixel> src0, std::span<const Pixel> src1,
324 std::span<const Pixel> src2, std::span<const Pixel> src3,
327 auto srcWidth = src0.size();
328 assert(src0.size() == srcWidth);
329 assert(src1.size() == srcWidth);
330 assert(src2.size() == srcWidth);
331 assert(src3.size() == srcWidth);
334 auto* dp = dstLine.data();
336 static constexpr unsigned y1 = ((NY - i) << 16) / NY;
344 const auto pos0 = pos1;
347 pos3 =
std::min(pos1 + 3, srcWidth) - 1;
355 if (sa == sb && sc == sd && sa == sc) {
358 }
else if (sa == sd && sb != sc) {
361 dp, sa, sb, sc, sd, src0[pos1], src1[pos0], src2[pos3], src3[pos2]);
362 }
else if (sb == sc && sa != sd) {
365 dp, sa, sb, sc, sd, src0[pos2], src2[pos0], src1[pos3], src3[pos1]);
375 LineRepeater<i - 1>::template scaleFixedLine<NX, NY, Pixel>(
376 src0, src1, src2, src3, dst, dstY);
383 template<
unsigned NX,
unsigned NY, std::
unsigned_
integral Pixel>
385 std::span<const Pixel> , std::span<const Pixel> ,
386 std::span<const Pixel> , std::span<const Pixel> ,
391template<std::
unsigned_
integral Pixel>
392template<
unsigned NX,
unsigned NY>
393void SaI3xScaler<Pixel>::scaleFixed(FrameSource& src,
394 unsigned srcStartY,
unsigned ,
unsigned srcWidth,
395 ScalerOutput<Pixel>& dst,
unsigned dstStartY,
unsigned dstEndY)
397 assert(dst.getWidth() == srcWidth * NX);
398 assert(dst.getHeight() == src.getHeight() * NY);
405 auto srcY = narrow<int>(srcStartY);
406 auto src0 = src.getLine(srcY - 1, buf0);
407 auto src1 = src.getLine(srcY + 0, buf1);
408 auto src2 = src.getLine(srcY + 1, buf2);
410 auto dstY = dstStartY;
411 while (dstY < dstEndY) {
412 auto src3 = src.getLine(srcY + 2, buf3);
413 LineRepeater<NY>::template scaleFixedLine<NX, NY, Pixel>(
414 src0, src1, src2, src3, dst, dstY);
425template<std::
unsigned_
integral Pixel>
426void SaI3xScaler<Pixel>::scaleAny(FrameSource& src,
427 unsigned srcStartY,
unsigned ,
unsigned srcWidth,
428 ScalerOutput<Pixel>& dst,
unsigned dstStartY,
unsigned dstEndY)
431 const unsigned wFinish = (srcWidth - 1) << 16;
432 const unsigned dw = wFinish / (dst.getWidth() - 1);
433 const unsigned hFinish = (src.getHeight() - 1) << 16;
434 const unsigned dh = hFinish / (dst.getHeight() - 1);
442 for (
auto dstY :
xrange(dstStartY, dstEndY)) {
444 auto line = narrow<int>(srcStartY + (h >> 16));
446 auto src0 = src.getLine(line - 1, buf0);
447 auto src1 = src.getLine(line + 0, buf1);
448 auto src2 = src.getLine(line + 1, buf2);
449 auto src3 = src.getLine(line + 2, buf3);
452 auto dstLine = dst.acquireLine(dstY);
453 auto* dp = dstLine.data();
456 const unsigned y1 = h & 0xffff;
457 const unsigned y2 = 0x10000 - y1;
466 for (
unsigned w = 0; w < wFinish; ) {
467 const unsigned pos0 = pos1;
470 pos3 =
std::min(pos1 + 3, srcWidth) - 1;
478 if (
A ==
B &&
C ==
D &&
A ==
C) {
482 }
while ((w >> 16) == pos1);
483 }
else if (
A ==
D &&
B !=
C) {
486 const unsigned x1 = w & 0xffff;
487 const unsigned f1 = (x1 >> 1) + (0x10000 >> 2);
488 const unsigned f2 = (y1 >> 1) + (0x10000 >> 2);
490 if (y1 <= f1 &&
A == src2[pos3] &&
A != src0[pos1]) {
491 product1 = bilinear<Pixel>(
A,
B, f1 - y1);
492 }
else if (y1 >= f1 &&
A == src1[pos0] &&
A != src3[pos2]) {
493 product1 = bilinear<Pixel>(
A,
C, y1 - f1);
494 }
else if (x1 >= f2 &&
A == src0[pos1] &&
A != src2[pos3]) {
495 product1 = bilinear<Pixel>(
A,
B, x1 - f2);
496 }
else if (x1 <= f2 &&
A == src3[pos2] &&
A != src1[pos0]) {
497 product1 = bilinear<Pixel>(
A,
C, f2 - x1);
498 }
else if (y1 >= x1) {
499 product1 = bilinear<Pixel>(
A,
C, y1 - x1);
502 product1 = bilinear<Pixel>(
A,
B, x1 - y1);
506 }
while ((w >> 16) == pos1);
507 }
else if (
B ==
C &&
A !=
D) {
510 const unsigned x1 = w & 0xffff;
511 const unsigned x2 = 0x10000 - x1;
512 const unsigned f1 = (x1 >> 1) + (0x10000 >> 2);
513 const unsigned f2 = (y1 >> 1) + (0x10000 >> 2);
515 if (y2 >= f1 &&
B == src2[pos0] &&
B != src0[pos2]) {
516 product1 = bilinear<Pixel>(
B,
A, y2 - f1);
517 }
else if (y2 <= f1 &&
B == src1[pos3] &&
B != src3[pos1]) {
518 product1 = bilinear<Pixel>(
B,
D, f1 - y2);
519 }
else if (x2 >= f2 &&
B == src0[pos2] &&
B != src2[pos0]) {
520 product1 = bilinear<Pixel>(
B,
A, x2 - f2);
521 }
else if (x2 <= f2 &&
B == src3[pos1] &&
B != src1[pos3]) {
522 product1 = bilinear<Pixel>(
B,
D, f2 - x2);
523 }
else if (y2 >= x1) {
524 product1 = bilinear<Pixel>(
B,
A, y2 - x1);
527 product1 = bilinear<Pixel>(
B,
D, x1 - y2);
531 }
while ((w >> 16) == pos1);
535 const unsigned x1 = w & 0xffff;
536 *dp++ = bilinear4<Pixel>(
A,
B,
C,
D, x1, y1);
538 }
while ((w >> 16) == pos1);
541 dst.releaseLine(dstY, dstLine);
545template<std::
unsigned_
integral Pixel>
547 unsigned srcStartY,
unsigned srcEndY,
unsigned srcWidth,
550 scaleFixed<3, 3>(src, srcStartY, srcEndY, srcWidth, dst, dstStartY, dstEndY);
static Pixel blend(unsigned a, unsigned b)
Interface for getting lines from a video frame.
Pixel getLineColor(unsigned line) const
Get the (single) color of the given line.
virtual unsigned getLineWidth(unsigned line) const =0
Gets the number of display pixels on the given line.
static void scaleFixedLine(std::span< const Pixel >, std::span< const Pixel >, std::span< const Pixel >, std::span< const Pixel >, ScalerOutput< Pixel > &, unsigned &)
static void scaleFixedLine(std::span< const Pixel > src0, std::span< const Pixel > src1, std::span< const Pixel > src2, std::span< const Pixel > src3, ScalerOutput< Pixel > &dst, unsigned &dstY)
static void blendSlash(Pixel *&, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned)
static void blend4(Pixel *&, unsigned, unsigned, unsigned, unsigned)
static void fill(Pixel *&, unsigned)
static void blendBackslash(Pixel *&, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned)
static void blendBackslash(Pixel *&dp, unsigned sa, unsigned sb, unsigned sc, unsigned sd, unsigned se, unsigned sg, unsigned sj, unsigned sl)
static void fill(Pixel *&dp, unsigned sa)
static void blendSlash(Pixel *&dp, unsigned sa, unsigned sb, unsigned sc, unsigned sd, unsigned sf, unsigned sh, unsigned si, unsigned sk)
static void blend4(Pixel *&dp, unsigned sa, unsigned sb, unsigned sc, unsigned sd)
2xSaI algorithm: edge-detection which produces a rounded look.
void scale1x1to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
void scaleBlank1to3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
SaI3xScaler(const PixelOperations< Pixel > &pixelOps)
Base class for 3x scalers.
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)
This file implemented 3 utility functions:
uint16_t bilinear< uint16_t >(unsigned a, unsigned b, unsigned x)
uint32_t bilinear4< uint32_t >(unsigned a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned y)
std::array< const EDStorage, 4 > A
uint32_t bilinear< uint32_t >(unsigned a, unsigned b, unsigned x)
uint16_t bilinear4< uint16_t >(unsigned a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned y)
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
static constexpr unsigned result
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
constexpr auto xrange(T e)