16template<std::
unsigned_
integral Pixel>
24template<std::
unsigned_
integral Pixel>
27 unsigned srcStartY,
unsigned srcEndY,
unsigned srcWidth,
35 const unsigned zoomFactorX = dstWidth / srcWidth;
36 const unsigned zoomFactorY = (dstEndY - dstStartY) / (srcEndY - srcStartY);
37 const auto zoomFactorXF = narrow<float>(zoomFactorX);
38 const auto zoomFactorYF = narrow<float>(zoomFactorY);
45 const int srcNumLines = narrow<int>(srcEndY - srcStartY);
46 VLA(
const Pixel*, srcLinePtrsArray, srcNumLines + 2);
47 auto** srcLinePtrs = &srcLinePtrsArray[1];
48 std::vector<MemBuffer<Pixel, SSE_ALIGNMENT>> workBuffer;
49 const Pixel* linePtr =
nullptr;
50 Pixel* work =
nullptr;
51 for (
auto y :
xrange(-1, srcNumLines + 1)) {
52 if (linePtr == work) {
55 work = workBuffer.emplace_back(srcWidth).data();
57 auto line = src.
getLine(srcStartY + y, std::span{work, srcWidth});
58 linePtr = line.data();
59 srcLinePtrs[y] = linePtr;
62 enum { UP = 1 << 0, RIGHT = 1 << 1, DOWN = 1 << 2, LEFT = 1 << 3 };
64 uint8_t* edgeGenPtr = edges.
data();
65 for (
auto y :
xrange(srcNumLines)) {
66 auto* srcLinePtr = srcLinePtrs[y];
67 for (
auto x :
xrange(srcWidth)) {
68 Pixel colMid = srcLinePtr[x];
70 if (x > 0 && srcLinePtr[x - 1] != colMid) {
73 if (x < srcWidth - 1 && srcLinePtr[x + 1] != colMid) {
76 if (srcLinePtrs[y - 1][x] != colMid) {
79 if (srcLinePtrs[y + 1][x] != colMid) {
82 *edgeGenPtr++ = pixEdges;
96 SLOPE_TOP_LEFT = 1 << 27,
97 SLOPE_TOP_RIGHT = 1 << 26,
98 SLOPE_BOT_LEFT = 1 << 25,
99 SLOPE_BOT_RIGHT = 1 << 24,
105 SPAN_SHIFT_2 = SPAN_SHIFT_1 + SPAN_BITS,
106 SPAN_MASK = (1 << SPAN_BITS) - 1,
108 assert(srcWidth <= SPAN_MASK);
112 unsigned* horizontalGenPtr = horizontals.
data();
113 const uint8_t* edgePtr = edges.
data();
114 for (
auto y :
xrange(srcNumLines)) {
116 while (x < srcWidth) {
118 bool slopeTopLeft =
false;
119 bool slopeTopRight =
false;
120 bool slopeBotLeft =
false;
121 bool slopeBotRight =
false;
124 unsigned topEndX = x + 1;
132 if (edgePtr[x] & UP) {
133 while (topEndX < srcWidth
134 && (edgePtr[topEndX] & (UP | LEFT)) == UP) topEndX++;
135 slopeTopLeft = (edgePtr[x] & LEFT)
136 && srcLinePtrs[y + 1][x - 1] == srcLinePtrs[y][x]
137 && srcLinePtrs[y][x - 1] == srcLinePtrs[y - 1][x];
138 slopeTopRight = (edgePtr[topEndX - 1] & RIGHT)
139 && srcLinePtrs[y + 1][topEndX] == srcLinePtrs[y][topEndX - 1]
140 && srcLinePtrs[y][topEndX] == srcLinePtrs[y - 1][topEndX - 1];
144 unsigned botEndX = x + 1;
145 if (edgePtr[x] & DOWN) {
146 while (botEndX < srcWidth
147 && (edgePtr[botEndX] & (DOWN | LEFT)) == DOWN) botEndX++;
148 slopeBotLeft = (edgePtr[x] & LEFT)
149 && srcLinePtrs[y - 1][x - 1] == srcLinePtrs[y][x]
150 && srcLinePtrs[y][x - 1] == srcLinePtrs[y + 1][x];
151 slopeBotRight = (edgePtr[botEndX - 1] & RIGHT)
152 && srcLinePtrs[y - 1][botEndX] == srcLinePtrs[y][botEndX - 1]
153 && srcLinePtrs[y][botEndX] == srcLinePtrs[y + 1][botEndX - 1];
157 if (!(slopeTopLeft || slopeTopRight ||
158 slopeBotLeft || slopeBotRight)) {
159 *horizontalGenPtr++ = EDGE_NONE;
163 (slopeTopLeft ? SLOPE_TOP_LEFT : 0)
164 | (slopeTopRight ? SLOPE_TOP_RIGHT : 0)
165 | (slopeBotLeft ? SLOPE_BOT_LEFT : 0)
166 | (slopeBotRight ? SLOPE_BOT_RIGHT : 0);
167 const unsigned lengths =
168 ((topEndX - x) << SPAN_SHIFT_1) |
169 ((botEndX - x) << SPAN_SHIFT_2);
171 assert(!slopeTopRight || !slopeBotRight || topEndX == botEndX);
172 const unsigned endX = slopeTopRight ? topEndX : (
173 slopeBotRight ? botEndX :
std::max(topEndX, botEndX)
175 const unsigned length = endX - x;
177 *horizontalGenPtr++ = EDGE_START | EDGE_END | slopes | lengths;
179 *horizontalGenPtr++ = EDGE_START | slopes | lengths;
181 *horizontalGenPtr++ = EDGE_INNER | slopes | i;
183 *horizontalGenPtr++ = EDGE_END | slopes | lengths;
188 assert(x == srcWidth);
191 assert(
unsigned(edgePtr - edges.
data()) == srcNumLines * srcWidth);
192 assert(
unsigned(horizontalGenPtr - horizontals.
data()) == srcNumLines * srcWidth);
196 edgePtr = edges.
data();
197 for (
auto x :
xrange(srcWidth)) {
198 unsigned* verticalGenPtr = &verticals[x];
200 while (y < srcNumLines) {
202 bool slopeTopLeft =
false;
203 bool slopeTopRight =
false;
204 bool slopeBotLeft =
false;
205 bool slopeBotRight =
false;
208 int leftEndY = y + 1;
209 if (edgePtr[y *
size_t(srcWidth)] & LEFT) {
210 while (leftEndY < srcNumLines
211 && (edgePtr[leftEndY *
size_t(srcWidth)] & (LEFT | UP)) == LEFT) leftEndY++;
213 const unsigned nextX =
std::min(x + 1, srcWidth - 1);
214 slopeTopLeft = (edgePtr[y * size_t(srcWidth)] & UP)
215 && srcLinePtrs[y - 1][nextX] == srcLinePtrs[y][x]
216 && srcLinePtrs[y - 1][x] == srcLinePtrs[y][x - 1];
217 slopeBotLeft = (edgePtr[(leftEndY - 1) *
size_t(srcWidth)] & DOWN)
218 && srcLinePtrs[leftEndY][nextX] == srcLinePtrs[leftEndY - 1][x]
219 && srcLinePtrs[leftEndY][x] == srcLinePtrs[leftEndY - 1][x - 1];
223 int rightEndY = y + 1;
224 if (edgePtr[y *
size_t(srcWidth)] & RIGHT) {
225 while (rightEndY < srcNumLines
226 && (edgePtr[rightEndY *
size_t(srcWidth)] & (RIGHT | UP)) == RIGHT) rightEndY++;
227 assert(x < srcWidth);
228 const unsigned prevX = x == 0 ? 0 : x - 1;
229 slopeTopRight = (edgePtr[y * size_t(srcWidth)] & UP)
230 && srcLinePtrs[y - 1][prevX] == srcLinePtrs[y][x]
231 && srcLinePtrs[y - 1][x] == srcLinePtrs[y][x + 1];
232 slopeBotRight = (edgePtr[(rightEndY - 1) *
size_t(srcWidth)] & DOWN)
233 && srcLinePtrs[rightEndY][prevX] == srcLinePtrs[rightEndY - 1][x]
234 && srcLinePtrs[rightEndY][x] == srcLinePtrs[rightEndY - 1][x + 1];
238 if (!(slopeTopLeft || slopeTopRight ||
239 slopeBotLeft || slopeBotRight)) {
240 *verticalGenPtr = EDGE_NONE;
241 verticalGenPtr += srcWidth;
245 (slopeTopLeft ? SLOPE_TOP_LEFT : 0)
246 | (slopeTopRight ? SLOPE_TOP_RIGHT : 0)
247 | (slopeBotLeft ? SLOPE_BOT_LEFT : 0)
248 | (slopeBotRight ? SLOPE_BOT_RIGHT : 0);
249 const unsigned lengths =
250 ((leftEndY - y) << SPAN_SHIFT_1) |
251 ((rightEndY - y) << SPAN_SHIFT_2);
253 assert(!slopeBotLeft || !slopeBotRight || leftEndY == rightEndY);
254 const unsigned endY = slopeBotLeft ? leftEndY : (
255 slopeBotRight ? rightEndY :
std::max(leftEndY, rightEndY)
257 const unsigned length = endY - y;
259 *verticalGenPtr = EDGE_START | EDGE_END | slopes | lengths;
260 verticalGenPtr += srcWidth;
262 *verticalGenPtr = EDGE_START | slopes | lengths;
263 verticalGenPtr += srcWidth;
269 *verticalGenPtr = EDGE_INNER | slopes | i;
270 verticalGenPtr += srcWidth;
272 *verticalGenPtr = EDGE_END | slopes | lengths;
273 verticalGenPtr += srcWidth;
275 y = narrow<int>(endY);
278 assert(y == srcNumLines);
279 assert(
unsigned(verticalGenPtr - verticals.
data()) == x + srcNumLines * srcWidth);
282 assert(
unsigned(edgePtr - edges.
data()) == srcWidth);
285 for (
auto i :
xrange(dstStartY, dstEndY)) {
290 unsigned dstY = dstStartY;
291 for (
auto y :
xrange(srcNumLines)) {
292 auto* srcLinePtr = srcLinePtrs[y];
293 for (
auto x :
xrange(srcWidth)) {
294 Pixel col = srcLinePtr[x];
295 for (
auto iy :
xrange(zoomFactorY)) {
296 for (
auto ix :
xrange(zoomFactorX)) {
297 dstLines[dstY + iy][x * zoomFactorX + ix] = col;
305 const unsigned* horizontalPtr = horizontals.
data();
307 for (
auto y :
xrange(srcNumLines)) {
309 while (x < srcWidth) {
311 unsigned horzInfo = *horizontalPtr;
312 if ((horzInfo & EDGE_MASK) == EDGE_NONE) {
317 assert((horzInfo & EDGE_MASK) == EDGE_START);
320 bool slopeTopLeft = (horzInfo & SLOPE_TOP_LEFT ) != 0;
321 bool slopeTopRight = (horzInfo & SLOPE_TOP_RIGHT) != 0;
322 bool slopeBotLeft = (horzInfo & SLOPE_BOT_LEFT ) != 0;
323 bool slopeBotRight = (horzInfo & SLOPE_BOT_RIGHT) != 0;
324 const unsigned startX = x;
325 const unsigned topEndX =
326 startX + ((horzInfo >> SPAN_SHIFT_1) & SPAN_MASK);
327 const unsigned botEndX =
328 startX + ((horzInfo >> SPAN_SHIFT_2) & SPAN_MASK);
330 assert(!slopeTopRight || !slopeBotRight || topEndX == botEndX);
331 const unsigned endX = slopeTopRight ? topEndX : (
332 slopeBotRight ? botEndX :
std::max(topEndX, botEndX)
335 horizontalPtr += endX - startX;
339 if (slopeTopLeft && slopeBotLeft) {
340 slopeTopLeft = slopeBotLeft =
false;
342 if (slopeTopRight && slopeBotRight) {
343 slopeTopRight = slopeBotRight =
false;
347 auto* srcTopLinePtr = srcLinePtrs[y - 1];
348 auto* srcCurLinePtr = srcLinePtrs[y + 0];
349 auto* srcBotLinePtr = srcLinePtrs[y + 1];
350 const unsigned x0 = startX * 2 * zoomFactorX;
353 ? (startX + topEndX) * zoomFactorX
355 ? (startX + botEndX) * zoomFactorX
357 const unsigned x3 = endX * 2 * zoomFactorX;
360 ? (startX + topEndX) * zoomFactorX
362 ? (startX + botEndX) * zoomFactorX
364 for (
auto iy :
xrange(zoomFactorY)) {
365 auto iyF = narrow<float>(iy);
366 auto dstLinePtr = dstLines[dstY + iy];
369 bool blendTopLeft =
false;
370 bool blendTopRight =
false;
371 bool blendBotLeft =
false;
372 bool blendBotRight =
false;
373 if (iy * 2 < zoomFactorY) {
374 blendTopLeft = slopeTopLeft;
375 blendTopRight = slopeTopRight;
377 if (iy * 2 + 1 >= zoomFactorY) {
378 blendBotLeft = slopeBotLeft;
379 blendBotRight = slopeBotRight;
383 if (blendTopLeft || blendBotLeft) {
386 assert(!(blendTopLeft && blendBotLeft));
387 const Pixel* srcMixLinePtr = blendTopLeft
390 float lineY = blendTopLeft
391 ? ((zoomFactorYF - 1.0f - iyF) / zoomFactorYF)
392 : (iyF / zoomFactorYF);
393 for (
unsigned fx = x0 | 1; fx < x1; fx += 2) {
394 float rx = narrow<float>(fx - x0) / narrow<float>(x1 - x0);
395 float ry = 0.5f + rx * 0.5f;
396 float weight = (ry - lineY) * zoomFactorYF;
397 dstLinePtr[fx / 2] = pixelOps.lerp(
398 srcMixLinePtr[fx / (zoomFactorX * 2)],
399 srcCurLinePtr[fx / (zoomFactorX * 2)],
405 if (blendTopRight || blendBotRight) {
408 assert(!(blendTopRight && blendBotRight));
409 const Pixel* srcMixLinePtr = blendTopRight
412 float lineY = blendTopRight
413 ? ((zoomFactorYF - 1.0f - iyF) / zoomFactorYF)
414 : (iyF / zoomFactorYF);
418 for (
unsigned fx = x2 | 1; fx < x3; fx += 2) {
419 float rx = narrow<float>(fx - x2) / narrow<float>(x3 - x2);
420 float ry = 1.0f - rx * 0.5f;
421 float weight = (ry - lineY) * zoomFactorYF;
422 dstLinePtr[fx / 2] = pixelOps.lerp(
423 srcMixLinePtr[fx / (zoomFactorX * 2)],
424 srcCurLinePtr[fx / (zoomFactorX * 2)],
433 for (
unsigned fx = x0 | 1; fx < x1; fx += 2) {
435 pixelOps.combine256(255, 0, 0);
439 for (
unsigned fx = x2 | 1; fx < x3; fx += 2) {
441 pixelOps.combine256(0, 0, 255);
444 }
else if (iy == zoomFactorY - 1) {
446 for (
unsigned fx = x0 | 1; fx < x1; fx += 2) {
448 pixelOps.combine256(255, 255, 0);
452 for (
unsigned fx = x2 | 1; fx < x3; fx += 2) {
454 pixelOps.combine256(0, 255, 0);
461 assert(x == srcWidth);
464 assert(
unsigned(horizontalPtr - horizontals.
data()) == srcNumLines * srcWidth);
467 for (
auto x :
xrange(srcWidth)) {
468 const unsigned* verticalPtr = &verticals[x];
470 while (y < srcNumLines) {
472 unsigned vertInfo = *verticalPtr;
473 if ((vertInfo & EDGE_MASK) == EDGE_NONE) {
475 verticalPtr += srcWidth;
478 assert((vertInfo & EDGE_MASK) == EDGE_START);
481 bool slopeTopLeft = (vertInfo & SLOPE_TOP_LEFT ) != 0;
482 bool slopeTopRight = (vertInfo & SLOPE_TOP_RIGHT) != 0;
483 bool slopeBotLeft = (vertInfo & SLOPE_BOT_LEFT ) != 0;
484 bool slopeBotRight = (vertInfo & SLOPE_BOT_RIGHT) != 0;
485 const unsigned startY = y;
486 const unsigned leftEndY =
487 startY + ((vertInfo >> SPAN_SHIFT_1) & SPAN_MASK);
488 const unsigned rightEndY =
489 startY + ((vertInfo >> SPAN_SHIFT_2) & SPAN_MASK);
491 assert(!slopeBotLeft || !slopeBotRight || leftEndY == rightEndY);
492 const unsigned endY = slopeBotLeft ? leftEndY : (
493 slopeBotRight ? rightEndY :
std::max(leftEndY, rightEndY)
496 verticalPtr += size_t(srcWidth) * (endY - startY);
499 if (slopeTopLeft && slopeTopRight) {
500 slopeTopLeft = slopeTopRight =
false;
502 if (slopeBotLeft && slopeBotRight) {
503 slopeBotLeft = slopeBotRight =
false;
507 const unsigned leftX = x == 0 ? 0 : x - 1;
508 const unsigned curX = x;
509 const unsigned rightX =
std::min(x + 1, srcWidth - 1);
510 const unsigned y0 = startY * 2 * zoomFactorY;
513 ? (startY + leftEndY) * zoomFactorY
515 ? (startY + rightEndY) * zoomFactorY
517 const unsigned y3 = endY * 2 * zoomFactorY;
520 ? (startY + leftEndY) * zoomFactorY
522 ? (startY + rightEndY) * zoomFactorY
524 for (
auto ix :
xrange(zoomFactorX)) {
525 auto ixF = narrow<float>(ix);
526 const unsigned fx = x * zoomFactorX + ix;
529 bool blendTopLeft =
false;
530 bool blendTopRight =
false;
531 bool blendBotLeft =
false;
532 bool blendBotRight =
false;
533 if (ix * 2 < zoomFactorX) {
534 blendTopLeft = slopeTopLeft;
535 blendBotLeft = slopeBotLeft;
537 if (ix * 2 + 1 >= zoomFactorX) {
538 blendTopRight = slopeTopRight;
539 blendBotRight = slopeBotRight;
543 if (blendTopLeft || blendTopRight) {
544 assert(!(blendTopLeft && blendTopRight));
545 unsigned mixX = blendTopLeft ? leftX : rightX;
546 float lineX = blendTopLeft
547 ? ((zoomFactorXF - 1.0f - ixF) / zoomFactorXF)
548 : (ixF / zoomFactorXF);
549 for (
unsigned fy = y0 | 1; fy < y1; fy += 2) {
550 auto dstLinePtr = dstLines[dstStartY + fy / 2];
551 float ry = narrow<float>(fy - y0) / narrow<float>(y1 - y0);
552 float rx = 0.5f + ry * 0.5f;
553 float weight = (rx - lineX) * zoomFactorXF;
554 dstLinePtr[fx] = pixelOps.lerp(
555 srcLinePtrs[fy / (zoomFactorY * 2)][mixX],
556 srcLinePtrs[fy / (zoomFactorY * 2)][curX],
562 if (blendBotLeft || blendBotRight) {
563 assert(!(blendBotLeft && blendBotRight));
564 unsigned mixX = blendBotLeft ? leftX : rightX;
565 float lineX = blendBotLeft
566 ? ((zoomFactorXF - 1.0f - ixF) / zoomFactorXF)
567 : (ixF / zoomFactorXF);
568 for (
unsigned fy = y2 | 1; fy < y3; fy += 2) {
569 auto dstLinePtr = dstLines[dstStartY + fy / 2];
570 float ry = narrow<float>(fy - y2) / narrow<float>(y3 - y2);
571 float rx = 1.0f - ry * 0.5f;
572 float weight = (rx - lineX) * zoomFactorXF;
573 dstLinePtr[fx] = pixelOps.lerp(
574 srcLinePtrs[fy / (zoomFactorY * 2)][mixX],
575 srcLinePtrs[fy / (zoomFactorY * 2)][curX],
584 for (
unsigned fy = y0 | 1; fy < y1; fy += 2) {
585 auto dstLinePtr = dstLines[
588 pixelOps.combine256(255, 0, 0);
592 for (
unsigned fy = y2 | 1; fy < y3; fy += 2) {
593 auto dstLinePtr = dstLines[
596 pixelOps.combine256(255, 255, 0);
599 }
else if (ix == zoomFactorX - 1) {
601 for (
unsigned fy = y0 | 1; fy < y1; fy += 2) {
602 auto dstLinePtr = dstLines[
605 pixelOps.combine256(0, 0, 255);
609 for (
unsigned fy = y2 | 1; fy < y3; fy += 2) {
610 auto dstLinePtr = dstLines[
613 pixelOps.combine256(0, 255, 0);
620 assert(y == srcNumLines);
625 if (srcWidth * zoomFactorX != dstWidth) {
626 for (
auto dy :
xrange(dstStartY, dstY)) {
628 (dy - dstStartY) / zoomFactorY - srcStartY,
629 unsigned(srcNumLines)
631 Pixel col = srcLinePtrs[sy][srcWidth - 1];
632 for (
auto dx :
xrange(srcWidth * zoomFactorX, dstWidth)) {
633 dstLines[dy][dx] = col;
637 if (dstY != dstEndY) {
640 Pixel col = srcLinePtrs[srcNumLines - 1][srcWidth - 1];
641 for (
auto dy :
xrange(dstY, dstEndY)) {
642 for (
auto dx :
xrange(dstWidth)) {
643 dstLines[dy][dx] = col;
648 for (
auto i :
xrange(dstStartY, dstEndY)) {
Interface for getting lines from a video frame.
std::span< const Pixel > getLine(int line, std::span< Pixel > buf) const
Gets a pointer to the pixels of the given line number.
Scaler that uses a variation of the morphological anti-aliasing algorithm.
void scaleImage(FrameSource &src, const RawFrame *superImpose, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Scales the image in the given area, which must consist of lines which are all equally wide.
MLAAScaler(unsigned dstWidth, const PixelOperations< Pixel > &pixelOps)
const T * data() const
Returns pointer to the start of the memory buffer.
A video frame as output by the VDP scanline conversion unit, before any postprocessing filters are ap...
virtual unsigned getHeight() const =0
virtual void releaseLine(unsigned y, std::span< Pixel > buf)=0
virtual std::span< Pixel > acquireLine(unsigned y)=0
T length(const vecN< N, T > &x)
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
This file implemented 3 utility functions:
#define VLA(TYPE, NAME, LENGTH)
constexpr auto xrange(T e)