openMSX
SaI3xScaler.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 "SaI3xScaler.hh"
8#include "FrameSource.hh"
9#include "ScalerOutput.hh"
10#include "narrow.hh"
11#include "vla.hh"
12#include "build-info.hh"
13#include <cassert>
14#include <cstdint>
15
16namespace openmsx {
17
18template<std::unsigned_integral Pixel>
20 : Scaler3<Pixel>(pixelOps_)
21 , pixelOps(pixelOps_)
22{
23}
24
25template<std::unsigned_integral Pixel>
27 FrameSource& src, unsigned srcStartY, unsigned srcEndY,
28 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
29{
30 unsigned dstHeight = dst.getHeight();
31 unsigned stopDstY = (dstEndY == dstHeight)
32 ? dstEndY : dstEndY - 3;
33 unsigned srcY = srcStartY, dstY = dstStartY;
34 for (/* */; dstY < stopDstY; srcY += 1, dstY += 3) {
35 auto color = src.getLineColor<Pixel>(srcY);
36 for (auto i : xrange(3)) {
37 dst.fillLine(dstY + i, color);
38 }
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 SaI3xScaler<Pixel>::blend(Pixel p1, Pixel p2) const
51{
52 return pixelOps.template blend<1, 1>(p1, p2);
53}
54
55static constexpr unsigned redBlueMask = 0xF81F;
56static constexpr unsigned greenMask = 0x7E0;
57
58// TODO use PixelOperations::lerp()
59template<std::unsigned_integral Pixel>
60[[nodiscard]] static Pixel bilinear(unsigned a, unsigned b, unsigned x);
61
62template<> [[nodiscard]] uint16_t bilinear<uint16_t>(unsigned a, unsigned b, unsigned x)
63{
64 if (a == b) return a;
65
66 const unsigned areaB = x >> 11; // reduce 16 bit fraction to 5 bits
67 const unsigned areaA = 0x20 - areaB;
68
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);
73}
74
75template<> [[nodiscard]] uint32_t bilinear<uint32_t>(unsigned a, unsigned b, unsigned x)
76{
77 if (a == b) return a;
78
79 const unsigned areaB = x >> 8; // reduce 16 bit fraction to 8 bits
80 const unsigned areaA = 0x100 - areaB;
81
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);
87}
88
89// TODO move to PixelOperations
90template<std::unsigned_integral Pixel> [[nodiscard]] static Pixel bilinear4(
91 unsigned a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned y);
92
93template<> uint16_t bilinear4<uint16_t>(
94 unsigned a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned y)
95{
96 x >>= 11;
97 y >>= 11;
98 const unsigned xy = (x * y) >> 5;
99
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;
104
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);
109 unsigned result = (
110 (areaA * a) + (areaB * b) + (areaC * c) + (areaD * d)
111 ) >> 5;
112 return (result & redBlueMask) | ((result >> 16) & greenMask);
113}
114
115template<> [[nodiscard]] uint32_t bilinear4<uint32_t>(
116 unsigned a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned y)
117{
118 x >>= 8;
119 y >>= 8;
120 const unsigned xy = (x * y) >> 8;
121
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;
126
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);
134}
135
136template<std::unsigned_integral Pixel>
138{
139public:
140 template<unsigned x>
141 [[nodiscard]] inline static Pixel blend(unsigned a, unsigned b);
142
143 template<unsigned x, unsigned y>
144 [[nodiscard]] inline static Pixel blend(unsigned a, unsigned b, unsigned c, unsigned d);
145};
146
147template<unsigned X, unsigned OLD, unsigned NEW>
148struct Round {
149 static_assert(OLD > NEW);
150 static constexpr unsigned result =
151 (X >> (OLD - NEW)) + ((X >> (OLD - NEW - 1)) & 1);
152};
153
154template<std::unsigned_integral Pixel>
155template<unsigned x>
156inline Pixel Blender<Pixel>::blend(unsigned a, unsigned b)
157{
158 if (a == b) return a;
159
160 const unsigned bits = (sizeof(Pixel) == 2) ? 5 : 8;
161 const unsigned areaB = Round<x, 16, bits>::result;
162 const unsigned areaA = (1 << bits) - areaB;
163
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);
169 } else {
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);
177 }
178}
179
180template<std::unsigned_integral Pixel>
181template<unsigned wx, unsigned wy>
183 unsigned a, unsigned b, unsigned c, unsigned d)
184{
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;
189 const unsigned areaD = Round<xy, 16, bits>::result;
190 const unsigned areaA = (1 << bits) - areaB - areaC - areaD;
191
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);
197 unsigned result = (
198 (areaA * a) + (areaB * b) + (areaC * c) + (areaD * d)
199 ) >> bits;
200 return (result & redBlueMask) | ((result >> 16) & greenMask);
201 } else {
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);
213 }
214}
215
216template<unsigned i>
218{
219public:
220 template<std::unsigned_integral Pixel>
221 inline static void fill(Pixel*& dp, unsigned sa) {
222 *dp++ = sa;
223 PixelStripRepeater<i - 1>::template fill<Pixel>(dp, sa);
224 }
225
226 template<unsigned NX, unsigned y, std::unsigned_integral Pixel>
227 inline static void blendBackslash(
228 Pixel*& dp,
229 unsigned sa, unsigned sb, unsigned sc, unsigned sd,
230 unsigned se, unsigned sg, unsigned sj, unsigned sl)
231 {
232 // Fractional parts of the fixed point X coordinates.
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) {
238 *dp++ = Blender<Pixel>::template blend<f1 - y1>(sa, sb);
239 } else if (y1 >= f1 && sa == sg && sa != sl) {
240 *dp++ = Blender<Pixel>::template blend<y1 - f1>(sa, sc);
241 } else if (x1 >= f2 && sa == se && sa != sj) {
242 *dp++ = Blender<Pixel>::template blend<x1 - f2>(sa, sb);
243 } else if (x1 <= f2 && sa == sl && sa != sg) {
244 *dp++ = Blender<Pixel>::template blend<f2 - x1>(sa, sc);
245 } else if (y1 >= x1) {
246 *dp++ = Blender<Pixel>::template blend<y1 - x1>(sa, sc);
247 } else if (y1 <= x1) {
248 *dp++ = Blender<Pixel>::template blend<x1 - y1>(sa, sb);
249 }
250 PixelStripRepeater<i - 1>::template blendBackslash<NX, y, Pixel>(
251 dp, sa, sb, sc, sd, se, sg, sj, sl);
252 }
253
254 template<unsigned NX, unsigned y, std::unsigned_integral Pixel>
255 inline static void blendSlash(
256 Pixel*& dp,
257 unsigned sa, unsigned sb, unsigned sc, unsigned sd,
258 unsigned sf, unsigned sh, unsigned si, unsigned sk)
259 {
260 // Fractional parts of the fixed point X coordinates.
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) {
268 *dp++ = Blender<Pixel>::template blend<y2 - f1>(sb, sa);
269 } else if (y2 <= f1 && sb == si && sb != sk) {
270 *dp++ = Blender<Pixel>::template blend<f1 - y2>(sb, sd);
271 } else if (x2 >= f2 && sb == sf && sb != sh) {
272 *dp++ = Blender<Pixel>::template blend<x2 - f2>(sb, sa);
273 } else if (x2 <= f2 && sb == sk && sb != si) {
274 *dp++ = Blender<Pixel>::template blend<f2 - x2>(sb, sd);
275 } else if (y2 >= x1) {
276 *dp++ = Blender<Pixel>::template blend<y2 - x1>(sb, sa);
277 } else if (y2 <= x1) {
278 *dp++ = Blender<Pixel>::template blend<x1 - y2>(sb, sd);
279 }
280 PixelStripRepeater<i - 1>::template blendSlash<NX, y, Pixel>(
281 dp, sa, sb, sc, sd, sf, sh, si, sk);
282 }
283
284 template<unsigned NX, unsigned y, std::unsigned_integral Pixel>
285 inline static void blend4(
286 Pixel*& dp, unsigned sa, unsigned sb, unsigned sc, unsigned sd)
287 {
288 const unsigned x = ((NX - i) << 16) / NX;
289 *dp++ = Blender<Pixel>::template blend<x, y>(sa, sb, sc, sd);
290 PixelStripRepeater<i - 1>::template blend4<NX, y, Pixel>(dp, sa, sb, sc, sd);
291 }
292};
293template<>
295{
296public:
297 template<std::unsigned_integral Pixel>
298 inline static void fill(Pixel*& /*dp*/, unsigned /*sa*/) { }
299
300 template<unsigned NX, unsigned y, std::unsigned_integral Pixel>
301 inline static void blendBackslash(
302 Pixel*& /*dp*/, unsigned /*sa*/, unsigned /*sb*/,
303 unsigned /*sc*/, unsigned /*sd*/, unsigned /*se*/,
304 unsigned /*sg*/, unsigned /*sj*/, unsigned /*sl*/) { }
305
306 template<unsigned NX, unsigned y, std::unsigned_integral Pixel>
307 inline static void blendSlash(
308 Pixel*& /*dp*/, unsigned /*sa*/, unsigned /*sb*/,
309 unsigned /*sc*/, unsigned /*sd*/, unsigned /*sf*/,
310 unsigned /*sh*/, unsigned /*si*/, unsigned /*sk*/) { }
311
312 template<unsigned NX, unsigned y, std::unsigned_integral Pixel>
313 inline static void blend4(Pixel*& /*dp*/, unsigned /*sa*/,
314 unsigned /*sb*/, unsigned /*sc*/, unsigned /*sd*/) { }
315};
316
317template<unsigned i>
319{
320public:
321 template<unsigned NX, unsigned NY, std::unsigned_integral Pixel>
322 inline static void scaleFixedLine(
323 std::span<const Pixel> src0, std::span<const Pixel> src1,
324 std::span<const Pixel> src2, std::span<const Pixel> src3,
325 ScalerOutput<Pixel>& dst, unsigned& dstY)
326 {
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);
332
333 auto dstLine = dst.acquireLine(dstY);
334 auto* dp = dstLine.data();
335 // Calculate fixed point coordinate.
336 static constexpr unsigned y1 = ((NY - i) << 16) / NY;
337
338 size_t pos1 = 0;
339 size_t pos2 = 0;
340 size_t pos3 = 1;
341 Pixel sb = src1[0];
342 Pixel sd = src2[0];
343 repeat(srcWidth, [&] {
344 const auto pos0 = pos1;
345 pos1 = pos2;
346 pos2 = pos3;
347 pos3 = std::min(pos1 + 3, srcWidth) - 1;
348 // Get source pixels.
349 const Pixel sa = sb; // current pixel
350 sb = src1[pos2]; // next pixel
351 const Pixel sc = sd;
352 sd = src2[pos2];
353
354 // Compute and write color of destination pixel.
355 if (sa == sb && sc == sd && sa == sc) {
356 // All the same color; fill.
357 PixelStripRepeater<NX>::template fill<Pixel>(dp, sa);
358 } else if (sa == sd && sb != sc) {
359 // Pattern in the form of a backslash.
360 PixelStripRepeater<NX>::template blendBackslash<NX, y1, Pixel>(
361 dp, sa, sb, sc, sd, src0[pos1], src1[pos0], src2[pos3], src3[pos2]);
362 } else if (sb == sc && sa != sd) {
363 // Pattern in the form of a slash.
364 PixelStripRepeater<NX>::template blendSlash<NX, y1, Pixel>(
365 dp, sa, sb, sc, sd, src0[pos2], src2[pos0], src1[pos3], src3[pos1]);
366 } else {
367 // No pattern; use bilinear interpolation.
368 PixelStripRepeater<NX>::template blend4<NX, y1, Pixel>(
369 dp, sa, sb, sc, sd);
370 }
371 });
372 dst.releaseLine(dstY, dstLine);
373 ++dstY;
374
375 LineRepeater<i - 1>::template scaleFixedLine<NX, NY, Pixel>(
376 src0, src1, src2, src3, dst, dstY);
377 }
378};
379template<>
381{
382public:
383 template<unsigned NX, unsigned NY, std::unsigned_integral Pixel>
384 inline static void scaleFixedLine(
385 std::span<const Pixel> /*src0*/, std::span<const Pixel> /*src1*/,
386 std::span<const Pixel> /*src2*/, std::span<const Pixel> /*src3*/,
387 ScalerOutput<Pixel>& /*dst*/, unsigned& /*dstY*/)
388 { }
389};
390
391template<std::unsigned_integral Pixel>
392template<unsigned NX, unsigned NY>
393void SaI3xScaler<Pixel>::scaleFixed(FrameSource& src,
394 unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
395 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
396{
397 assert(dst.getWidth() == srcWidth * NX);
398 assert(dst.getHeight() == src.getHeight() * NY);
399
400 VLA_SSE_ALIGNED(Pixel, buf0, srcWidth);
401 VLA_SSE_ALIGNED(Pixel, buf1, srcWidth);
402 VLA_SSE_ALIGNED(Pixel, buf2, srcWidth);
403 VLA_SSE_ALIGNED(Pixel, buf3, srcWidth);
404
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);
409
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);
415 src0 = src1;
416 src1 = src2;
417 src2 = src3;
418 std::swap(buf0, buf1);
419 std::swap(buf1, buf2);
420 std::swap(buf2, buf3);
421 ++srcY;
422 }
423}
424
425template<std::unsigned_integral Pixel>
426void SaI3xScaler<Pixel>::scaleAny(FrameSource& src,
427 unsigned srcStartY, unsigned /*srcEndY*/, unsigned srcWidth,
428 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
429{
430 // Calculate fixed point end coordinates and deltas.
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);
435
436 VLA_SSE_ALIGNED(Pixel, buf0, srcWidth);
437 VLA_SSE_ALIGNED(Pixel, buf1, srcWidth);
438 VLA_SSE_ALIGNED(Pixel, buf2, srcWidth);
439 VLA_SSE_ALIGNED(Pixel, buf3, srcWidth);
440
441 unsigned h = 0;
442 for (auto dstY : xrange(dstStartY, dstEndY)) {
443 // Get source line pointers.
444 auto line = narrow<int>(srcStartY + (h >> 16));
445 // TODO possible optimization: reuse srcN from previous step
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);
450
451 // Get destination line pointer.
452 auto dstLine = dst.acquireLine(dstY);
453 auto* dp = dstLine.data();
454
455 // Fractional parts of the fixed point Y coordinates.
456 const unsigned y1 = h & 0xffff;
457 const unsigned y2 = 0x10000 - y1;
458 // Next line.
459 h += dh;
460
461 unsigned pos1 = 0;
462 unsigned pos2 = 0;
463 unsigned pos3 = 1;
464 Pixel B = src1[0];
465 Pixel D = src2[0];
466 for (unsigned w = 0; w < wFinish; ) {
467 const unsigned pos0 = pos1;
468 pos1 = pos2;
469 pos2 = pos3;
470 pos3 = std::min(pos1 + 3, srcWidth) - 1;
471 // Get source pixels.
472 const Pixel A = B; // current pixel
473 B = src1[pos2]; // next pixel
474 const Pixel C = D;
475 D = src2[pos2];
476
477 // Compute and write color of destination pixel.
478 if (A == B && C == D && A == C) { // 0
479 do {
480 *dp++ = A;
481 w += dw;
482 } while ((w >> 16) == pos1);
483 } else if (A == D && B != C) { // 1
484 do {
485 // Fractional parts of the fixed point X coordinates.
486 const unsigned x1 = w & 0xffff;
487 const unsigned f1 = (x1 >> 1) + (0x10000 >> 2);
488 const unsigned f2 = (y1 >> 1) + (0x10000 >> 2);
489 Pixel product1;
490 if (y1 <= f1 && A == src2[pos3] && A != src0[pos1]) { // close to B
491 product1 = bilinear<Pixel>(A, B, f1 - y1);
492 } else if (y1 >= f1 && A == src1[pos0] && A != src3[pos2]) { // close to C
493 product1 = bilinear<Pixel>(A, C, y1 - f1);
494 } else if (x1 >= f2 && A == src0[pos1] && A != src2[pos3]) { // close to B
495 product1 = bilinear<Pixel>(A, B, x1 - f2);
496 } else if (x1 <= f2 && A == src3[pos2] && A != src1[pos0]) { // close to C
497 product1 = bilinear<Pixel>(A, C, f2 - x1);
498 } else if (y1 >= x1) { // close to C
499 product1 = bilinear<Pixel>(A, C, y1 - x1);
500 } else {
501 assert(y1 < x1); // close to B
502 product1 = bilinear<Pixel>(A, B, x1 - y1);
503 }
504 *dp++ = product1;
505 w += dw;
506 } while ((w >> 16) == pos1);
507 } else if (B == C && A != D) { // 2
508 do {
509 // Fractional parts of the fixed point X coordinates.
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);
514 Pixel product1;
515 if (y2 >= f1 && B == src2[pos0] && B != src0[pos2]) { // close to A
516 product1 = bilinear<Pixel>(B, A, y2 - f1);
517 } else if (y2 <= f1 && B == src1[pos3] && B != src3[pos1]) { // close to D
518 product1 = bilinear<Pixel>(B, D, f1 - y2);
519 } else if (x2 >= f2 && B == src0[pos2] && B != src2[pos0]) { // close to A
520 product1 = bilinear<Pixel>(B, A, x2 - f2);
521 } else if (x2 <= f2 && B == src3[pos1] && B != src1[pos3]) { // close to D
522 product1 = bilinear<Pixel>(B, D, f2 - x2);
523 } else if (y2 >= x1) { // close to A
524 product1 = bilinear<Pixel>(B, A, y2 - x1);
525 } else {
526 assert(y2 < x1); // close to D
527 product1 = bilinear<Pixel>(B, D, x1 - y2);
528 }
529 *dp++ = product1;
530 w += dw;
531 } while ((w >> 16) == pos1);
532 } else { // 3
533 do {
534 // Fractional parts of the fixed point X coordinates.
535 const unsigned x1 = w & 0xffff;
536 *dp++ = bilinear4<Pixel>(A, B, C, D, x1, y1);
537 w += dw;
538 } while ((w >> 16) == pos1);
539 }
540 }
541 dst.releaseLine(dstY, dstLine);
542 }
543}
544
545template<std::unsigned_integral Pixel>
547 unsigned srcStartY, unsigned srcEndY, unsigned srcWidth,
548 ScalerOutput<Pixel>& dst, unsigned dstStartY, unsigned dstEndY)
549{
550 scaleFixed<3, 3>(src, srcStartY, srcEndY, srcWidth, dst, dstStartY, dstEndY);
551}
552
553
554// Force template instantiation.
555#if HAVE_16BPP
556template class SaI3xScaler<uint16_t>;
557#endif
558#if HAVE_32BPP
559template class SaI3xScaler<uint32_t>;
560#endif
561
562} // namespace openmsx
static Pixel blend(unsigned a, unsigned b)
Definition: SaI3xScaler.cc:156
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.
static void scaleFixedLine(std::span< const Pixel >, std::span< const Pixel >, std::span< const Pixel >, std::span< const Pixel >, ScalerOutput< Pixel > &, unsigned &)
Definition: SaI3xScaler.cc:384
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)
Definition: SaI3xScaler.cc:322
static void blendSlash(Pixel *&, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned)
Definition: SaI3xScaler.cc:307
static void blend4(Pixel *&, unsigned, unsigned, unsigned, unsigned)
Definition: SaI3xScaler.cc:313
static void fill(Pixel *&, unsigned)
Definition: SaI3xScaler.cc:298
static void blendBackslash(Pixel *&, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned)
Definition: SaI3xScaler.cc:301
static void blendBackslash(Pixel *&dp, unsigned sa, unsigned sb, unsigned sc, unsigned sd, unsigned se, unsigned sg, unsigned sj, unsigned sl)
Definition: SaI3xScaler.cc:227
static void fill(Pixel *&dp, unsigned sa)
Definition: SaI3xScaler.cc:221
static void blendSlash(Pixel *&dp, unsigned sa, unsigned sb, unsigned sc, unsigned sd, unsigned sf, unsigned sh, unsigned si, unsigned sk)
Definition: SaI3xScaler.cc:255
static void blend4(Pixel *&dp, unsigned sa, unsigned sb, unsigned sc, unsigned sd)
Definition: SaI3xScaler.cc:285
2xSaI algorithm: edge-detection which produces a rounded look.
Definition: SaI3xScaler.hh:14
void scale1x1to3x3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, unsigned srcWidth, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Definition: SaI3xScaler.cc:546
void scaleBlank1to3(FrameSource &src, unsigned srcStartY, unsigned srcEndY, ScalerOutput< Pixel > &dst, unsigned dstStartY, unsigned dstEndY) override
Definition: SaI3xScaler.cc:26
SaI3xScaler(const PixelOperations< Pixel > &pixelOps)
Definition: SaI3xScaler.cc:19
Base class for 3x scalers.
Definition: Scaler3.hh:12
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:266
This file implemented 3 utility functions:
Definition: Autofire.cc:9
uint16_t bilinear< uint16_t >(unsigned a, unsigned b, unsigned x)
Definition: SaI3xScaler.cc:62
uint32_t Pixel
uint32_t bilinear4< uint32_t >(unsigned a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned y)
Definition: SaI3xScaler.cc:115
std::array< const EDStorage, 4 > A
uint32_t bilinear< uint32_t >(unsigned a, unsigned b, unsigned x)
Definition: SaI3xScaler.cc:75
uint16_t bilinear4< uint16_t >(unsigned a, unsigned b, unsigned c, unsigned d, unsigned x, unsigned y)
Definition: SaI3xScaler.cc:93
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
Definition: MemBuffer.hh:202
static constexpr unsigned result
Definition: SaI3xScaler.cc:150
#define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH)
Definition: vla.hh:50
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
Definition: xrange.hh:148
constexpr auto xrange(T e)
Definition: xrange.hh:133