15 template<
typename Pixel>
23 template<
typename Pixel>
26 unsigned srcStartY,
unsigned srcEndY,
unsigned srcWidth,
34 const unsigned zoomFactorX = dstWidth / srcWidth;
35 const unsigned zoomFactorY = (dstEndY - dstStartY) / (srcEndY - srcStartY);
42 const int srcNumLines = srcEndY - srcStartY;
43 VLA(
const Pixel*, srcLinePtrsArray, srcNumLines + 2);
44 auto** srcLinePtrs = &srcLinePtrsArray[1];
45 std::vector<MemBuffer<Pixel, SSE_ALIGNMENT>> workBuffer;
46 const Pixel* line =
nullptr;
47 Pixel* work =
nullptr;
48 for (
auto y :
xrange(-1, srcNumLines + 1)) {
52 work = workBuffer.emplace_back(srcWidth).data();
54 line = src.
getLinePtr(srcStartY + y, srcWidth, work);
55 srcLinePtrs[y] = line;
58 enum { UP = 1 << 0, RIGHT = 1 << 1, DOWN = 1 << 2, LEFT = 1 << 3 };
60 uint8_t* edgeGenPtr = edges.
data();
61 for (
auto y :
xrange(srcNumLines)) {
62 auto* srcLinePtr = srcLinePtrs[y];
63 for (
auto x :
xrange(srcWidth)) {
64 Pixel colMid = srcLinePtr[
x];
66 if (
x > 0 && srcLinePtr[
x - 1] != colMid) {
69 if (
x < srcWidth - 1 && srcLinePtr[
x + 1] != colMid) {
72 if (srcLinePtrs[y - 1][
x] != colMid) {
75 if (srcLinePtrs[y + 1][
x] != colMid) {
78 *edgeGenPtr++ = pixEdges;
92 SLOPE_TOP_LEFT = 1 << 27,
93 SLOPE_TOP_RIGHT = 1 << 26,
94 SLOPE_BOT_LEFT = 1 << 25,
95 SLOPE_BOT_RIGHT = 1 << 24,
101 SPAN_SHIFT_2 = SPAN_SHIFT_1 + SPAN_BITS,
102 SPAN_MASK = (1 << SPAN_BITS) - 1,
104 assert(srcWidth <= SPAN_MASK);
108 unsigned* horizontalGenPtr = horizontals.
data();
109 const uint8_t* edgePtr = edges.
data();
110 for (
auto y :
xrange(srcNumLines)) {
112 while (
x < srcWidth) {
114 bool slopeTopLeft =
false;
115 bool slopeTopRight =
false;
116 bool slopeBotLeft =
false;
117 bool slopeBotRight =
false;
120 unsigned topEndX =
x + 1;
128 if (edgePtr[
x] & UP) {
129 while (topEndX < srcWidth
130 && (edgePtr[topEndX] & (UP | LEFT)) == UP) topEndX++;
131 slopeTopLeft = (edgePtr[
x] & LEFT)
132 && srcLinePtrs[y + 1][
x - 1] == srcLinePtrs[y][
x]
133 && srcLinePtrs[y][
x - 1] == srcLinePtrs[y - 1][
x];
134 slopeTopRight = (edgePtr[topEndX - 1] & RIGHT)
135 && srcLinePtrs[y + 1][topEndX] == srcLinePtrs[y][topEndX - 1]
136 && srcLinePtrs[y][topEndX] == srcLinePtrs[y - 1][topEndX - 1];
140 unsigned botEndX =
x + 1;
141 if (edgePtr[
x] & DOWN) {
142 while (botEndX < srcWidth
143 && (edgePtr[botEndX] & (DOWN | LEFT)) == DOWN) botEndX++;
144 slopeBotLeft = (edgePtr[
x] & LEFT)
145 && srcLinePtrs[y - 1][
x - 1] == srcLinePtrs[y][
x]
146 && srcLinePtrs[y][
x - 1] == srcLinePtrs[y + 1][
x];
147 slopeBotRight = (edgePtr[botEndX - 1] & RIGHT)
148 && srcLinePtrs[y - 1][botEndX] == srcLinePtrs[y][botEndX - 1]
149 && srcLinePtrs[y][botEndX] == srcLinePtrs[y + 1][botEndX - 1];
153 if (!(slopeTopLeft || slopeTopRight ||
154 slopeBotLeft || slopeBotRight)) {
155 *horizontalGenPtr++ = EDGE_NONE;
159 (slopeTopLeft ? SLOPE_TOP_LEFT : 0)
160 | (slopeTopRight ? SLOPE_TOP_RIGHT : 0)
161 | (slopeBotLeft ? SLOPE_BOT_LEFT : 0)
162 | (slopeBotRight ? SLOPE_BOT_RIGHT : 0);
163 const unsigned lengths =
164 ((topEndX -
x) << SPAN_SHIFT_1) |
165 ((botEndX -
x) << SPAN_SHIFT_2);
167 assert(!slopeTopRight || !slopeBotRight || topEndX == botEndX);
168 const unsigned endX = slopeTopRight ? topEndX : (
169 slopeBotRight ? botEndX :
std::max(topEndX, botEndX)
171 const unsigned length = endX -
x;
173 *horizontalGenPtr++ = EDGE_START | EDGE_END | slopes | lengths;
175 *horizontalGenPtr++ = EDGE_START | slopes | lengths;
177 *horizontalGenPtr++ = EDGE_INNER | slopes | i;
179 *horizontalGenPtr++ = EDGE_END | slopes | lengths;
184 assert(
x == srcWidth);
187 assert(
unsigned(edgePtr - edges.
data()) == srcNumLines * srcWidth);
188 assert(
unsigned(horizontalGenPtr - horizontals.
data()) == srcNumLines * srcWidth);
192 edgePtr = edges.
data();
193 for (
auto x :
xrange(srcWidth)) {
194 unsigned* verticalGenPtr = &verticals[
x];
196 while (y < srcNumLines) {
198 bool slopeTopLeft =
false;
199 bool slopeTopRight =
false;
200 bool slopeBotLeft =
false;
201 bool slopeBotRight =
false;
204 int leftEndY = y + 1;
205 if (edgePtr[y * srcWidth] & LEFT) {
206 while (leftEndY < srcNumLines
207 && (edgePtr[leftEndY * srcWidth] & (LEFT | UP)) == LEFT) leftEndY++;
209 const unsigned nextX =
std::min(
x + 1, srcWidth - 1);
210 slopeTopLeft = (edgePtr[y * srcWidth] & UP)
211 && srcLinePtrs[y - 1][nextX] == srcLinePtrs[y][
x]
212 && srcLinePtrs[y - 1][
x] == srcLinePtrs[y][
x - 1];
213 slopeBotLeft = (edgePtr[(leftEndY - 1) * srcWidth] & DOWN)
214 && srcLinePtrs[leftEndY][nextX] == srcLinePtrs[leftEndY - 1][
x]
215 && srcLinePtrs[leftEndY][
x] == srcLinePtrs[leftEndY - 1][
x - 1];
219 int rightEndY = y + 1;
220 if (edgePtr[y * srcWidth] & RIGHT) {
221 while (rightEndY < srcNumLines
222 && (edgePtr[rightEndY * srcWidth] & (RIGHT | UP)) == RIGHT) rightEndY++;
223 assert(
x < srcWidth);
224 const unsigned prevX =
x == 0 ? 0 :
x - 1;
225 slopeTopRight = (edgePtr[y * srcWidth] & UP)
226 && srcLinePtrs[y - 1][prevX] == srcLinePtrs[y][
x]
227 && srcLinePtrs[y - 1][
x] == srcLinePtrs[y][
x + 1];
228 slopeBotRight = (edgePtr[(rightEndY - 1) * srcWidth] & DOWN)
229 && srcLinePtrs[rightEndY][prevX] == srcLinePtrs[rightEndY - 1][
x]
230 && srcLinePtrs[rightEndY][
x] == srcLinePtrs[rightEndY - 1][
x + 1];
234 if (!(slopeTopLeft || slopeTopRight ||
235 slopeBotLeft || slopeBotRight)) {
236 *verticalGenPtr = EDGE_NONE;
237 verticalGenPtr += srcWidth;
241 (slopeTopLeft ? SLOPE_TOP_LEFT : 0)
242 | (slopeTopRight ? SLOPE_TOP_RIGHT : 0)
243 | (slopeBotLeft ? SLOPE_BOT_LEFT : 0)
244 | (slopeBotRight ? SLOPE_BOT_RIGHT : 0);
245 const unsigned lengths =
246 ((leftEndY - y) << SPAN_SHIFT_1) |
247 ((rightEndY - y) << SPAN_SHIFT_2);
249 assert(!slopeBotLeft || !slopeBotRight || leftEndY == rightEndY);
250 const unsigned endY = slopeBotLeft ? leftEndY : (
251 slopeBotRight ? rightEndY :
std::max(leftEndY, rightEndY)
253 const unsigned length = endY - y;
255 *verticalGenPtr = EDGE_START | EDGE_END | slopes | lengths;
256 verticalGenPtr += srcWidth;
258 *verticalGenPtr = EDGE_START | slopes | lengths;
259 verticalGenPtr += srcWidth;
265 *verticalGenPtr = EDGE_INNER | slopes | i;
266 verticalGenPtr += srcWidth;
268 *verticalGenPtr = EDGE_END | slopes | lengths;
269 verticalGenPtr += srcWidth;
274 assert(y == srcNumLines);
275 assert(
unsigned(verticalGenPtr - verticals.
data()) ==
x + srcNumLines * srcWidth);
278 assert(
unsigned(edgePtr - edges.
data()) == srcWidth);
281 for (
auto i :
xrange(dstStartY, dstEndY)) {
286 unsigned dstY = dstStartY;
287 for (
auto y :
xrange(srcNumLines)) {
288 auto* srcLinePtr = srcLinePtrs[y];
289 for (
auto x :
xrange(srcWidth)) {
290 Pixel col = srcLinePtr[
x];
291 for (
auto iy :
xrange(zoomFactorY)) {
292 for (
auto ix :
xrange(zoomFactorX)) {
293 dstLines[dstY + iy][
x * zoomFactorX + ix] = col;
301 const unsigned* horizontalPtr = horizontals.
data();
303 for (
auto y :
xrange(srcNumLines)) {
305 while (
x < srcWidth) {
307 unsigned horzInfo = *horizontalPtr;
308 if ((horzInfo & EDGE_MASK) == EDGE_NONE) {
313 assert((horzInfo & EDGE_MASK) == EDGE_START);
316 bool slopeTopLeft = (horzInfo & SLOPE_TOP_LEFT ) != 0;
317 bool slopeTopRight = (horzInfo & SLOPE_TOP_RIGHT) != 0;
318 bool slopeBotLeft = (horzInfo & SLOPE_BOT_LEFT ) != 0;
319 bool slopeBotRight = (horzInfo & SLOPE_BOT_RIGHT) != 0;
320 const unsigned startX =
x;
321 const unsigned topEndX =
322 startX + ((horzInfo >> SPAN_SHIFT_1) & SPAN_MASK);
323 const unsigned botEndX =
324 startX + ((horzInfo >> SPAN_SHIFT_2) & SPAN_MASK);
326 assert(!slopeTopRight || !slopeBotRight || topEndX == botEndX);
327 const unsigned endX = slopeTopRight ? topEndX : (
328 slopeBotRight ? botEndX :
std::max(topEndX, botEndX)
331 horizontalPtr += endX - startX;
335 if (slopeTopLeft && slopeBotLeft) {
336 slopeTopLeft = slopeBotLeft =
false;
338 if (slopeTopRight && slopeBotRight) {
339 slopeTopRight = slopeBotRight =
false;
343 auto* srcTopLinePtr = srcLinePtrs[y - 1];
344 auto* srcCurLinePtr = srcLinePtrs[y + 0];
345 auto* srcBotLinePtr = srcLinePtrs[y + 1];
346 const unsigned x0 = startX * 2 * zoomFactorX;
349 ? (startX + topEndX) * zoomFactorX
351 ? (startX + botEndX) * zoomFactorX
353 const unsigned x3 = endX * 2 * zoomFactorX;
356 ? (startX + topEndX) * zoomFactorX
358 ? (startX + botEndX) * zoomFactorX
360 for (
auto iy :
xrange(zoomFactorY)) {
361 auto* dstLinePtr = dstLines[dstY + iy];
364 bool blendTopLeft =
false;
365 bool blendTopRight =
false;
366 bool blendBotLeft =
false;
367 bool blendBotRight =
false;
368 if (iy * 2 < zoomFactorY) {
369 blendTopLeft = slopeTopLeft;
370 blendTopRight = slopeTopRight;
372 if (iy * 2 + 1 >= zoomFactorY) {
373 blendBotLeft = slopeBotLeft;
374 blendBotRight = slopeBotRight;
378 if (blendTopLeft || blendBotLeft) {
381 assert(!(blendTopLeft && blendBotLeft));
382 const Pixel* srcMixLinePtr = blendTopLeft
385 float lineY = blendTopLeft
386 ? ((zoomFactorY - 1 - iy) /
float(zoomFactorY))
387 : (iy /
float(zoomFactorY));
388 for (
unsigned fx = x0 | 1; fx < x1; fx += 2) {
389 float rx = (fx - x0) /
float(x1 - x0);
390 float ry = 0.5f + rx * 0.5f;
391 float weight = (ry - lineY) * zoomFactorY;
392 dstLinePtr[fx / 2] = pixelOps.lerp(
393 srcMixLinePtr[fx / (zoomFactorX * 2)],
394 srcCurLinePtr[fx / (zoomFactorX * 2)],
400 if (blendTopRight || blendBotRight) {
403 assert(!(blendTopRight && blendBotRight));
404 const Pixel* srcMixLinePtr = blendTopRight
407 float lineY = blendTopRight
408 ? ((zoomFactorY - 1 - iy) /
float(zoomFactorY))
409 : (iy /
float(zoomFactorY));
413 for (
unsigned fx = x2 | 1; fx < x3; fx += 2) {
414 float rx = (fx - x2) /
float(x3 - x2);
415 float ry = 1.0f - rx * 0.5f;
416 float weight = (ry - lineY) * zoomFactorY;
417 dstLinePtr[fx / 2] = pixelOps.lerp(
418 srcMixLinePtr[fx / (zoomFactorX * 2)],
419 srcCurLinePtr[fx / (zoomFactorX * 2)],
428 for (
unsigned fx = x0 | 1; fx < x1; fx += 2) {
430 pixelOps.combine256(255, 0, 0);
434 for (
unsigned fx = x2 | 1; fx < x3; fx += 2) {
436 pixelOps.combine256(0, 0, 255);
439 }
else if (iy == zoomFactorY - 1) {
441 for (
unsigned fx = x0 | 1; fx < x1; fx += 2) {
443 pixelOps.combine256(255, 255, 0);
447 for (
unsigned fx = x2 | 1; fx < x3; fx += 2) {
449 pixelOps.combine256(0, 255, 0);
456 assert(
x == srcWidth);
459 assert(
unsigned(horizontalPtr - horizontals.
data()) == srcNumLines * srcWidth);
462 for (
auto x :
xrange(srcWidth)) {
463 const unsigned* verticalPtr = &verticals[
x];
465 while (y < srcNumLines) {
467 unsigned vertInfo = *verticalPtr;
468 if ((vertInfo & EDGE_MASK) == EDGE_NONE) {
470 verticalPtr += srcWidth;
473 assert((vertInfo & EDGE_MASK) == EDGE_START);
476 bool slopeTopLeft = (vertInfo & SLOPE_TOP_LEFT ) != 0;
477 bool slopeTopRight = (vertInfo & SLOPE_TOP_RIGHT) != 0;
478 bool slopeBotLeft = (vertInfo & SLOPE_BOT_LEFT ) != 0;
479 bool slopeBotRight = (vertInfo & SLOPE_BOT_RIGHT) != 0;
480 const unsigned startY = y;
481 const unsigned leftEndY =
482 startY + ((vertInfo >> SPAN_SHIFT_1) & SPAN_MASK);
483 const unsigned rightEndY =
484 startY + ((vertInfo >> SPAN_SHIFT_2) & SPAN_MASK);
486 assert(!slopeBotLeft || !slopeBotRight || leftEndY == rightEndY);
487 const unsigned endY = slopeBotLeft ? leftEndY : (
488 slopeBotRight ? rightEndY :
std::max(leftEndY, rightEndY)
491 verticalPtr += srcWidth * (endY - startY);
494 if (slopeTopLeft && slopeTopRight) {
495 slopeTopLeft = slopeTopRight =
false;
497 if (slopeBotLeft && slopeBotRight) {
498 slopeBotLeft = slopeBotRight =
false;
502 const unsigned leftX =
x == 0 ? 0 :
x - 1;
503 const unsigned curX =
x;
504 const unsigned rightX =
std::min(
x + 1, srcWidth - 1);
505 const unsigned y0 = startY * 2 * zoomFactorY;
508 ? (startY + leftEndY) * zoomFactorY
510 ? (startY + rightEndY) * zoomFactorY
512 const unsigned y3 = endY * 2 * zoomFactorY;
515 ? (startY + leftEndY) * zoomFactorY
517 ? (startY + rightEndY) * zoomFactorY
519 for (
auto ix :
xrange(zoomFactorX)) {
520 const unsigned fx =
x * zoomFactorX + ix;
523 bool blendTopLeft =
false;
524 bool blendTopRight =
false;
525 bool blendBotLeft =
false;
526 bool blendBotRight =
false;
527 if (ix * 2 < zoomFactorX) {
528 blendTopLeft = slopeTopLeft;
529 blendBotLeft = slopeBotLeft;
531 if (ix * 2 + 1 >= zoomFactorX) {
532 blendTopRight = slopeTopRight;
533 blendBotRight = slopeBotRight;
537 if (blendTopLeft || blendTopRight) {
538 assert(!(blendTopLeft && blendTopRight));
539 unsigned mixX = blendTopLeft ? leftX : rightX;
540 float lineX = blendTopLeft
541 ? ((zoomFactorX - 1 - ix) /
float(zoomFactorX))
542 : (ix /
float(zoomFactorX));
543 for (
unsigned fy = y0 | 1; fy < y1; fy += 2) {
544 auto* dstLinePtr = dstLines[dstStartY + fy / 2];
545 float ry = (fy - y0) /
float(y1 - y0);
546 float rx = 0.5f + ry * 0.5f;
547 float weight = (rx - lineX) * zoomFactorX;
548 dstLinePtr[fx] = pixelOps.lerp(
549 srcLinePtrs[fy / (zoomFactorY * 2)][mixX],
550 srcLinePtrs[fy / (zoomFactorY * 2)][curX],
556 if (blendBotLeft || blendBotRight) {
557 assert(!(blendBotLeft && blendBotRight));
558 unsigned mixX = blendBotLeft ? leftX : rightX;
559 float lineX = blendBotLeft
560 ? ((zoomFactorX - 1 - ix) /
float(zoomFactorX))
561 : (ix /
float(zoomFactorX));
562 for (
unsigned fy = y2 | 1; fy < y3; fy += 2) {
563 auto* dstLinePtr = dstLines[dstStartY + fy / 2];
564 float ry = (fy - y2) /
float(y3 - y2);
565 float rx = 1.0f - ry * 0.5f;
566 float weight = (rx - lineX) * zoomFactorX;
567 dstLinePtr[fx] = pixelOps.lerp(
568 srcLinePtrs[fy / (zoomFactorY * 2)][mixX],
569 srcLinePtrs[fy / (zoomFactorY * 2)][curX],
578 for (
unsigned fy = y0 | 1; fy < y1; fy += 2) {
579 auto* dstLinePtr = dstLines[
582 pixelOps.combine256(255, 0, 0);
586 for (
unsigned fy = y2 | 1; fy < y3; fy += 2) {
587 auto* dstLinePtr = dstLines[
590 pixelOps.combine256(255, 255, 0);
593 }
else if (ix == zoomFactorX - 1) {
595 for (
unsigned fy = y0 | 1; fy < y1; fy += 2) {
596 auto* dstLinePtr = dstLines[
599 pixelOps.combine256(0, 0, 255);
603 for (
unsigned fy = y2 | 1; fy < y3; fy += 2) {
604 auto* dstLinePtr = dstLines[
607 pixelOps.combine256(0, 255, 0);
614 assert(y == srcNumLines);
619 if (srcWidth * zoomFactorX != dstWidth) {
620 for (
auto dy :
xrange(dstStartY, dstY)) {
622 (dy - dstStartY) / zoomFactorY - srcStartY,
623 unsigned(srcNumLines)
625 Pixel col = srcLinePtrs[sy][srcWidth - 1];
626 for (
auto dx :
xrange(srcWidth * zoomFactorX, dstWidth)) {
627 dstLines[dy][dx] = col;
631 if (dstY != dstEndY) {
634 Pixel col = srcLinePtrs[srcNumLines - 1][srcWidth - 1];
635 for (
auto dy :
xrange(dstY, dstEndY)) {
636 for (
auto dx :
xrange(dstWidth)) {
637 dstLines[dy][dx] = col;
642 for (
auto i :
xrange(dstStartY, dstEndY)) {
Interface for getting lines from a video frame.
const Pixel * getLinePtr(int line, unsigned width, 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 Pixel * acquireLine(unsigned y)=0
virtual unsigned getHeight() const =0
virtual void releaseLine(unsigned y, Pixel *buf)=0
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
T length(const vecN< N, T > &x)
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:
constexpr KeyMatrixPosition x
Keyboard bindings.
#define VLA(TYPE, NAME, LENGTH)
constexpr auto xrange(T e)