FabGL
ESP32 Display Controller and Graphics Library
cvbs16controller.cpp
1/*
2 Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3 Copyright (c) 2019-2022 Fabrizio Di Vittorio.
4 All rights reserved.
5
6
7* Please contact fdivitto2013@gmail.com if you need a commercial license.
8
9
10* This library and related software is available under GPL v3.
11
12 FabGL is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 FabGL is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26
27#include <alloca.h>
28#include <stdarg.h>
29#include <math.h>
30#include <string.h>
31
32#include "freertos/FreeRTOS.h"
33#include "freertos/task.h"
34
35#include "esp_spi_flash.h"
36
37#include "fabutils.h"
38#include "cvbs16controller.h"
39
40
41
42#pragma GCC optimize ("O2")
43
44
45
46
47namespace fabgl {
48
49
50
51// high nibble is pixel 0, low nibble is pixel 1
52
53static inline __attribute__((always_inline)) void CVBS16_SETPIXELINROW(uint8_t * row, int x, int value) {
54 int brow = x >> 1;
55 row[brow] = (x & 1) ? ((row[brow] & 0xf0) | value) : ((row[brow] & 0x0f) | (value << 4));
56}
57
58static inline __attribute__((always_inline)) int CVBS16_GETPIXELINROW(uint8_t * row, int x) {
59 int brow = x >> 1;
60 return ((x & 1) ? (row[brow] & 0x0f) : ((row[brow] & 0xf0) >> 4));
61}
62
63#define CVBS16_INVERTPIXELINROW(row, x) (row)[(x) >> 1] ^= (0xf0 >> (((x) & 1) << 2))
64
65static inline __attribute__((always_inline)) void CVBS16_SETPIXEL(int x, int y, int value) {
66 auto row = (uint8_t*) CVBS16Controller::sgetScanline(y);
67 int brow = x >> 1;
68 row[brow] = (x & 1) ? ((row[brow] & 0xf0) | value) : ((row[brow] & 0x0f) | (value << 4));
69}
70
71#define CVBS16_GETPIXEL(x, y) CVBS16_GETPIXELINROW((uint8_t*)CVBS16Controller::s_viewPort[(y)], (x))
72
73#define CVBS16_INVERT_PIXEL(x, y) CVBS16_INVERTPIXELINROW((uint8_t*)CVBS16Controller::s_viewPort[(y)], (x))
74
75
76#define CVBS16_COLUMNSQUANTUM 16
77
78
79/*************************************************************************************/
80/* CVBS16Controller definitions */
81
82
83CVBS16Controller * CVBS16Controller::s_instance = nullptr;
84volatile uint16_t * * CVBS16Controller::s_paletteToRawPixel[2];
85
86
87CVBS16Controller::CVBS16Controller()
88 : CVBSPalettedController(CVBS16_COLUMNSQUANTUM, NativePixelFormat::PALETTE16, 2, 1),
89 m_monochrome(false)
90{
91 s_instance = this;
92}
93
94
95void CVBS16Controller::allocateViewPort()
96{
97 CVBSPalettedController::allocateViewPort();
98
99 for (int line = 0; line < 2; ++line) {
100 s_paletteToRawPixel[line] = (volatile uint16_t * *) heap_caps_malloc(sizeof(uint16_t *) * 16, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
101 for (int index = 0; index < 16; ++index)
102 s_paletteToRawPixel[line][index] = (uint16_t *) heap_caps_malloc(sizeof(uint16_t) * CVBS_SUBCARRIERPHASES * 2, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
103 }
104
105 switch (horizontalRate()) {
106 case 1:
107 setDrawScanlineCallback(drawScanlineX1);
108 break;
109 case 2:
110 setDrawScanlineCallback(drawScanlineX2);
111 break;
112 default:
113 setDrawScanlineCallback(drawScanlineX3);
114 break;
115 }
116
117 //printf("m_viewPortWidth = %d m_viewPortHeight = %d\n", m_viewPortWidth, m_viewPortHeight);
118 //printf("C. Free Memory : %d bytes\r\n\n", heap_caps_get_free_size(MALLOC_CAP_32BIT));
119}
120
121
122void CVBS16Controller::checkViewPortSize()
123{
124 CVBSPalettedController::checkViewPortSize();
125}
126
127
128
129void CVBS16Controller::setupDefaultPalette()
130{
131 for (int colorIndex = 0; colorIndex < 16; ++colorIndex) {
132 RGB888 rgb888((Color)colorIndex);
133 setPaletteItem(colorIndex, rgb888);
134 }
135}
136
137
138void CVBS16Controller::setPaletteItem(int index, RGB888 const & color)
139{
140 index %= 16;
141 m_palette[index] = color;
142
143 double range = params()->whiteLevel - params()->blackLevel + 1;
144
145 for (int line = 0; line < 2; ++line) {
146 for (int sample = 0; sample < CVBS_SUBCARRIERPHASES * 2; ++sample) {
147
148 double phase = 2. * M_PI * (sample) / CVBS_SUBCARRIERPHASES;
149
150 double Y;
151 double chroma = params()->getComposite(line == 0, phase, color.R / 255., color.G / 255., color.B / 255., &Y);
152
153 // black/white?
154 if (m_monochrome)
155 chroma = 0;
156
157 s_paletteToRawPixel[line][index][sample] = (uint16_t)(params()->blackLevel + (Y + chroma) * range) << 8;
158 }
159 }
160}
161
162
163void CVBS16Controller::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
164{
165 genericSetPixelAt(pixelDesc, updateRect,
166 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
167 CVBS16_SETPIXEL
168 );
169}
170
171
172// coordinates are absolute values (not relative to origin)
173// line clipped on current absolute clipping rectangle
174void CVBS16Controller::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
175{
176 genericAbsDrawLine(X1, Y1, X2, Y2, color,
177 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
178 [&] (int Y, int X1, int X2, uint8_t colorIndex) { rawFillRow(Y, X1, X2, colorIndex); },
179 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); },
180 CVBS16_SETPIXEL,
181 [&] (int X, int Y) { CVBS16_INVERT_PIXEL(X, Y); }
182 );
183}
184
185
186// parameters not checked
187void CVBS16Controller::rawFillRow(int y, int x1, int x2, RGB888 color)
188{
189 rawFillRow(y, x1, x2, RGB888toPaletteIndex(color));
190}
191
192
193// parameters not checked
194void CVBS16Controller::rawFillRow(int y, int x1, int x2, uint8_t colorIndex)
195{
196 uint8_t * row = (uint8_t*) m_viewPort[y];
197 // fill first pixels before full 16 bits word
198 int x = x1;
199 for (; x <= x2 && (x & 3) != 0; ++x) {
200 CVBS16_SETPIXELINROW(row, x, colorIndex);
201 }
202 // fill whole 16 bits words (4 pixels)
203 if (x <= x2) {
204 int sz = (x2 & ~3) - x;
205 memset((void*)(row + x / 2), colorIndex | (colorIndex << 4), sz / 2);
206 x += sz;
207 }
208 // fill last unaligned pixels
209 for (; x <= x2; ++x) {
210 CVBS16_SETPIXELINROW(row, x, colorIndex);
211 }
212}
213
214
215// parameters not checked
216void CVBS16Controller::rawInvertRow(int y, int x1, int x2)
217{
218 auto row = m_viewPort[y];
219 for (int x = x1; x <= x2; ++x)
220 CVBS16_INVERTPIXELINROW(row, x);
221}
222
223
224void CVBS16Controller::rawCopyRow(int x1, int x2, int srcY, int dstY)
225{
226 auto srcRow = (uint8_t*) m_viewPort[srcY];
227 auto dstRow = (uint8_t*) m_viewPort[dstY];
228 // copy first pixels before full 16 bits word
229 int x = x1;
230 for (; x <= x2 && (x & 3) != 0; ++x) {
231 CVBS16_SETPIXELINROW(dstRow, x, CVBS16_GETPIXELINROW(srcRow, x));
232 }
233 // copy whole 16 bits words (4 pixels)
234 auto src = (uint16_t*)(srcRow + x / 2);
235 auto dst = (uint16_t*)(dstRow + x / 2);
236 for (int right = (x2 & ~3); x < right; x += 4)
237 *dst++ = *src++;
238 // copy last unaligned pixels
239 for (x = (x2 & ~3); x <= x2; ++x) {
240 CVBS16_SETPIXELINROW(dstRow, x, CVBS16_GETPIXELINROW(srcRow, x));
241 }
242}
243
244
245void CVBS16Controller::swapRows(int yA, int yB, int x1, int x2)
246{
247 auto rowA = (uint8_t*) m_viewPort[yA];
248 auto rowB = (uint8_t*) m_viewPort[yB];
249 // swap first pixels before full 16 bits word
250 int x = x1;
251 for (; x <= x2 && (x & 3) != 0; ++x) {
252 uint8_t a = CVBS16_GETPIXELINROW(rowA, x);
253 uint8_t b = CVBS16_GETPIXELINROW(rowB, x);
254 CVBS16_SETPIXELINROW(rowA, x, b);
255 CVBS16_SETPIXELINROW(rowB, x, a);
256 }
257 // swap whole 16 bits words (4 pixels)
258 auto a = (uint16_t*)(rowA + x / 2);
259 auto b = (uint16_t*)(rowB + x / 2);
260 for (int right = (x2 & ~3); x < right; x += 4)
261 tswap(*a++, *b++);
262 // swap last unaligned pixels
263 for (x = (x2 & ~3); x <= x2; ++x) {
264 uint8_t a = CVBS16_GETPIXELINROW(rowA, x);
265 uint8_t b = CVBS16_GETPIXELINROW(rowB, x);
266 CVBS16_SETPIXELINROW(rowA, x, b);
267 CVBS16_SETPIXELINROW(rowB, x, a);
268 }
269}
270
271
272void CVBS16Controller::drawEllipse(Size const & size, Rect & updateRect)
273{
274 genericDrawEllipse(size, updateRect,
275 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
276 CVBS16_SETPIXEL
277 );
278}
279
280
281void CVBS16Controller::clear(Rect & updateRect)
282{
283 hideSprites(updateRect);
284 uint8_t paletteIndex = RGB888toPaletteIndex(getActualBrushColor());
285 uint8_t pattern = paletteIndex | (paletteIndex << 4);
286 for (int y = 0; y < m_viewPortHeight; ++y)
287 memset((uint8_t*) m_viewPort[y], pattern, m_viewPortWidth / 2);
288}
289
290
291// scroll < 0 -> scroll UP
292// scroll > 0 -> scroll DOWN
293void CVBS16Controller::VScroll(int scroll, Rect & updateRect)
294{
295 genericVScroll(scroll, updateRect,
296 [&] (int yA, int yB, int x1, int x2) { swapRows(yA, yB, x1, x2); }, // swapRowsCopying
297 [&] (int yA, int yB) { tswap(m_viewPort[yA], m_viewPort[yB]); }, // swapRowsPointers
298 [&] (int y, int x1, int x2, RGB888 color) { rawFillRow(y, x1, x2, color); } // rawFillRow
299 );
300}
301
302
303void CVBS16Controller::HScroll(int scroll, Rect & updateRect)
304{
305 hideSprites(updateRect);
306 uint8_t back4 = RGB888toPaletteIndex(getActualBrushColor());
307
308 int Y1 = paintState().scrollingRegion.Y1;
309 int Y2 = paintState().scrollingRegion.Y2;
310 int X1 = paintState().scrollingRegion.X1;
311 int X2 = paintState().scrollingRegion.X2;
312
313 int width = X2 - X1 + 1;
314 bool HScrolllingRegionAligned = ((X1 & 3) == 0 && (width & 3) == 0); // 4 pixels aligned
315
316 if (scroll < 0) {
317 // scroll left
318 for (int y = Y1; y <= Y2; ++y) {
319 if (HScrolllingRegionAligned) {
320 // aligned horizontal scrolling region, fast version
321 uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 2;
322 for (int s = -scroll; s > 0;) {
323 if (s > 1) {
324 // scroll left by 2, 4, 6, ... moving bytes
325 auto sc = s & ~1;
326 auto sz = width & ~1;
327 memmove(row, row + sc / 2, (sz - sc) / 2);
328 rawFillRow(y, X2 - sc + 1, X2, back4);
329 s -= sc;
330 } else if (s & 1) {
331 // scroll left 1 pixel (uint16_t at the time = 4 pixels)
332 // nibbles 0,1,2... P is prev or background
333 // byte : 01 23 45 67 -> 12 34 56 7P
334 // word (little endian CPU) : 2301 6745 -> 3412 7P56
335 auto prev = back4;
336 auto w = (uint16_t *) (row + width / 2) - 1;
337 for (int i = 0; i < width; i += 4) {
338 const uint16_t p4 = *w; // four pixels
339 *w-- = (p4 << 4 & 0xf000) | (prev << 8 & 0x0f00) | (p4 << 4 & 0x00f0) | (p4 >> 12 & 0x000f);
340 prev = p4 >> 4 & 0x000f;
341 }
342 --s;
343 }
344 }
345 } else {
346 // unaligned horizontal scrolling region, fallback to slow version
347 auto row = (uint8_t*) m_viewPort[y];
348 for (int x = X1; x <= X2 + scroll; ++x)
349 CVBS16_SETPIXELINROW(row, x, CVBS16_GETPIXELINROW(row, x - scroll));
350 // fill right area with brush color
351 rawFillRow(y, X2 + 1 + scroll, X2, back4);
352 }
353 }
354 } else if (scroll > 0) {
355 // scroll right
356 for (int y = Y1; y <= Y2; ++y) {
357 if (HScrolllingRegionAligned) {
358 // aligned horizontal scrolling region, fast version
359 uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 2;
360 for (int s = scroll; s > 0;) {
361 if (s > 1) {
362 // scroll right by 2, 4, 6, ... moving bytes
363 auto sc = s & ~1;
364 auto sz = width & ~1;
365 memmove(row + sc / 2, row, (sz - sc) / 2);
366 rawFillRow(y, X1, X1 + sc - 1, back4);
367 s -= sc;
368 } else if (s & 1) {
369 // scroll right 1 pixel (uint16_t at the time = 4 pixels)
370 // nibbles 0,1,2... P is prev or background
371 // byte : 01 23 45 67 -> P0 12 34 56 7...
372 // word (little endian CPU) : 2301 6745 -> 12P0 5634 ...
373 auto prev = back4;
374 auto w = (uint16_t *) row;
375 for (int i = 0; i < width; i += 4) {
376 const uint16_t p4 = *w; // four pixels
377 *w++ = (p4 << 12 & 0xf000) | (p4 >> 4 & 0x0f00) | (prev << 4) | (p4 >> 4 & 0x000f);
378 prev = p4 >> 8 & 0x000f;
379 }
380 --s;
381 }
382 }
383 } else {
384 // unaligned horizontal scrolling region, fallback to slow version
385 auto row = (uint8_t*) m_viewPort[y];
386 for (int x = X2 - scroll; x >= X1; --x)
387 CVBS16_SETPIXELINROW(row, x + scroll, CVBS16_GETPIXELINROW(row, x));
388 // fill left area with brush color
389 rawFillRow(y, X1, X1 + scroll - 1, back4);
390 }
391 }
392
393 }
394}
395
396
397void CVBS16Controller::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
398{
399 genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
400 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
401 [&] (int y) { return (uint8_t*) m_viewPort[y]; },
402 CVBS16_SETPIXELINROW
403 );
404}
405
406
407void CVBS16Controller::invertRect(Rect const & rect, Rect & updateRect)
408{
409 genericInvertRect(rect, updateRect,
410 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
411 );
412}
413
414
415void CVBS16Controller::swapFGBG(Rect const & rect, Rect & updateRect)
416{
417 genericSwapFGBG(rect, updateRect,
418 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
419 [&] (int y) { return (uint8_t*) m_viewPort[y]; },
420 CVBS16_GETPIXELINROW,
421 CVBS16_SETPIXELINROW
422 );
423}
424
425
426// Slow operation!
427// supports overlapping of source and dest rectangles
428void CVBS16Controller::copyRect(Rect const & source, Rect & updateRect)
429{
430 genericCopyRect(source, updateRect,
431 [&] (int y) { return (uint8_t*) m_viewPort[y]; },
432 CVBS16_GETPIXELINROW,
433 CVBS16_SETPIXELINROW
434 );
435}
436
437
438// no bounds check is done!
439void CVBS16Controller::readScreen(Rect const & rect, RGB888 * destBuf)
440{
441 for (int y = rect.Y1; y <= rect.Y2; ++y) {
442 auto row = (uint8_t*) m_viewPort[y];
443 for (int x = rect.X1; x <= rect.X2; ++x, ++destBuf) {
444 const RGB222 v = m_palette[CVBS16_GETPIXELINROW(row, x)];
445 *destBuf = RGB888(v.R * 85, v.G * 85, v.B * 85); // 85 x 3 = 255
446 }
447 }
448}
449
450
451void CVBS16Controller::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
452{
453 genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
454 [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
455 CVBS16_SETPIXELINROW
456 );
457}
458
459
460void CVBS16Controller::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
461{
462 auto foregroundColorIndex = RGB888toPaletteIndex(bitmap->foregroundColor);
463 genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
464 [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
465 CVBS16_GETPIXELINROW,
466 [&] (uint8_t * row, int x) { CVBS16_SETPIXELINROW(row, x, foregroundColorIndex); } // rawSetPixelInRow
467 );
468}
469
470
471void CVBS16Controller::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
472{
473 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
474 [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
475 CVBS16_GETPIXELINROW,
476 [&] (uint8_t * row, int x, uint8_t src) { CVBS16_SETPIXELINROW(row, x, RGB2222toPaletteIndex(src)); } // rawSetPixelInRow
477 );
478}
479
480
481void CVBS16Controller::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
482{
483 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
484 [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
485 [&] (uint8_t * row, int x) { return CVBS16_GETPIXELINROW(row, x); }, // rawGetPixelInRow
486 [&] (uint8_t * row, int x, RGBA8888 const & src) { CVBS16_SETPIXELINROW(row, x, RGB8888toPaletteIndex(src)); } // rawSetPixelInRow
487 );
488}
489
490
491void IRAM_ATTR CVBS16Controller::drawScanlineX1(void * arg, uint16_t * dest, int scanLine)
492{
493 auto ctrl = (CVBS16Controller *) arg;
494
495 auto const width = ctrl->m_viewPortWidth;
496 auto const height = ctrl->m_viewPortHeight;
497
498 auto src = (uint8_t const *) s_viewPortVisible[scanLine];
499 auto dest32 = (uint32_t*) dest;
500
501 int firstVisibleSample = CVBSGenerator::firstVisibleSample();
502 int subCarrierPhaseSam = CVBSGenerator::subCarrierPhase();
503 auto paletteToRaw = s_paletteToRawPixel[CVBSGenerator::interFrameLine() & 1];
504 auto sampleLUT = CVBSGenerator::lineSampleToSubCarrierSample();
505
506 //if (CVBSGenerator::frame() == 0) {
507 // for (int i = 0; i < width; ++i)
508 // *dest++ = 0x2000;
509 //} else
510
511 // optimization warn: horizontal resolution must be a multiple of 16!
512 for (int col = 0; col < width; col += 16) {
513
514 auto src1 = *(src + 0);
515 auto src2 = *(src + 1);
516 auto src3 = *(src + 2);
517 auto src4 = *(src + 3);
518 auto src5 = *(src + 4);
519 auto src6 = *(src + 5);
520 auto src7 = *(src + 6);
521 auto src8 = *(src + 7);
522
523 PSRAM_HACK;
524
525 int sample = firstVisibleSample + col;
526
527 auto v1 = (paletteToRaw[src1 >> 4][sampleLUT[sample + 0] + subCarrierPhaseSam] << 16) | (paletteToRaw[src1 & 0x0f][sampleLUT[sample + 1] + subCarrierPhaseSam]);
528 auto v2 = (paletteToRaw[src2 >> 4][sampleLUT[sample + 2] + subCarrierPhaseSam] << 16) | (paletteToRaw[src2 & 0x0f][sampleLUT[sample + 3] + subCarrierPhaseSam]);
529 auto v3 = (paletteToRaw[src3 >> 4][sampleLUT[sample + 4] + subCarrierPhaseSam] << 16) | (paletteToRaw[src3 & 0x0f][sampleLUT[sample + 5] + subCarrierPhaseSam]);
530 auto v4 = (paletteToRaw[src4 >> 4][sampleLUT[sample + 6] + subCarrierPhaseSam] << 16) | (paletteToRaw[src4 & 0x0f][sampleLUT[sample + 7] + subCarrierPhaseSam]);
531 auto v5 = (paletteToRaw[src5 >> 4][sampleLUT[sample + 8] + subCarrierPhaseSam] << 16) | (paletteToRaw[src5 & 0x0f][sampleLUT[sample + 9] + subCarrierPhaseSam]);
532 auto v6 = (paletteToRaw[src6 >> 4][sampleLUT[sample + 10] + subCarrierPhaseSam] << 16) | (paletteToRaw[src6 & 0x0f][sampleLUT[sample + 11] + subCarrierPhaseSam]);
533 auto v7 = (paletteToRaw[src7 >> 4][sampleLUT[sample + 12] + subCarrierPhaseSam] << 16) | (paletteToRaw[src7 & 0x0f][sampleLUT[sample + 13] + subCarrierPhaseSam]);
534 auto v8 = (paletteToRaw[src8 >> 4][sampleLUT[sample + 14] + subCarrierPhaseSam] << 16) | (paletteToRaw[src8 & 0x0f][sampleLUT[sample + 15] + subCarrierPhaseSam]);
535
536 *(dest32 + 0) = v1;
537 *(dest32 + 1) = v2;
538 *(dest32 + 2) = v3;
539 *(dest32 + 3) = v4;
540 *(dest32 + 4) = v5;
541 *(dest32 + 5) = v6;
542 *(dest32 + 6) = v7;
543 *(dest32 + 7) = v8;
544
545 dest32 += 8; // advance by 8x2=16 samples
546 src += 8; // advance by 8x2=16 pixels
547
548 }
549
550 if (scanLine >= height - 1 && !ctrl->m_primitiveProcessingSuspended && spi_flash_cache_enabled() && ctrl->m_primitiveExecTask) {
551 // vertical sync, unlock primitive execution task
552 // warn: don't use vTaskSuspendAll() in primitive drawing, otherwise vTaskNotifyGiveFromISR may be blocked and screen will flick!
553 vTaskNotifyGiveFromISR(ctrl->m_primitiveExecTask, NULL);
554 }
555
556}
557
558
559void IRAM_ATTR CVBS16Controller::drawScanlineX2(void * arg, uint16_t * dest, int scanLine)
560{
561 auto ctrl = (CVBS16Controller *) arg;
562
563 auto const width = ctrl->m_viewPortWidth * 2;
564 auto const height = ctrl->m_viewPortHeight;
565
566 auto src = (uint8_t const *) s_viewPortVisible[scanLine];
567 auto dest32 = (uint32_t*) dest;
568
569 int firstVisibleSample = CVBSGenerator::firstVisibleSample();
570 int subCarrierPhaseSam = CVBSGenerator::subCarrierPhase();
571 auto paletteToRaw = s_paletteToRawPixel[CVBSGenerator::interFrameLine() & 1];
572 auto sampleLUT = CVBSGenerator::lineSampleToSubCarrierSample();
573
574 // optimization warn: horizontal resolution must be a multiple of 8!
575 for (int col = 0; col < width; col += 16) {
576
577 auto src1 = *(src + 0);
578 auto src2 = *(src + 1);
579 auto src3 = *(src + 2);
580 auto src4 = *(src + 3);
581
582 PSRAM_HACK;
583
584 int sample = firstVisibleSample + col;
585
586 auto v1 = (paletteToRaw[src1 >> 4 ][sampleLUT[sample + 0] + subCarrierPhaseSam] << 16) | (paletteToRaw[src1 >> 4 ][sampleLUT[sample + 1] + subCarrierPhaseSam]);
587 auto v2 = (paletteToRaw[src1 & 0x0f][sampleLUT[sample + 2] + subCarrierPhaseSam] << 16) | (paletteToRaw[src1 & 0x0f][sampleLUT[sample + 3] + subCarrierPhaseSam]);
588 auto v3 = (paletteToRaw[src2 >> 4 ][sampleLUT[sample + 4] + subCarrierPhaseSam] << 16) | (paletteToRaw[src2 >> 4 ][sampleLUT[sample + 5] + subCarrierPhaseSam]);
589 auto v4 = (paletteToRaw[src2 & 0x0f][sampleLUT[sample + 6] + subCarrierPhaseSam] << 16) | (paletteToRaw[src2 & 0x0f][sampleLUT[sample + 7] + subCarrierPhaseSam]);
590 auto v5 = (paletteToRaw[src3 >> 4 ][sampleLUT[sample + 8] + subCarrierPhaseSam] << 16) | (paletteToRaw[src3 >> 4 ][sampleLUT[sample + 9] + subCarrierPhaseSam]);
591 auto v6 = (paletteToRaw[src3 & 0x0f][sampleLUT[sample + 10] + subCarrierPhaseSam] << 16) | (paletteToRaw[src3 & 0x0f][sampleLUT[sample + 11] + subCarrierPhaseSam]);
592 auto v7 = (paletteToRaw[src4 >> 4 ][sampleLUT[sample + 12] + subCarrierPhaseSam] << 16) | (paletteToRaw[src4 >> 4 ][sampleLUT[sample + 13] + subCarrierPhaseSam]);
593 auto v8 = (paletteToRaw[src4 & 0x0f][sampleLUT[sample + 14] + subCarrierPhaseSam] << 16) | (paletteToRaw[src4 & 0x0f][sampleLUT[sample + 15] + subCarrierPhaseSam]);
594
595 *(dest32 + 0) = v1;
596 *(dest32 + 1) = v2;
597 *(dest32 + 2) = v3;
598 *(dest32 + 3) = v4;
599 *(dest32 + 4) = v5;
600 *(dest32 + 5) = v6;
601 *(dest32 + 6) = v7;
602 *(dest32 + 7) = v8;
603
604 dest32 += 8; // advances by 8x2=16 samples
605 src += 4; // advance by 4x2=8 pixels
606
607 }
608
609 if (scanLine >= height - 1 && !ctrl->m_primitiveProcessingSuspended && spi_flash_cache_enabled() && ctrl->m_primitiveExecTask) {
610 // vertical sync, unlock primitive execution task
611 // warn: don't use vTaskSuspendAll() in primitive drawing, otherwise vTaskNotifyGiveFromISR may be blocked and screen will flick!
612 vTaskNotifyGiveFromISR(ctrl->m_primitiveExecTask, NULL);
613 }
614
615}
616
617
618void IRAM_ATTR CVBS16Controller::drawScanlineX3(void * arg, uint16_t * dest, int scanLine)
619{
620 auto ctrl = (CVBS16Controller *) arg;
621
622 auto const width = ctrl->m_viewPortWidth * 3;
623 auto const height = ctrl->m_viewPortHeight;
624
625 auto src = (uint8_t const *) s_viewPortVisible[scanLine];
626 auto dest32 = (uint32_t*) dest;
627
628 int firstVisibleSample = CVBSGenerator::firstVisibleSample();
629 int subCarrierPhaseSam = CVBSGenerator::subCarrierPhase();
630 auto paletteToRaw = s_paletteToRawPixel[CVBSGenerator::interFrameLine() & 1];
631 auto sampleLUT = CVBSGenerator::lineSampleToSubCarrierSample();
632
633 // optimization warn: horizontal resolution must be a multiple of 8!
634 for (int col = 0; col < width; col += 24) {
635
636 auto src1 = *(src + 0);
637 auto src2 = *(src + 1);
638 auto src3 = *(src + 2);
639 auto src4 = *(src + 3);
640
641 PSRAM_HACK;
642
643 int sample = firstVisibleSample + col;
644
645 auto v1 = (paletteToRaw[src1 >> 4 ][sampleLUT[sample + 0] + subCarrierPhaseSam] << 16) | (paletteToRaw[src1 >> 4 ][sampleLUT[sample + 1] + subCarrierPhaseSam]);
646 auto v2 = (paletteToRaw[src1 >> 4 ][sampleLUT[sample + 2] + subCarrierPhaseSam] << 16) | (paletteToRaw[src1 & 0x0f][sampleLUT[sample + 3] + subCarrierPhaseSam]);
647 auto v3 = (paletteToRaw[src1 & 0x0f][sampleLUT[sample + 4] + subCarrierPhaseSam] << 16) | (paletteToRaw[src1 & 0x0f][sampleLUT[sample + 5] + subCarrierPhaseSam]);
648
649 auto v4 = (paletteToRaw[src2 >> 4 ][sampleLUT[sample + 6] + subCarrierPhaseSam] << 16) | (paletteToRaw[src2 >> 4 ][sampleLUT[sample + 7] + subCarrierPhaseSam]);
650 auto v5 = (paletteToRaw[src2 >> 4 ][sampleLUT[sample + 8] + subCarrierPhaseSam] << 16) | (paletteToRaw[src2 & 0x0f][sampleLUT[sample + 9] + subCarrierPhaseSam]);
651 auto v6 = (paletteToRaw[src2 & 0x0f][sampleLUT[sample + 10] + subCarrierPhaseSam] << 16) | (paletteToRaw[src2 & 0x0f][sampleLUT[sample + 11] + subCarrierPhaseSam]);
652
653 auto v7 = (paletteToRaw[src3 >> 4 ][sampleLUT[sample + 12] + subCarrierPhaseSam] << 16) | (paletteToRaw[src3 >> 4 ][sampleLUT[sample + 13] + subCarrierPhaseSam]);
654 auto v8 = (paletteToRaw[src3 >> 4 ][sampleLUT[sample + 14] + subCarrierPhaseSam] << 16) | (paletteToRaw[src3 & 0x0f][sampleLUT[sample + 15] + subCarrierPhaseSam]);
655 auto v9 = (paletteToRaw[src3 & 0x0f][sampleLUT[sample + 16] + subCarrierPhaseSam] << 16) | (paletteToRaw[src3 & 0x0f][sampleLUT[sample + 17] + subCarrierPhaseSam]);
656
657 auto v10 = (paletteToRaw[src4 >> 4 ][sampleLUT[sample + 18] + subCarrierPhaseSam] << 16) | (paletteToRaw[src4 >> 4 ][sampleLUT[sample + 19] + subCarrierPhaseSam]);
658 auto v11 = (paletteToRaw[src4 >> 4 ][sampleLUT[sample + 20] + subCarrierPhaseSam] << 16) | (paletteToRaw[src4 & 0x0f][sampleLUT[sample + 21] + subCarrierPhaseSam]);
659 auto v12 = (paletteToRaw[src4 & 0x0f][sampleLUT[sample + 22] + subCarrierPhaseSam] << 16) | (paletteToRaw[src4 & 0x0f][sampleLUT[sample + 23] + subCarrierPhaseSam]);
660
661 *(dest32 + 0) = v1;
662 *(dest32 + 1) = v2;
663 *(dest32 + 2) = v3;
664 *(dest32 + 3) = v4;
665 *(dest32 + 4) = v5;
666 *(dest32 + 5) = v6;
667 *(dest32 + 6) = v7;
668 *(dest32 + 7) = v8;
669 *(dest32 + 8) = v9;
670 *(dest32 + 9) = v10;
671 *(dest32 + 10) = v11;
672 *(dest32 + 11) = v12;
673
674 dest32 += 12; // advance by 12x2=24 samples
675 src += 4; // advance by 4x2=8 pixels
676
677 }
678
679 if (scanLine >= height - 1 && !ctrl->m_primitiveProcessingSuspended && spi_flash_cache_enabled() && ctrl->m_primitiveExecTask) {
680 // vertical sync, unlock primitive execution task
681 // warn: don't use vTaskSuspendAll() in primitive drawing, otherwise vTaskNotifyGiveFromISR may be blocked and screen will flick!
682 vTaskNotifyGiveFromISR(ctrl->m_primitiveExecTask, NULL);
683 }
684
685}
686
687
688
689
690
691} // end of namespace
This file contains fabgl::CVBS16Controller definition.
uint8_t width
int16_t X
uint8_t swapFGBG
int16_t Y
uint8_t height
int16_t X1
Definition: fabutils.h:0
int16_t Y2
Definition: fabutils.h:3
int16_t X2
Definition: fabutils.h:2
int16_t Y1
Definition: fabutils.h:1
This file contains some utility classes and functions.
NativePixelFormat
This enum defines the display controller native pixel format.
Color
This enum defines named colors.