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 s_paletteToRawPixel[0] = s_paletteToRawPixel[1] = nullptr;
93}
94
95
96void CVBS16Controller::allocateViewPort()
97{
98 CVBSPalettedController::allocateViewPort();
99
100 for (int line = 0; line < 2; ++line) {
101 s_paletteToRawPixel[line] = (volatile uint16_t * *) heap_caps_malloc(sizeof(uint16_t *) * 16, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
102 for (int index = 0; index < 16; ++index)
103 s_paletteToRawPixel[line][index] = (uint16_t *) heap_caps_malloc(sizeof(uint16_t) * CVBS_SUBCARRIERPHASES * 2, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
104 }
105
106 switch (horizontalRate()) {
107 case 1:
108 setDrawScanlineCallback(drawScanlineX1);
109 break;
110 case 2:
111 setDrawScanlineCallback(drawScanlineX2);
112 break;
113 default:
114 setDrawScanlineCallback(drawScanlineX3);
115 break;
116 }
117
118 //printf("m_viewPortWidth = %d m_viewPortHeight = %d\n", m_viewPortWidth, m_viewPortHeight);
119 //printf("C. Free Memory : %d bytes\r\n\n", heap_caps_get_free_size(MALLOC_CAP_32BIT));
120}
121
122
123void CVBS16Controller::checkViewPortSize()
124{
125 CVBSPalettedController::checkViewPortSize();
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::setMonochrome(bool value)
139{
140 m_monochrome = value;
141 setupDefaultPalette();
142}
143
144
145void CVBS16Controller::setPaletteItem(int index, RGB888 const & color)
146{
147 if (s_paletteToRawPixel[0]) {
148 index %= 16;
149 m_palette[index] = color;
150
151 double range = params()->whiteLevel - params()->blackLevel + 1;
152
153 double r = color.R / 255.;
154 double g = color.G / 255.;
155 double b = color.B / 255.;
156
157 for (int line = 0; line < 2; ++line) {
158 for (int sample = 0; sample < CVBS_SUBCARRIERPHASES * 2; ++sample) {
159
160 double phase = 2. * M_PI * sample / CVBS_SUBCARRIERPHASES;
161
162 double Y;
163 double chroma = params()->getComposite(line == 0, phase, r, g, b, &Y);
164
165 // black/white?
166 if (m_monochrome)
167 chroma = 0;
168
169 s_paletteToRawPixel[line][index][sample] = (uint16_t)(params()->blackLevel + (Y + chroma) * range) << 8;
170 }
171 }
172 }
173}
174
175
176void CVBS16Controller::setPixelAt(PixelDesc const & pixelDesc, Rect & updateRect)
177{
178 genericSetPixelAt(pixelDesc, updateRect,
179 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
180 CVBS16_SETPIXEL
181 );
182}
183
184
185// coordinates are absolute values (not relative to origin)
186// line clipped on current absolute clipping rectangle
187void CVBS16Controller::absDrawLine(int X1, int Y1, int X2, int Y2, RGB888 color)
188{
189 genericAbsDrawLine(X1, Y1, X2, Y2, color,
190 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
191 [&] (int Y, int X1, int X2, uint8_t colorIndex) { rawFillRow(Y, X1, X2, colorIndex); },
192 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); },
193 CVBS16_SETPIXEL,
194 [&] (int X, int Y) { CVBS16_INVERT_PIXEL(X, Y); }
195 );
196}
197
198
199// parameters not checked
200void CVBS16Controller::rawFillRow(int y, int x1, int x2, RGB888 color)
201{
202 rawFillRow(y, x1, x2, RGB888toPaletteIndex(color));
203}
204
205
206// parameters not checked
207void CVBS16Controller::rawFillRow(int y, int x1, int x2, uint8_t colorIndex)
208{
209 uint8_t * row = (uint8_t*) m_viewPort[y];
210 // fill first pixels before full 16 bits word
211 int x = x1;
212 for (; x <= x2 && (x & 3) != 0; ++x) {
213 CVBS16_SETPIXELINROW(row, x, colorIndex);
214 }
215 // fill whole 16 bits words (4 pixels)
216 if (x <= x2) {
217 int sz = (x2 & ~3) - x;
218 memset((void*)(row + x / 2), colorIndex | (colorIndex << 4), sz / 2);
219 x += sz;
220 }
221 // fill last unaligned pixels
222 for (; x <= x2; ++x) {
223 CVBS16_SETPIXELINROW(row, x, colorIndex);
224 }
225}
226
227
228// parameters not checked
229void CVBS16Controller::rawInvertRow(int y, int x1, int x2)
230{
231 auto row = m_viewPort[y];
232 for (int x = x1; x <= x2; ++x)
233 CVBS16_INVERTPIXELINROW(row, x);
234}
235
236
237void CVBS16Controller::rawCopyRow(int x1, int x2, int srcY, int dstY)
238{
239 auto srcRow = (uint8_t*) m_viewPort[srcY];
240 auto dstRow = (uint8_t*) m_viewPort[dstY];
241 // copy first pixels before full 16 bits word
242 int x = x1;
243 for (; x <= x2 && (x & 3) != 0; ++x) {
244 CVBS16_SETPIXELINROW(dstRow, x, CVBS16_GETPIXELINROW(srcRow, x));
245 }
246 // copy whole 16 bits words (4 pixels)
247 auto src = (uint16_t*)(srcRow + x / 2);
248 auto dst = (uint16_t*)(dstRow + x / 2);
249 for (int right = (x2 & ~3); x < right; x += 4)
250 *dst++ = *src++;
251 // copy last unaligned pixels
252 for (x = (x2 & ~3); x <= x2; ++x) {
253 CVBS16_SETPIXELINROW(dstRow, x, CVBS16_GETPIXELINROW(srcRow, x));
254 }
255}
256
257
258void CVBS16Controller::swapRows(int yA, int yB, int x1, int x2)
259{
260 auto rowA = (uint8_t*) m_viewPort[yA];
261 auto rowB = (uint8_t*) m_viewPort[yB];
262 // swap first pixels before full 16 bits word
263 int x = x1;
264 for (; x <= x2 && (x & 3) != 0; ++x) {
265 uint8_t a = CVBS16_GETPIXELINROW(rowA, x);
266 uint8_t b = CVBS16_GETPIXELINROW(rowB, x);
267 CVBS16_SETPIXELINROW(rowA, x, b);
268 CVBS16_SETPIXELINROW(rowB, x, a);
269 }
270 // swap whole 16 bits words (4 pixels)
271 auto a = (uint16_t*)(rowA + x / 2);
272 auto b = (uint16_t*)(rowB + x / 2);
273 for (int right = (x2 & ~3); x < right; x += 4)
274 tswap(*a++, *b++);
275 // swap last unaligned pixels
276 for (x = (x2 & ~3); x <= x2; ++x) {
277 uint8_t a = CVBS16_GETPIXELINROW(rowA, x);
278 uint8_t b = CVBS16_GETPIXELINROW(rowB, x);
279 CVBS16_SETPIXELINROW(rowA, x, b);
280 CVBS16_SETPIXELINROW(rowB, x, a);
281 }
282}
283
284
285void CVBS16Controller::drawEllipse(Size const & size, Rect & updateRect)
286{
287 genericDrawEllipse(size, updateRect,
288 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
289 CVBS16_SETPIXEL
290 );
291}
292
293
294void CVBS16Controller::clear(Rect & updateRect)
295{
296 hideSprites(updateRect);
297 uint8_t paletteIndex = RGB888toPaletteIndex(getActualBrushColor());
298 uint8_t pattern = paletteIndex | (paletteIndex << 4);
299 for (int y = 0; y < m_viewPortHeight; ++y)
300 memset((uint8_t*) m_viewPort[y], pattern, m_viewPortWidth / 2);
301}
302
303
304// scroll < 0 -> scroll UP
305// scroll > 0 -> scroll DOWN
306void CVBS16Controller::VScroll(int scroll, Rect & updateRect)
307{
308 genericVScroll(scroll, updateRect,
309 [&] (int yA, int yB, int x1, int x2) { swapRows(yA, yB, x1, x2); }, // swapRowsCopying
310 [&] (int yA, int yB) { tswap(m_viewPort[yA], m_viewPort[yB]); }, // swapRowsPointers
311 [&] (int y, int x1, int x2, RGB888 color) { rawFillRow(y, x1, x2, color); } // rawFillRow
312 );
313}
314
315
316void CVBS16Controller::HScroll(int scroll, Rect & updateRect)
317{
318 hideSprites(updateRect);
319 uint8_t back4 = RGB888toPaletteIndex(getActualBrushColor());
320
321 int Y1 = paintState().scrollingRegion.Y1;
322 int Y2 = paintState().scrollingRegion.Y2;
323 int X1 = paintState().scrollingRegion.X1;
324 int X2 = paintState().scrollingRegion.X2;
325
326 int width = X2 - X1 + 1;
327 bool HScrolllingRegionAligned = ((X1 & 3) == 0 && (width & 3) == 0); // 4 pixels aligned
328
329 if (scroll < 0) {
330 // scroll left
331 for (int y = Y1; y <= Y2; ++y) {
332 if (HScrolllingRegionAligned) {
333 // aligned horizontal scrolling region, fast version
334 uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 2;
335 for (int s = -scroll; s > 0;) {
336 if (s > 1) {
337 // scroll left by 2, 4, 6, ... moving bytes
338 auto sc = s & ~1;
339 auto sz = width & ~1;
340 memmove(row, row + sc / 2, (sz - sc) / 2);
341 rawFillRow(y, X2 - sc + 1, X2, back4);
342 s -= sc;
343 } else if (s & 1) {
344 // scroll left 1 pixel (uint16_t at the time = 4 pixels)
345 // nibbles 0,1,2... P is prev or background
346 // byte : 01 23 45 67 -> 12 34 56 7P
347 // word (little endian CPU) : 2301 6745 -> 3412 7P56
348 auto prev = back4;
349 auto w = (uint16_t *) (row + width / 2) - 1;
350 for (int i = 0; i < width; i += 4) {
351 const uint16_t p4 = *w; // four pixels
352 *w-- = (p4 << 4 & 0xf000) | (prev << 8 & 0x0f00) | (p4 << 4 & 0x00f0) | (p4 >> 12 & 0x000f);
353 prev = p4 >> 4 & 0x000f;
354 }
355 --s;
356 }
357 }
358 } else {
359 // unaligned horizontal scrolling region, fallback to slow version
360 auto row = (uint8_t*) m_viewPort[y];
361 for (int x = X1; x <= X2 + scroll; ++x)
362 CVBS16_SETPIXELINROW(row, x, CVBS16_GETPIXELINROW(row, x - scroll));
363 // fill right area with brush color
364 rawFillRow(y, X2 + 1 + scroll, X2, back4);
365 }
366 }
367 } else if (scroll > 0) {
368 // scroll right
369 for (int y = Y1; y <= Y2; ++y) {
370 if (HScrolllingRegionAligned) {
371 // aligned horizontal scrolling region, fast version
372 uint8_t * row = (uint8_t*) (m_viewPort[y]) + X1 / 2;
373 for (int s = scroll; s > 0;) {
374 if (s > 1) {
375 // scroll right by 2, 4, 6, ... moving bytes
376 auto sc = s & ~1;
377 auto sz = width & ~1;
378 memmove(row + sc / 2, row, (sz - sc) / 2);
379 rawFillRow(y, X1, X1 + sc - 1, back4);
380 s -= sc;
381 } else if (s & 1) {
382 // scroll right 1 pixel (uint16_t at the time = 4 pixels)
383 // nibbles 0,1,2... P is prev or background
384 // byte : 01 23 45 67 -> P0 12 34 56 7...
385 // word (little endian CPU) : 2301 6745 -> 12P0 5634 ...
386 auto prev = back4;
387 auto w = (uint16_t *) row;
388 for (int i = 0; i < width; i += 4) {
389 const uint16_t p4 = *w; // four pixels
390 *w++ = (p4 << 12 & 0xf000) | (p4 >> 4 & 0x0f00) | (prev << 4) | (p4 >> 4 & 0x000f);
391 prev = p4 >> 8 & 0x000f;
392 }
393 --s;
394 }
395 }
396 } else {
397 // unaligned horizontal scrolling region, fallback to slow version
398 auto row = (uint8_t*) m_viewPort[y];
399 for (int x = X2 - scroll; x >= X1; --x)
400 CVBS16_SETPIXELINROW(row, x + scroll, CVBS16_GETPIXELINROW(row, x));
401 // fill left area with brush color
402 rawFillRow(y, X1, X1 + scroll - 1, back4);
403 }
404 }
405
406 }
407}
408
409
410void CVBS16Controller::drawGlyph(Glyph const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
411{
412 genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
413 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
414 [&] (int y) { return (uint8_t*) m_viewPort[y]; },
415 CVBS16_SETPIXELINROW
416 );
417}
418
419
420void CVBS16Controller::invertRect(Rect const & rect, Rect & updateRect)
421{
422 genericInvertRect(rect, updateRect,
423 [&] (int Y, int X1, int X2) { rawInvertRow(Y, X1, X2); }
424 );
425}
426
427
428void CVBS16Controller::swapFGBG(Rect const & rect, Rect & updateRect)
429{
430 genericSwapFGBG(rect, updateRect,
431 [&] (RGB888 const & color) { return RGB888toPaletteIndex(color); },
432 [&] (int y) { return (uint8_t*) m_viewPort[y]; },
433 CVBS16_GETPIXELINROW,
434 CVBS16_SETPIXELINROW
435 );
436}
437
438
439// Slow operation!
440// supports overlapping of source and dest rectangles
441void CVBS16Controller::copyRect(Rect const & source, Rect & updateRect)
442{
443 genericCopyRect(source, updateRect,
444 [&] (int y) { return (uint8_t*) m_viewPort[y]; },
445 CVBS16_GETPIXELINROW,
446 CVBS16_SETPIXELINROW
447 );
448}
449
450
451// no bounds check is done!
452void CVBS16Controller::readScreen(Rect const & rect, RGB888 * destBuf)
453{
454 for (int y = rect.Y1; y <= rect.Y2; ++y) {
455 auto row = (uint8_t*) m_viewPort[y];
456 for (int x = rect.X1; x <= rect.X2; ++x, ++destBuf) {
457 const RGB222 v = m_palette[CVBS16_GETPIXELINROW(row, x)];
458 *destBuf = RGB888(v.R * 85, v.G * 85, v.B * 85); // 85 x 3 = 255
459 }
460 }
461}
462
463
464void CVBS16Controller::rawDrawBitmap_Native(int destX, int destY, Bitmap const * bitmap, int X1, int Y1, int XCount, int YCount)
465{
466 genericRawDrawBitmap_Native(destX, destY, (uint8_t*) bitmap->data, bitmap->width, X1, Y1, XCount, YCount,
467 [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
468 CVBS16_SETPIXELINROW
469 );
470}
471
472
473void CVBS16Controller::rawDrawBitmap_Mask(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
474{
475 auto foregroundColorIndex = RGB888toPaletteIndex(bitmap->foregroundColor);
476 genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
477 [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
478 CVBS16_GETPIXELINROW,
479 [&] (uint8_t * row, int x) { CVBS16_SETPIXELINROW(row, x, foregroundColorIndex); } // rawSetPixelInRow
480 );
481}
482
483
484void CVBS16Controller::rawDrawBitmap_RGBA2222(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
485{
486 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
487 [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
488 CVBS16_GETPIXELINROW,
489 [&] (uint8_t * row, int x, uint8_t src) { CVBS16_SETPIXELINROW(row, x, RGB2222toPaletteIndex(src)); } // rawSetPixelInRow
490 );
491}
492
493
494void CVBS16Controller::rawDrawBitmap_RGBA8888(int destX, int destY, Bitmap const * bitmap, void * saveBackground, int X1, int Y1, int XCount, int YCount)
495{
496 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint8_t*)saveBackground, X1, Y1, XCount, YCount,
497 [&] (int y) { return (uint8_t*) m_viewPort[y]; }, // rawGetRow
498 [&] (uint8_t * row, int x) { return CVBS16_GETPIXELINROW(row, x); }, // rawGetPixelInRow
499 [&] (uint8_t * row, int x, RGBA8888 const & src) { CVBS16_SETPIXELINROW(row, x, RGB8888toPaletteIndex(src)); } // rawSetPixelInRow
500 );
501}
502
503
504void IRAM_ATTR CVBS16Controller::drawScanlineX1(void * arg, uint16_t * dest, int destSample, int scanLine)
505{
506 auto ctrl = (CVBS16Controller *) arg;
507
508 auto const width = ctrl->m_viewPortWidth;
509
510 auto src = (uint8_t const *) s_viewPortVisible[scanLine];
511 auto dest32 = (uint32_t*) (dest + destSample);
512
513 int subCarrierPhaseSam = CVBSGenerator::subCarrierPhase();
514 auto paletteToRaw = (uint16_t * *) s_paletteToRawPixel[CVBSGenerator::lineSwitch()];
515 auto sampleLUT = CVBSGenerator::lineSampleToSubCarrierSample() + destSample;
516
517 // optimization warn: horizontal resolution must be a multiple of 16!
518 for (int col = 0; col < width; col += 16) {
519
520 auto src1 = *(src + 0);
521 auto src2 = *(src + 1);
522 auto src3 = *(src + 2);
523 auto src4 = *(src + 3);
524 auto src5 = *(src + 4);
525 auto src6 = *(src + 5);
526 auto src7 = *(src + 6);
527 auto src8 = *(src + 7);
528
529 PSRAM_HACK;
530
531 *dest32++ = (paletteToRaw[src1 >> 4][*sampleLUT + subCarrierPhaseSam] << 16) | (paletteToRaw[src1 & 0x0f][*(sampleLUT + 1) + subCarrierPhaseSam]);
532 sampleLUT += 2;
533 *dest32++ = (paletteToRaw[src2 >> 4][*sampleLUT + subCarrierPhaseSam] << 16) | (paletteToRaw[src2 & 0x0f][*(sampleLUT + 1) + subCarrierPhaseSam]);
534 sampleLUT += 2;
535 *dest32++ = (paletteToRaw[src3 >> 4][*sampleLUT + subCarrierPhaseSam] << 16) | (paletteToRaw[src3 & 0x0f][*(sampleLUT + 1) + subCarrierPhaseSam]);
536 sampleLUT += 2;
537 *dest32++ = (paletteToRaw[src4 >> 4][*sampleLUT + subCarrierPhaseSam] << 16) | (paletteToRaw[src4 & 0x0f][*(sampleLUT + 1) + subCarrierPhaseSam]);
538 sampleLUT += 2;
539 *dest32++ = (paletteToRaw[src5 >> 4][*sampleLUT + subCarrierPhaseSam] << 16) | (paletteToRaw[src5 & 0x0f][*(sampleLUT + 1) + subCarrierPhaseSam]);
540 sampleLUT += 2;
541 *dest32++ = (paletteToRaw[src6 >> 4][*sampleLUT + subCarrierPhaseSam] << 16) | (paletteToRaw[src6 & 0x0f][*(sampleLUT + 1) + subCarrierPhaseSam]);
542 sampleLUT += 2;
543 *dest32++ = (paletteToRaw[src7 >> 4][*sampleLUT + subCarrierPhaseSam] << 16) | (paletteToRaw[src7 & 0x0f][*(sampleLUT + 1) + subCarrierPhaseSam]);
544 sampleLUT += 2;
545 *dest32++ = (paletteToRaw[src8 >> 4][*sampleLUT + subCarrierPhaseSam] << 16) | (paletteToRaw[src8 & 0x0f][*(sampleLUT + 1) + subCarrierPhaseSam]);
546 sampleLUT += 2;
547
548 src += 8; // advance by 8x2=16 pixels
549
550 }
551
552 if (CVBSGenerator::VSync() && !ctrl->m_primitiveProcessingSuspended && spi_flash_cache_enabled() && ctrl->m_primitiveExecTask) {
553 // vertical sync, unlock primitive execution task
554 // warn: don't use vTaskSuspendAll() in primitive drawing, otherwise vTaskNotifyGiveFromISR may be blocked and screen will flick!
555 vTaskNotifyGiveFromISR(ctrl->m_primitiveExecTask, NULL);
556 }
557}
558
559
560void IRAM_ATTR CVBS16Controller::drawScanlineX2(void * arg, uint16_t * dest, int destSample, int scanLine)
561{
562 auto ctrl = (CVBS16Controller *) arg;
563
564 auto const width = ctrl->m_viewPortWidth * 2;
565
566 auto src = (uint8_t const *) s_viewPortVisible[scanLine];
567 auto dest32 = (uint32_t*) (dest + destSample);
568
569 int subCarrierPhaseSam = CVBSGenerator::subCarrierPhase();
570 auto paletteToRaw = (uint16_t * *) s_paletteToRawPixel[CVBSGenerator::lineSwitch()];
571 auto sampleLUT = CVBSGenerator::lineSampleToSubCarrierSample() + destSample;
572
573 /*
574 if (CVBSGenerator::field() == 0) {
575 dest += destSample;
576 for (int i = 0; i < width; ++i)
577 *dest++ = 0x1900;
578 } else
579 */
580
581 // optimization warn: horizontal resolution must be a multiple of 8!
582 for (int col = 0; col < width; col += 16) {
583
584 auto src1 = *(src + 0);
585 auto src2 = *(src + 1);
586 auto src3 = *(src + 2);
587 auto src4 = *(src + 3);
588
589 PSRAM_HACK;
590
591 auto praw = paletteToRaw[src1 >> 4] + subCarrierPhaseSam;
592 *dest32++ = (praw[*sampleLUT] << 16) | (praw[*(sampleLUT + 1)]);
593 sampleLUT += 2;
594
595 praw = paletteToRaw[src1 & 0x0f] + subCarrierPhaseSam;
596 *dest32++ = (praw[*sampleLUT] << 16) | (praw[*(sampleLUT + 1)]);
597 sampleLUT += 2;
598
599 praw = paletteToRaw[src2 >> 4] + subCarrierPhaseSam;
600 *dest32++ = (praw[*sampleLUT] << 16) | (praw[*(sampleLUT + 1)]);
601 sampleLUT += 2;
602
603 praw = paletteToRaw[src2 & 0x0f] + subCarrierPhaseSam;
604 *dest32++ = (praw[*sampleLUT] << 16) | (praw[*(sampleLUT + 1)]);
605 sampleLUT += 2;
606
607 praw = paletteToRaw[src3 >> 4] + subCarrierPhaseSam;
608 *dest32++ = (praw[*sampleLUT] << 16) | (praw[*(sampleLUT + 1)]);
609 sampleLUT += 2;
610
611 praw = paletteToRaw[src3 & 0x0f] + subCarrierPhaseSam;
612 *dest32++ = (praw[*sampleLUT] << 16) | (praw[*(sampleLUT + 1)]);
613 sampleLUT += 2;
614
615 praw = paletteToRaw[src4 >> 4] + subCarrierPhaseSam;
616 *dest32++ = (praw[*sampleLUT] << 16) | (praw[*(sampleLUT + 1)]);
617 sampleLUT += 2;
618
619 praw = paletteToRaw[src4 & 0x0f] + subCarrierPhaseSam;
620 *dest32++ = (praw[*sampleLUT] << 16) | (praw[*(sampleLUT + 1)]);
621 sampleLUT += 2;
622
623 src += 4; // advance by 4x2=8 pixels
624
625 }
626
627 if (CVBSGenerator::VSync() && !ctrl->m_primitiveProcessingSuspended && spi_flash_cache_enabled() && ctrl->m_primitiveExecTask) {
628 // vertical sync, unlock primitive execution task
629 // warn: don't use vTaskSuspendAll() in primitive drawing, otherwise vTaskNotifyGiveFromISR may be blocked and screen will flick!
630 vTaskNotifyGiveFromISR(ctrl->m_primitiveExecTask, NULL);
631 }
632
633}
634
635
636void IRAM_ATTR CVBS16Controller::drawScanlineX3(void * arg, uint16_t * dest, int destSample, int scanLine)
637{
638 auto ctrl = (CVBS16Controller *) arg;
639
640 auto const width = ctrl->m_viewPortWidth * 3;
641
642 auto src = (uint8_t const *) s_viewPortVisible[scanLine];
643 auto dest32 = (uint32_t*) (dest + destSample);
644
645 int subCarrierPhaseSam = CVBSGenerator::subCarrierPhase();
646 auto paletteToRaw = (uint16_t * *) s_paletteToRawPixel[CVBSGenerator::lineSwitch()];
647 auto sampleLUT = CVBSGenerator::lineSampleToSubCarrierSample() + destSample;
648
649 // optimization warn: horizontal resolution must be a multiple of 8!
650 for (int col = 0; col < width; col += 24) {
651
652 auto src1 = *(src + 0);
653 auto src2 = *(src + 1);
654 auto src3 = *(src + 2);
655 auto src4 = *(src + 3);
656
657 PSRAM_HACK;
658
659 auto prawl = paletteToRaw[src1 >> 4] + subCarrierPhaseSam;
660 auto prawr = paletteToRaw[src1 & 0x0f] + subCarrierPhaseSam;
661 *dest32++ = (prawl[*sampleLUT] << 16) | (prawl[*(sampleLUT + 1)]);
662 sampleLUT += 2;
663 *dest32++ = (prawl[*sampleLUT] << 16) | (prawr[*(sampleLUT + 1)]);
664 sampleLUT += 2;
665 *dest32++ = (prawr[*sampleLUT] << 16) | (prawr[*(sampleLUT + 1)]);
666 sampleLUT += 2;
667
668 prawl = paletteToRaw[src2 >> 4] + subCarrierPhaseSam;
669 prawr = paletteToRaw[src2 & 0x0f] + subCarrierPhaseSam;
670 *dest32++ = (prawl[*sampleLUT] << 16) | (prawl[*(sampleLUT + 1)]);
671 sampleLUT += 2;
672 *dest32++ = (prawl[*sampleLUT] << 16) | (prawr[*(sampleLUT + 1)]);
673 sampleLUT += 2;
674 *dest32++ = (prawr[*sampleLUT] << 16) | (prawr[*(sampleLUT + 1)]);
675 sampleLUT += 2;
676
677 prawl = paletteToRaw[src3 >> 4] + subCarrierPhaseSam;
678 prawr = paletteToRaw[src3 & 0x0f] + subCarrierPhaseSam;
679 *dest32++ = (prawl[*sampleLUT] << 16) | (prawl[*(sampleLUT + 1)]);
680 sampleLUT += 2;
681 *dest32++ = (prawl[*sampleLUT] << 16) | (prawr[*(sampleLUT + 1)]);
682 sampleLUT += 2;
683 *dest32++ = (prawr[*sampleLUT] << 16) | (prawr[*(sampleLUT + 1)]);
684 sampleLUT += 2;
685
686 prawl = paletteToRaw[src4 >> 4] + subCarrierPhaseSam;
687 prawr = paletteToRaw[src4 & 0x0f] + subCarrierPhaseSam;
688 *dest32++ = (prawl[*sampleLUT] << 16) | (prawl[*(sampleLUT + 1)]);
689 sampleLUT += 2;
690 *dest32++ = (prawl[*sampleLUT] << 16) | (prawr[*(sampleLUT + 1)]);
691 sampleLUT += 2;
692 *dest32++ = (prawr[*sampleLUT] << 16) | (prawr[*(sampleLUT + 1)]);
693 sampleLUT += 2;
694
695 src += 4; // advance by 4x2=8 pixels
696
697 }
698
699 if (CVBSGenerator::VSync() && !ctrl->m_primitiveProcessingSuspended && spi_flash_cache_enabled() && ctrl->m_primitiveExecTask) {
700 // vertical sync, unlock primitive execution task
701 // warn: don't use vTaskSuspendAll() in primitive drawing, otherwise vTaskNotifyGiveFromISR may be blocked and screen will flick!
702 vTaskNotifyGiveFromISR(ctrl->m_primitiveExecTask, NULL);
703 }
704}
705
706
707
708
709
710} // end of namespace
This file contains fabgl::CVBS16Controller definition.
uint8_t width
int16_t X
uint8_t swapFGBG
int16_t Y
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.