FabGL
ESP32 Display Controller and Graphics Library
cvbspalettedcontroller.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
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 "fabutils.h"
37
38
39
40#pragma GCC optimize ("O2")
41
42
43
44namespace fabgl {
45
46
47
48
49
50/*************************************************************************************/
51/* CVBSPalettedController definitions */
52
53
54volatile uint8_t * * CVBSPalettedController::s_viewPort;
55volatile uint8_t * * CVBSPalettedController::s_viewPortVisible;
56
57
58
59
60CVBSPalettedController::CVBSPalettedController(int columnsQuantum, NativePixelFormat nativePixelFormat, int viewPortRatioDiv, int viewPortRatioMul)
61 : CVBSBaseController(),
62 m_columnsQuantum(columnsQuantum),
63 m_nativePixelFormat(nativePixelFormat),
64 m_viewPortRatioDiv(viewPortRatioDiv),
65 m_viewPortRatioMul(viewPortRatioMul)
66{
67 m_palette = (RGB222*) heap_caps_malloc(sizeof(RGB222) * getPaletteSize(), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
68}
69
70
71CVBSPalettedController::~CVBSPalettedController()
72{
73 heap_caps_free(m_palette);
74}
75
76
77void CVBSPalettedController::init()
78{
79 CVBSBaseController::init();
80
81 m_taskProcessingPrimitives = false;
82 m_processPrimitivesOnBlank = false;
83 m_primitiveExecTask = nullptr;
84}
85
86
87void CVBSPalettedController::end()
88{
89 if (m_primitiveExecTask) {
90 vTaskDelete(m_primitiveExecTask);
91 m_primitiveExecTask = nullptr;
92 m_taskProcessingPrimitives = false;
93 }
94 CVBSBaseController::end();
95}
96
97
98void CVBSPalettedController::suspendBackgroundPrimitiveExecution()
99{
100 CVBSBaseController::suspendBackgroundPrimitiveExecution();
101 while (m_taskProcessingPrimitives)
102 ;
103}
104
105// make sure view port width is divisible by m_columnsQuantum
106void CVBSPalettedController::checkViewPortSize()
107{
108 m_viewPortWidth &= ~(m_columnsQuantum - 1);
109}
110
111
112void CVBSPalettedController::allocateViewPort()
113{
114 CVBSBaseController::allocateViewPort(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL, m_viewPortWidth / m_viewPortRatioDiv * m_viewPortRatioMul);
115}
116
117
118void CVBSPalettedController::freeViewPort()
119{
120 CVBSBaseController::freeViewPort();
121}
122
123
124void CVBSPalettedController::setResolution(CVBSParams const * params, int viewPortWidth, int viewPortHeight, bool doubleBuffered)
125{
126 CVBSBaseController::setResolution(params, viewPortWidth, viewPortHeight, doubleBuffered);
127
128 s_viewPort = m_viewPort;
129 s_viewPortVisible = m_viewPortVisible;
130
131 // fill view port
132 for (int i = 0; i < m_viewPortHeight; ++i)
133 memset((void*)(m_viewPort[i]), 0, m_viewPortWidth / m_viewPortRatioDiv * m_viewPortRatioMul);
134
135 setupDefaultPalette();
136 updateRGB2PaletteLUT();
137
138 calculateAvailableCyclesForDrawings();
139
140 if (m_primitiveExecTask == nullptr) {
141 xTaskCreatePinnedToCore(primitiveExecTask, "" , FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_STACK_SIZE, this, FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_PRIORITY, &m_primitiveExecTask, CoreUsage::quietCore());
142 }
143
144 resumeBackgroundPrimitiveExecution();
145
146 run();
147}
148
149
150int CVBSPalettedController::getPaletteSize()
151{
152 switch (nativePixelFormat()) {
153 case NativePixelFormat::PALETTE2:
154 return 2;
155 case NativePixelFormat::PALETTE4:
156 return 4;
157 case NativePixelFormat::PALETTE8:
158 return 8;
159 case NativePixelFormat::PALETTE16:
160 return 16;
161 default:
162 return 0;
163 }
164}
165
166
167// rebuild m_packedRGB222_to_PaletteIndex
168void CVBSPalettedController::updateRGB2PaletteLUT()
169{
170 auto paletteSize = getPaletteSize();
171 for (int r = 0; r < 4; ++r)
172 for (int g = 0; g < 4; ++g)
173 for (int b = 0; b < 4; ++b) {
174 double H1, S1, V1;
175 rgb222_to_hsv(r, g, b, &H1, &S1, &V1);
176 int bestIdx = 0;
177 int bestDst = 1000000000;
178 for (int i = 0; i < paletteSize; ++i) {
179 double H2, S2, V2;
180 rgb222_to_hsv(m_palette[i].R, m_palette[i].G, m_palette[i].B, &H2, &S2, &V2);
181 double AH = H1 - H2;
182 double AS = S1 - S2;
183 double AV = V1 - V2;
184 int dst = AH * AH + AS * AS + AV * AV;
185 if (dst <= bestDst) { // "<=" to prioritize higher indexes
186 bestIdx = i;
187 bestDst = dst;
188 if (bestDst == 0)
189 break;
190 }
191 }
192 m_packedRGB222_to_PaletteIndex[r | (g << 2) | (b << 4)] = bestIdx;
193 }
194}
195
196
197// calculates number of CPU cycles usable to draw primitives
198void CVBSPalettedController::calculateAvailableCyclesForDrawings()
199{
200 int availtime_us;
201
202 if (m_processPrimitivesOnBlank) {
203 // allowed time to process primitives is limited to the vertical blank. Slow, but avoid flickering
204 availtime_us = params()->blankLines * params()->line_us;
205 } else {
206 // allowed time is the half of an entire frame. Fast, but may flick
207 availtime_us = params()->fieldLines * params()->line_us;
208 }
209
210 availtime_us = params()->blankLines * params()->line_us;
211
212 m_primitiveExecTimeoutCycles = getCPUFrequencyMHz() * availtime_us; // at 240Mhz, there are 240 cycles every microsecond
213}
214
215
216// we can use getCycleCount here because primitiveExecTask is pinned to a specific core (so cycle counter is the same)
217// getCycleCount() requires 0.07us, while esp_timer_get_time() requires 0.78us
218void CVBSPalettedController::primitiveExecTask(void * arg)
219{
220 auto ctrl = (CVBSPalettedController *) arg;
221
222 while (true) {
223 if (!ctrl->m_primitiveProcessingSuspended) {
224 auto startCycle = ctrl->backgroundPrimitiveTimeoutEnabled() ? getCycleCount() : 0;
225 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
226 ctrl->m_taskProcessingPrimitives = true;
227 do {
228 Primitive prim;
229 if (ctrl->getPrimitive(&prim, 0) == false)
230 break;
231 ctrl->execPrimitive(prim, updateRect, false);
232 if (ctrl->m_primitiveProcessingSuspended)
233 break;
234 } while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startCycle + ctrl->m_primitiveExecTimeoutCycles > getCycleCount()));
235 ctrl->showSprites(updateRect);
236 ctrl->m_taskProcessingPrimitives = false;
237 }
238
239 // wait for vertical sync
240 ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
241 }
242
243}
244
245
246void CVBSPalettedController::swapBuffers()
247{
248 CVBSBaseController::swapBuffers();
249 s_viewPort = m_viewPort;
250 s_viewPortVisible = m_viewPortVisible;
251}
252
253
254
255} // end of namespace
256
This file contains fabgl::CVBSPalettedController definition.
uint8_t B
uint8_t G
uint8_t R
#define FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_PRIORITY
Definition: fabglconf.h:150
#define FABGLIB_VGAPALETTEDCONTROLLER_PRIMTASK_STACK_SIZE
Definition: fabglconf.h:146
This file contains some utility classes and functions.
NativePixelFormat
This enum defines the display controller native pixel format.