| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 3 | * 2013-2022 by the Falcon Player Developers. | ||
| 4 | * | ||
| 5 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 6 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 7 | * file for descriptions of what files are covered by each license. | ||
| 8 | * | ||
| 9 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 10 | * included LICENSE.LGPL file. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "fpp-pch.h" | ||
| 14 | |||
| 15 | #include "fpp-json.h" | ||
| 16 | |||
| 17 | #include <sys/ioctl.h> | ||
| 18 | #include <sys/mman.h> | ||
| 19 | #include <sys/socket.h> | ||
| 20 | #include <sys/uio.h> | ||
| 21 | #include <algorithm> | ||
| 22 | #include <chrono> | ||
| 23 | #include <cstring> | ||
| 24 | #include <ctime> | ||
| 25 | #include <errno.h> | ||
| 26 | #include <fcntl.h> | ||
| 27 | #include <mutex> | ||
| 28 | #include <stdlib.h> | ||
| 29 | #include <string> | ||
| 30 | #include <thread> | ||
| 31 | #include <unistd.h> | ||
| 32 | |||
| 33 | #include "Warnings.h" | ||
| 34 | #include "common.h" | ||
| 35 | #include "log.h" | ||
| 36 | #include "settings.h" | ||
| 37 | #include "commands/Commands.h" | ||
| 38 | |||
| 39 | #include "FrameBuffer.h" | ||
| 40 | #include "IOCTLFrameBuffer.h" | ||
| 41 | #include "KMSFrameBuffer.h" | ||
| 42 | #include "SocketFrameBuffer.h" | ||
| 43 | #include "X11FrameBuffer.h" | ||
| 44 | |||
| 45 | #define FB_CURRENT_PAGE_PTR (m_pageBuffers[m_cPage]) | ||
| 46 | |||
| 47 | /* | ||
| 48 | * Usage: | ||
| 49 | * | ||
| 50 | * AutoSync Off: | ||
| 51 | * | ||
| 52 | * Client -> FBCopyData(); | ||
| 53 | * Client -> FBSyncDisplay(); | ||
| 54 | * FrameBuffer::DrawLoop() calls transition effect | ||
| 55 | * - None -> NextPage(false); copy output buffer, SyncDisplay(); | ||
| 56 | * - Others -> loop(copy output buffer, SyncDisplay()) | ||
| 57 | * (effects only work on a single m_cPage page) | ||
| 58 | * | ||
| 59 | * AutoSync On: | ||
| 60 | * Client: | ||
| 61 | * page = Page(true); | ||
| 62 | * if IsDirty(page) then can't do anything, else do... | ||
| 63 | * NextPage(true) | ||
| 64 | * copy data to BufferPage(page) | ||
| 65 | * SetDirty(page) | ||
| 66 | * FrameBuffer::SyncLoop*(): | ||
| 67 | * - if page m_cPage is dirty, then output and increment else just sleep/wait | ||
| 68 | * | ||
| 69 | */ | ||
| 70 | |||
| 71 | void StartDrawLoopThread(FrameBuffer* fb); | ||
| 72 | |||
| 73 | /* | ||
| 74 | * | ||
| 75 | */ | ||
| 76 | 75 | FrameBuffer::FrameBuffer() { | |
| 77 |
1/2✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
|
75 | LogDebug(VB_CHANNELOUT, "FrameBuffer::FrameBuffer()\n"); |
| 78 | |||
| 79 | 75 | m_typeSeed = (unsigned int)time(NULL); | |
| 80 | 75 | } | |
| 81 | |||
| 82 | /* | ||
| 83 | * | ||
| 84 | */ | ||
| 85 | 72 | void FrameBuffer::StopDrawLoop(void) { | |
| 86 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 33 times.
|
72 | if (!m_drawThread) { |
| 87 | 39 | m_runLoop = false; | |
| 88 | 39 | return; | |
| 89 | } | ||
| 90 | |||
| 91 | // Flip the predicate under the same lock DrawLoop() waits on, otherwise a | ||
| 92 | // notify that lands between its m_runLoop check and its wait() is lost and | ||
| 93 | // the join() below never returns. | ||
| 94 | { | ||
| 95 |
1/2✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
|
33 | std::unique_lock<std::mutex> lock(m_bufferLock); |
| 96 | 33 | m_runLoop = false; | |
| 97 | 33 | } | |
| 98 | 33 | m_drawSignal.notify_all(); | |
| 99 | |||
| 100 | 33 | m_drawThread->join(); | |
| 101 |
1/2✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
|
33 | delete m_drawThread; |
| 102 | 33 | m_drawThread = nullptr; | |
| 103 | } | ||
| 104 | |||
| 105 | 72 | FrameBuffer::~FrameBuffer() { | |
| 106 | // Subclasses stop the loop in their own destructors (where their state is | ||
| 107 | // still alive); this covers a bare FrameBuffer and is a no-op once they have. | ||
| 108 | 72 | StopDrawLoop(); | |
| 109 | |||
| 110 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 3 times.
|
72 | if (m_outputBuffer) { |
| 111 | 66 | free(m_outputBuffer); | |
| 112 | 66 | m_outputBuffer = nullptr; | |
| 113 | } | ||
| 114 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 3 times.
|
72 | if (m_stagingBuffer) { |
| 115 | 66 | free(m_stagingBuffer); | |
| 116 | 66 | m_stagingBuffer = nullptr; | |
| 117 | } | ||
| 118 | |||
| 119 | // Only the base teardown - the vtable is already demoted to FrameBuffer here, | ||
| 120 | // so this can never reach a subclass override. Subclasses call their own | ||
| 121 | // DestroyFrameBuffer() from their destructor; this is the no-subclass case | ||
| 122 | // and a no-op once one has run (it clears m_fbFd). | ||
| 123 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
72 | if (m_fbFd > -1) { |
| 124 | ✗ | FrameBuffer::DestroyFrameBuffer(); | |
| 125 | } | ||
| 126 | |||
| 127 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
72 | if (m_dirtyPages) { |
| 128 | ✗ | delete[] m_dirtyPages; | |
| 129 | ✗ | m_dirtyPages = nullptr; | |
| 130 | } | ||
| 131 | 72 | } | |
| 132 | |||
| 133 | /* | ||
| 134 | * | ||
| 135 | */ | ||
| 136 | 75 | int FrameBuffer::FBInit(const Json::Value& config) { | |
| 137 |
2/2✓ Branch 0 taken 61 times.
✓ Branch 1 taken 14 times.
|
75 | if (config.isMember("Name")) |
| 138 |
2/4✓ Branch 0 taken 61 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 61 times.
✗ Branch 3 not taken.
|
61 | m_name = config["Name"].asString(); |
| 139 | |||
| 140 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | if (config.isMember("X")) |
| 141 | ✗ | m_xOffset = config["X"].asInt(); | |
| 142 | |||
| 143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | if (config.isMember("Y")) |
| 144 | ✗ | m_yOffset = config["Y"].asInt(); | |
| 145 | |||
| 146 |
1/2✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
|
75 | if (config.isMember("Width")) |
| 147 | 75 | m_width = config["Width"].asInt(); | |
| 148 | |||
| 149 |
1/2✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
|
75 | if (config.isMember("Height")) |
| 150 | 75 | m_height = config["Height"].asInt(); | |
| 151 | |||
| 152 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 62 times.
|
75 | if (config.isMember("PixelSize")) |
| 153 | 13 | m_pixelSize = config["PixelSize"].asInt(); | |
| 154 | |||
| 155 |
1/2✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
|
75 | if (config.isMember("Device")) |
| 156 |
2/4✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
|
75 | m_device = config["Device"].asString(); |
| 157 | |||
| 158 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 48 times.
|
75 | if (config.isMember("TransitionType")) |
| 159 |
2/4✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
|
27 | m_transitionType = (ImageTransitionType)(atoi(config["TransitionType"].asString().c_str())); |
| 160 | |||
| 161 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 74 times.
|
75 | if (config.isMember("Pages")) { |
| 162 | // Clamp at the one place the page count enters from config. Only | ||
| 163 | // m_pageBuffers[0..MAX_PAGES-1] are ever assigned, and NextPage() walks | ||
| 164 | // m_cPage modulo m_pages, so an out-of-range count hands the draw path | ||
| 165 | // an index with no buffer behind it. | ||
| 166 | 1 | int p = config["Pages"].asInt(); | |
| 167 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | if (p < 1 || p > MAX_PAGES) { |
| 168 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | LogWarn(VB_CHANNELOUT, "FrameBuffer: Pages=%d out of range, using %d\n", |
| 169 | p, p < 1 ? 1 : MAX_PAGES); | ||
| 170 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | p = p < 1 ? 1 : MAX_PAGES; |
| 171 | } | ||
| 172 | 1 | m_pages = p; | |
| 173 | } | ||
| 174 | |||
| 175 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 61 times.
|
75 | if (config.isMember("BitsPerPixel")) |
| 176 | 14 | m_bpp = config["BitsPerPixel"].asInt(); | |
| 177 | |||
| 178 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 75 times.
|
75 | if (config.isMember("AutoSync")) |
| 179 | ✗ | m_autoSync = config["AutoSync"].asBool(); | |
| 180 | |||
| 181 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 61 times.
|
75 | if (config.isMember("VariableRefresh")) |
| 182 | 14 | m_variableRefresh = config["VariableRefresh"].asBool(); | |
| 183 | |||
| 184 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 62 times.
|
75 | if (m_pixelSize > 0) { |
| 185 | 13 | m_pixelsWide = m_width / m_pixelSize; | |
| 186 | 13 | m_pixelsHigh = m_height / m_pixelSize; | |
| 187 | |||
| 188 | // Adjust our Width/Height to be whole numbers | ||
| 189 | 13 | m_width = m_pixelSize * m_pixelsWide; | |
| 190 | 13 | m_height = m_pixelSize * m_pixelsHigh; | |
| 191 | } else { | ||
| 192 | 62 | m_pixelsWide = m_width; | |
| 193 | 62 | m_pixelsHigh = m_height; | |
| 194 | } | ||
| 195 | |||
| 196 | 75 | int result = InitializeFrameBuffer(); | |
| 197 |
2/2✓ Branch 0 taken 38 times.
✓ Branch 1 taken 37 times.
|
75 | if (m_pixelSize == 0) { |
| 198 | 38 | m_pixelSize = 1; | |
| 199 | } | ||
| 200 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 72 times.
|
75 | if (!result) |
| 201 | 3 | return 0; | |
| 202 | |||
| 203 | 72 | ClipToPage(); | |
| 204 | |||
| 205 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
|
72 | if (m_autoSync) { |
| 206 | ✗ | m_dirtyPages = new volatile uint8_t[m_pages]; | |
| 207 | ✗ | for (int i = 0; i < m_pages; i++) | |
| 208 | ✗ | m_dirtyPages[i] = 0; | |
| 209 | } | ||
| 210 | |||
| 211 | 72 | m_outputBuffer = (uint8_t*)calloc(1, m_pageSize); | |
| 212 | 72 | m_stagingBuffer = (uint8_t*)calloc(1, m_pageSize); | |
| 213 |
2/4✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
|
72 | if (!m_outputBuffer || !m_stagingBuffer) { |
| 214 | ✗ | LogErr(VB_CHANNELOUT, "Error, unable to allocate outputBuffer buffer\n"); | |
| 215 | ✗ | DestroyFrameBuffer(); | |
| 216 | ✗ | return 0; | |
| 217 | } | ||
| 218 | |||
| 219 | 72 | LogDebug(VB_CHANNELOUT, "Initialized %s frame buffer at %dx%d resolution.\n", | |
| 220 | m_device.c_str(), m_width, m_height); | ||
| 221 | |||
| 222 | 72 | Dump(); | |
| 223 | 72 | m_runLoop = true; | |
| 224 |
2/6✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
72 | m_drawThread = new std::thread(StartDrawLoopThread, this); |
| 225 | 72 | return 1; | |
| 226 | } | ||
| 227 | |||
| 228 | 74 | FrameBuffer* FrameBuffer::createFrameBuffer(const Json::Value& config) { | |
| 229 | 74 | FrameBuffer* fb = nullptr; | |
| 230 | 74 | std::string device; | |
| 231 |
2/4✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
|
74 | if (config.isMember("Device")) { |
| 232 |
2/4✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
|
74 | device = config["Device"].asString(); |
| 233 | } | ||
| 234 | |||
| 235 |
1/2✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
|
74 | LogDebug(VB_CHANNELOUT, "FrameBuffer::createFrameBuffer for device='%s'\n", device.c_str()); |
| 236 | |||
| 237 | #ifdef USE_X11 | ||
| 238 | fb = new X11FrameBuffer(); | ||
| 239 | if (fb->FBInit(config)) { | ||
| 240 | return fb; | ||
| 241 | } | ||
| 242 | delete fb; | ||
| 243 | fb = nullptr; | ||
| 244 | #endif | ||
| 245 | #ifdef USE_FRAMEBUFFER_SOCKET | ||
| 246 | fb = new SocketFrameBuffer(); | ||
| 247 | if (fb->FBInit(config)) { | ||
| 248 | return fb; | ||
| 249 | } | ||
| 250 | delete fb; | ||
| 251 | fb = nullptr; | ||
| 252 | #endif | ||
| 253 | #ifdef HAS_KMS_FB | ||
| 254 | // DPI needs KMS for direct pixel output | ||
| 255 | // For HDMI: Try KMS first for full-resolution displays (better performance) | ||
| 256 | // Small models will fall through to IOCTL which has auto-scaling | ||
| 257 | 74 | bool isDPI = (device.find("DPI") != std::string::npos); | |
| 258 | 74 | bool isSmallModel = false; | |
| 259 | |||
| 260 | // Check if this is a small model that needs scaling | ||
| 261 |
5/10✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 74 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 74 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 74 times.
✗ Branch 9 not taken.
|
74 | if (config.isMember("Width") && config.isMember("Height")) { |
| 262 |
2/4✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
|
74 | int width = config["Width"].asInt(); |
| 263 |
2/4✓ Branch 0 taken 74 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
✗ Branch 3 not taken.
|
74 | int height = config["Height"].asInt(); |
| 264 | // Consider anything smaller than 1280x720 as "small" needing potential scaling | ||
| 265 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
74 | isSmallModel = (width < 1280 || height < 720); |
| 266 | } | ||
| 267 | |||
| 268 | // #2713: named DRM connectors (DSI/HDMI/DPI/DisplayPort) can only be driven | ||
| 269 | // correctly via KMS (it scales any model size onto the panel and page-flips, | ||
| 270 | // so it scans out reliably). IOCTL fbdev cannot present a buffer smaller than | ||
| 271 | // the panel mode and does not reliably scan out on vc4, so never route a | ||
| 272 | // connector-named device to IOCTL. | ||
| 273 |
2/2✓ Branch 0 taken 59 times.
✓ Branch 1 taken 2 times.
|
61 | bool isConnector = isDPI || |
| 274 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 8 times.
|
120 | device.find("DSI") != std::string::npos || |
| 275 |
3/4✓ Branch 0 taken 61 times.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 51 times.
|
194 | device.find("HDMI") != std::string::npos || |
| 276 | 51 | device.find("DisplayPort") != std::string::npos; | |
| 277 |
4/6✓ Branch 0 taken 51 times.
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 51 times.
|
74 | bool tryKMS = isConnector || device.empty() || !isSmallModel; |
| 278 | |||
| 279 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 51 times.
|
74 | if (tryKMS) { |
| 280 |
3/8✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 23 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
23 | fb = new KMSFrameBuffer(); |
| 281 |
3/4✓ Branch 0 taken 23 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 1 times.
|
23 | if (fb->FBInit(config)) { |
| 282 | 22 | return fb; | |
| 283 | } | ||
| 284 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | delete fb; |
| 285 | 1 | fb = nullptr; | |
| 286 | } | ||
| 287 | #endif | ||
| 288 | #ifdef HAS_IOCTL_FB | ||
| 289 |
3/8✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 52 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
52 | fb = new IOCTLFrameBuffer(); |
| 290 |
3/4✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 2 times.
|
52 | if (fb->FBInit(config)) { |
| 291 | 50 | return fb; | |
| 292 | } | ||
| 293 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | delete fb; |
| 294 | 2 | fb = nullptr; | |
| 295 | #endif | ||
| 296 | 2 | return nullptr; | |
| 297 | 74 | } | |
| 298 | |||
| 299 | static constexpr int WARNING_ID_FB_CROPPED = 67; | ||
| 300 | |||
| 301 | // Everything that writes a page - FBCopyData() and the transitions - must stay | ||
| 302 | // inside it, and the page is whatever the device gave us (m_rowStride bytes by | ||
| 303 | // m_pageSize / m_rowStride rows), which need not be what the model asked for: | ||
| 304 | // IOCTLFrameBuffer deliberately keeps the framebuffer at its native size. Clip | ||
| 305 | // once here, after the backend has sized the page, rather than in each loop. | ||
| 306 | 72 | void FrameBuffer::ClipToPage() { | |
| 307 | 72 | m_drawCols = m_pixelsWide; | |
| 308 | 72 | m_drawRows = m_pixelsHigh; | |
| 309 | 72 | int bytesPerPixel = m_bpp / 8; | |
| 310 |
3/6✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 72 times.
|
72 | if (m_rowStride <= 0 || bytesPerPixel <= 0 || m_pixelSize <= 0) { |
| 311 | ✗ | return; | |
| 312 | } | ||
| 313 | 72 | int pageCols = m_rowStride / bytesPerPixel; | |
| 314 | 72 | int pageRows = m_pageSize / m_rowStride; | |
| 315 | |||
| 316 | 72 | m_drawCols = std::min(m_pixelsWide, pageCols / m_pixelSize); | |
| 317 | 72 | m_drawRows = std::min(m_pixelsHigh, pageRows / m_pixelSize); | |
| 318 | // The transitions work in device pixels over the same page-sized buffers. | ||
| 319 | 72 | m_width = std::min(m_width, pageCols); | |
| 320 | 72 | m_height = std::min(m_height, pageRows); | |
| 321 | |||
| 322 |
3/4✓ Branch 0 taken 71 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 71 times.
|
72 | if (m_drawCols < m_pixelsWide || m_drawRows < m_pixelsHigh) { |
| 323 |
4/8✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
2 | m_clipWarning = "Framebuffer " + m_device + ": the " + std::to_string(m_pixelsWide) + "x" + |
| 324 |
4/8✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
4 | std::to_string(m_pixelsHigh) + " model does not fit the " + std::to_string(pageCols) + "x" + |
| 325 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
4 | std::to_string(pageRows) + " display, so only the top-left " + std::to_string(m_drawCols) + |
| 326 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
3 | "x" + std::to_string(m_drawRows) + " is shown"; |
| 327 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | LogWarn(VB_CHANNELOUT, "%s\n", m_clipWarning.c_str()); |
| 328 | // Lasts as long as this framebuffer; DestroyFrameBuffer() takes it down. | ||
| 329 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | WarningHolder::AddWarning(WARNING_ID_FB_CROPPED, m_clipWarning); |
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | 36 | void FrameBuffer::DestroyFrameBuffer(void) { | |
| 334 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | if (!m_clipWarning.empty()) { |
| 335 | ✗ | WarningHolder::RemoveWarning(WARNING_ID_FB_CROPPED, m_clipWarning); | |
| 336 | ✗ | m_clipWarning.clear(); | |
| 337 | } | ||
| 338 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 21 times.
|
36 | if (m_buffer) { |
| 339 | 15 | memset(m_buffer, 0, m_bufferSize); | |
| 340 | 15 | munmap(m_buffer, m_bufferSize); | |
| 341 | 15 | m_buffer = nullptr; | |
| 342 | } | ||
| 343 | |||
| 344 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 20 times.
|
36 | if (m_fbFd != -1) { |
| 345 | 16 | close(m_fbFd); | |
| 346 | 16 | m_fbFd = -1; | |
| 347 | } | ||
| 348 | 36 | } | |
| 349 | |||
| 350 | /* | ||
| 351 | * | ||
| 352 | */ | ||
| 353 | 2311 | void FrameBuffer::FBCopyData(const uint8_t* buffer, int draw) { | |
| 354 | 2311 | int drow = 0; | |
| 355 | 2311 | const uint8_t* sR = buffer + 0; // Input always RGB | |
| 356 | 2311 | const uint8_t* sG = buffer + 1; | |
| 357 | 2311 | const uint8_t* sB = buffer + 2; | |
| 358 | 2311 | uint8_t* ob = m_stagingBuffer; | |
| 359 | |||
| 360 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 2311 times.
✓ Branch 2 taken 2311 times.
✗ Branch 3 not taken.
|
2311 | std::unique_lock<std::mutex> lock(draw ? m_bufferLock : m_stagingLock); |
| 361 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2311 times.
|
2311 | if (draw) { |
| 362 | ✗ | ob = (m_pixelSize == 1) ? FB_CURRENT_PAGE_PTR : m_outputBuffer; | |
| 363 | } | ||
| 364 | uint8_t* dR; | ||
| 365 | uint8_t* dG; | ||
| 366 | uint8_t* dB; | ||
| 367 | 2311 | int add = m_bpp / 8; | |
| 368 | |||
| 369 | // Rows and columns past m_drawRows/m_drawCols do not fit the page (see | ||
| 370 | // ClipToPage()); the source is still walked at its full m_pixelsWide. | ||
| 371 | 2311 | int skip = (m_pixelsWide - m_drawCols) * 3; | |
| 372 |
2/2✓ Branch 0 taken 79114 times.
✓ Branch 1 taken 2311 times.
|
81425 | for (int y = 0; y < m_drawRows; y++) { |
| 373 | // Output is BGR(A) | ||
| 374 | 79114 | dB = ob + (drow * m_pixelSize * m_rowStride); | |
| 375 | 79114 | dG = dB + 1; | |
| 376 | 79114 | dR = dB + 2; | |
| 377 | |||
| 378 |
2/2✓ Branch 0 taken 5454112 times.
✓ Branch 1 taken 79114 times.
|
5533226 | for (int x = 0; x < m_drawCols; x++) { |
| 379 |
2/2✓ Branch 0 taken 95288160 times.
✓ Branch 1 taken 5454112 times.
|
100742272 | for (int sc = 0; sc < m_pixelSize; sc++) { |
| 380 | 95288160 | *dR = *sR; | |
| 381 | 95288160 | *dG = *sG; | |
| 382 | 95288160 | *dB = *sB; | |
| 383 | |||
| 384 | 95288160 | dR += add; | |
| 385 | 95288160 | dG += add; | |
| 386 | 95288160 | dB += add; | |
| 387 | } | ||
| 388 | |||
| 389 | 5454112 | sR += 3; | |
| 390 | 5454112 | sG += 3; | |
| 391 | 5454112 | sB += 3; | |
| 392 | } | ||
| 393 | 79114 | sR += skip; | |
| 394 | 79114 | sG += skip; | |
| 395 | 79114 | sB += skip; | |
| 396 | |||
| 397 | 79114 | dB = ob + (drow * m_pixelSize * m_rowStride); | |
| 398 |
2/2✓ Branch 0 taken 1355248 times.
✓ Branch 1 taken 79114 times.
|
1434362 | for (int sc = 1; sc < m_pixelSize; sc++) { |
| 399 | 1355248 | memcpy(dB + (sc * m_rowStride), dB, m_rowStride); | |
| 400 | } | ||
| 401 | |||
| 402 | 79114 | drow++; | |
| 403 | } | ||
| 404 | |||
| 405 |
1/2✓ Branch 0 taken 2311 times.
✗ Branch 1 not taken.
|
2311 | lock.unlock(); |
| 406 |
1/2✓ Branch 0 taken 2311 times.
✗ Branch 1 not taken.
|
2311 | if (!draw) { |
| 407 | 2311 | m_imageReady = true; | |
| 408 | } | ||
| 409 | 2311 | } | |
| 410 | |||
| 411 | /* | ||
| 412 | * | ||
| 413 | */ | ||
| 414 | 72 | void FrameBuffer::Dump(void) { | |
| 415 | 72 | LogDebug(VB_CHANNELOUT, "Device : %s\n", m_device.c_str()); | |
| 416 | 72 | LogDebug(VB_CHANNELOUT, "Width : %d\n", m_width); | |
| 417 | 72 | LogDebug(VB_CHANNELOUT, "Height : %d\n", m_height); | |
| 418 | 72 | LogDebug(VB_CHANNELOUT, "PixelSize : %d\n", m_pixelSize); | |
| 419 | 72 | LogDebug(VB_CHANNELOUT, "Pixels Wide : %d\n", m_pixelsWide); | |
| 420 | 72 | LogDebug(VB_CHANNELOUT, "Pixels High : %d\n", m_pixelsHigh); | |
| 421 | 72 | LogDebug(VB_CHANNELOUT, "bpp : %d\n", m_bpp); | |
| 422 | 72 | LogDebug(VB_CHANNELOUT, "Transition Type: %d\n", (int)m_transitionType); | |
| 423 | 72 | } | |
| 424 | |||
| 425 | /* | ||
| 426 | * | ||
| 427 | */ | ||
| 428 | 2313 | void FrameBuffer::FBStartDraw(ImageTransitionType transitionType) { | |
| 429 |
1/2✓ Branch 0 taken 2313 times.
✗ Branch 1 not taken.
|
2313 | if (transitionType == IT_Default) |
| 430 | 2313 | transitionType = m_transitionType; | |
| 431 | |||
| 432 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2313 times.
|
2313 | if (transitionType == IT_Random) |
| 433 | ✗ | transitionType = (ImageTransitionType)(rand_r(&m_typeSeed) % ((int)IT_MAX - 1) + 1); | |
| 434 | |||
| 435 | 2313 | m_nextTransitionType = transitionType; | |
| 436 | |||
| 437 | 2313 | m_drawSignal.notify_all(); | |
| 438 | 2313 | } | |
| 439 | |||
| 440 | /* | ||
| 441 | * | ||
| 442 | */ | ||
| 443 | 72 | void StartDrawLoopThread(FrameBuffer* fb) { | |
| 444 | 72 | fb->DrawLoop(); | |
| 445 | 33 | } | |
| 446 | |||
| 447 | 72 | void FrameBuffer::DrawLoop(void) { | |
| 448 |
2/4✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
|
72 | SetThreadName("FPP-FBDrawLoop"); |
| 449 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
|
72 | if (m_autoSync) { |
| 450 | ✗ | SyncLoop(); | |
| 451 | ✗ | return; | |
| 452 | } | ||
| 453 | |||
| 454 |
1/2✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
|
72 | std::unique_lock<std::mutex> lock(m_bufferLock); |
| 455 |
2/2✓ Branch 0 taken 5049 times.
✓ Branch 1 taken 33 times.
|
5082 | while (m_runLoop) { |
| 456 | // Taken before drawing, not cleared after: an image that arrives while | ||
| 457 | // a transition runs (they take most of a second) was marked done along | ||
| 458 | // with the one being drawn, and never reached the screen. | ||
| 459 |
2/2✓ Branch 0 taken 1764 times.
✓ Branch 1 taken 3285 times.
|
5049 | if (m_imageReady.exchange(false)) { |
| 460 |
1/2✓ Branch 0 taken 1764 times.
✗ Branch 1 not taken.
|
1764 | lock.unlock(); |
| 461 | { | ||
| 462 |
1/2✓ Branch 0 taken 1764 times.
✗ Branch 1 not taken.
|
1764 | std::lock_guard<std::mutex> staged(m_stagingLock); |
| 463 | 1764 | memcpy(m_outputBuffer, m_stagingBuffer, m_pageSize); | |
| 464 | 1764 | } | |
| 465 | // m_nextTransitionType = IT_WipeFromHCenter; | ||
| 466 |
14/15✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 7 times.
✓ Branch 7 taken 4 times.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 4 times.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 4 times.
✓ Branch 14 taken 1709 times.
|
1764 | switch (m_nextTransitionType) { |
| 467 | ✗ | case IT_Random: | |
| 468 | ✗ | m_nextTransitionType = (ImageTransitionType)(rand_r(&m_typeSeed) % ((int)IT_MAX - 1) + 1); | |
| 469 | ✗ | m_imageReady = true; | |
| 470 | ✗ | lock.lock(); | |
| 471 | ✗ | continue; | |
| 472 | break; | ||
| 473 | |||
| 474 | 4 | case IT_SlideUp: | |
| 475 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawSlideUp(); |
| 476 | 4 | break; | |
| 477 | |||
| 478 | 4 | case IT_SlideDown: | |
| 479 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawSlideDown(); |
| 480 | 4 | break; | |
| 481 | |||
| 482 | 4 | case IT_SlideLeft: | |
| 483 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawSlideLeft(); |
| 484 | 4 | break; | |
| 485 | |||
| 486 | 4 | case IT_SlideRight: | |
| 487 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawSlideRight(); |
| 488 | 4 | break; | |
| 489 | |||
| 490 | 4 | case IT_WipeUp: | |
| 491 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawWipeUp(); |
| 492 | 4 | break; | |
| 493 | |||
| 494 | 7 | case IT_WipeDown: | |
| 495 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | FBDrawWipeDown(); |
| 496 | 7 | break; | |
| 497 | |||
| 498 | 4 | case IT_WipeLeft: | |
| 499 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawWipeLeft(); |
| 500 | 4 | break; | |
| 501 | |||
| 502 | 4 | case IT_WipeRight: | |
| 503 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawWipeRight(); |
| 504 | 4 | break; | |
| 505 | |||
| 506 | 4 | case IT_WipeToHCenter: | |
| 507 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawWipeToHCenter(); |
| 508 | 4 | break; | |
| 509 | |||
| 510 | 4 | case IT_WipeFromHCenter: | |
| 511 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawWipeFromHCenter(); |
| 512 | 4 | break; | |
| 513 | |||
| 514 | 4 | case IT_HorzBlindsOpen: | |
| 515 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawHorzBlindsOpen(); |
| 516 | 4 | break; | |
| 517 | |||
| 518 | 4 | case IT_HorzBlindsClose: | |
| 519 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawHorzBlindsClose(); |
| 520 | 4 | break; | |
| 521 | |||
| 522 | 4 | case IT_Mosaic: | |
| 523 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | FBDrawMosaic(); |
| 524 | 4 | break; | |
| 525 | |||
| 526 | 1709 | default: | |
| 527 |
1/2✓ Branch 0 taken 1709 times.
✗ Branch 1 not taken.
|
1709 | FBDrawNormal(); |
| 528 | 1709 | break; | |
| 529 | } | ||
| 530 | |||
| 531 |
1/2✓ Branch 0 taken 1764 times.
✗ Branch 1 not taken.
|
1764 | lock.lock(); |
| 532 |
2/2✓ Branch 0 taken 55 times.
✓ Branch 1 taken 1709 times.
|
1764 | if (m_nextTransitionType != IT_Normal) { |
| 533 | // Whatever a transition's step sizes left undrawn, it ends | ||
| 534 | // with the whole image on screen. | ||
| 535 | 55 | memcpy(FB_CURRENT_PAGE_PTR, m_outputBuffer, m_pageSize); | |
| 536 |
1/2✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
|
55 | SyncDisplay(); |
| 537 | } | ||
| 538 | 1764 | continue; | |
| 539 | 1764 | } | |
| 540 | |||
| 541 | // FBStartDraw() notifies without m_bufferLock, which a transition holds | ||
| 542 | // for its whole length, so a notify can land between the check above | ||
| 543 | // and the wait; the timeout bounds how long such an image waits. | ||
| 544 |
1/2✓ Branch 0 taken 3246 times.
✗ Branch 1 not taken.
|
3285 | m_drawSignal.wait_for(lock, std::chrono::milliseconds(50), |
| 545 |
4/4✓ Branch 0 taken 6498 times.
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 1203 times.
✓ Branch 3 taken 5295 times.
|
6531 | [this] { return !m_runLoop || m_imageReady; }); |
| 546 | } | ||
| 547 | 33 | } | |
| 548 | |||
| 549 | 2489 | void FrameBuffer::NextPage(bool producer) { | |
| 550 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2488 times.
|
2489 | if (m_pages == 1) |
| 551 | 1 | return; | |
| 552 | |||
| 553 | // LogInfo(VB_CHANNELOUT, "%s - %d - NextPage() for %s\n", m_device.c_str(), m_cPage, producer ? "producer" : "consumer"); | ||
| 554 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2488 times.
|
2488 | if (producer) |
| 555 | ✗ | m_pPage = (m_pPage + 1) % m_pages; | |
| 556 | else | ||
| 557 | 2488 | m_cPage = (m_cPage + 1) % m_pages; | |
| 558 | } | ||
| 559 | |||
| 560 | /* | ||
| 561 | * | ||
| 562 | */ | ||
| 563 | 1709 | void FrameBuffer::FBDrawNormal(void) { | |
| 564 | 1709 | m_bufferLock.lock(); | |
| 565 | |||
| 566 | 1709 | NextPage(); | |
| 567 | 1709 | memcpy(FB_CURRENT_PAGE_PTR, m_outputBuffer, m_pageSize); | |
| 568 | 1709 | SyncDisplay(true); | |
| 569 | 1709 | m_bufferLock.unlock(); | |
| 570 | 1709 | } | |
| 571 | |||
| 572 | /* | ||
| 573 | * | ||
| 574 | */ | ||
| 575 | 4 | void FrameBuffer::FBDrawSlideUp(void) { | |
| 576 | 4 | int rEach = 2; | |
| 577 | 4 | int sleepTime = 800 * 1000 * rEach / m_height; | |
| 578 | |||
| 579 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | if ((m_height % 4) == 0) |
| 580 | 2 | rEach = 4; | |
| 581 | |||
| 582 | 4 | m_bufferLock.lock(); | |
| 583 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 4 times.
|
52 | for (int i = m_height - rEach; i >= 0;) { |
| 584 | 48 | memcpy(FB_CURRENT_PAGE_PTR + (m_rowStride * i), m_outputBuffer, m_rowStride * (m_height - i)); | |
| 585 | 48 | SyncDisplay(); | |
| 586 | |||
| 587 | 48 | usleep(sleepTime); | |
| 588 | |||
| 589 | 48 | i -= rEach; | |
| 590 | } | ||
| 591 | 4 | m_bufferLock.unlock(); | |
| 592 | 4 | } | |
| 593 | |||
| 594 | /* | ||
| 595 | * | ||
| 596 | */ | ||
| 597 | 4 | void FrameBuffer::FBDrawSlideDown(void) { | |
| 598 | 4 | int rEach = 2; | |
| 599 | 4 | int sleepTime = 800 * 1000 * rEach / m_height; | |
| 600 | |||
| 601 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | if ((m_height % 4) == 0) |
| 602 | 2 | rEach = 4; | |
| 603 | |||
| 604 | 4 | m_bufferLock.lock(); | |
| 605 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 4 times.
|
52 | for (int i = rEach; i <= m_height;) { |
| 606 | 48 | memcpy(FB_CURRENT_PAGE_PTR, m_outputBuffer + (m_rowStride * (m_height - i)), m_rowStride * i); | |
| 607 | 48 | SyncDisplay(); | |
| 608 | |||
| 609 | 48 | usleep(sleepTime); | |
| 610 | |||
| 611 | 48 | i += rEach; | |
| 612 | } | ||
| 613 | 4 | m_bufferLock.unlock(); | |
| 614 | 4 | } | |
| 615 | |||
| 616 | /* | ||
| 617 | * | ||
| 618 | */ | ||
| 619 | 4 | void FrameBuffer::FBDrawSlideLeft(void) { | |
| 620 | 4 | int colsEach = 2; | |
| 621 | 4 | int sleepTime = 800 * 1000 * colsEach / m_width; | |
| 622 | |||
| 623 |
2/2✓ Branch 0 taken 112 times.
✓ Branch 1 taken 4 times.
|
116 | for (int x = m_width - colsEach; x >= 0; x -= colsEach) { |
| 624 | 112 | DrawSquare(x, 0, m_width - x, m_height, 0, 0); | |
| 625 | 112 | SyncDisplay(); | |
| 626 | |||
| 627 | 112 | usleep(sleepTime); | |
| 628 | } | ||
| 629 | 4 | } | |
| 630 | |||
| 631 | /* | ||
| 632 | * | ||
| 633 | */ | ||
| 634 | 4 | void FrameBuffer::FBDrawSlideRight(void) { | |
| 635 | 4 | int colsEach = 2; | |
| 636 | 4 | int sleepTime = 800 * 1000 * colsEach / m_width; | |
| 637 | |||
| 638 |
2/2✓ Branch 0 taken 112 times.
✓ Branch 1 taken 4 times.
|
116 | for (int x = 2; x <= m_width; x += colsEach) { |
| 639 | 112 | DrawSquare(0, 0, x, m_height, m_width - x, 0); | |
| 640 | 112 | SyncDisplay(); | |
| 641 | |||
| 642 | 112 | usleep(sleepTime); | |
| 643 | } | ||
| 644 | 4 | } | |
| 645 | |||
| 646 | /* | ||
| 647 | * | ||
| 648 | */ | ||
| 649 | 4 | void FrameBuffer::FBDrawWipeUp(void) { | |
| 650 | 4 | int rEach = 2; | |
| 651 | |||
| 652 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | if ((m_height % 4) == 0) |
| 653 | 2 | rEach = 4; | |
| 654 | |||
| 655 | 4 | int sleepTime = 800 * 1000 * rEach / m_height; | |
| 656 | |||
| 657 | 4 | m_bufferLock.lock(); | |
| 658 |
2/2✓ Branch 0 taken 48 times.
✓ Branch 1 taken 4 times.
|
52 | for (int i = m_height - rEach; i >= 0;) { |
| 659 | 48 | CopyRows(i, rEach); | |
| 660 | 48 | SyncDisplay(); | |
| 661 | |||
| 662 | 48 | usleep(sleepTime); | |
| 663 | |||
| 664 | 48 | i -= rEach; | |
| 665 | } | ||
| 666 | 4 | m_bufferLock.unlock(); | |
| 667 | 4 | } | |
| 668 | |||
| 669 | /* | ||
| 670 | * | ||
| 671 | */ | ||
| 672 | 7 | void FrameBuffer::FBDrawWipeDown(void) { | |
| 673 | 7 | int rEach = 2; | |
| 674 | |||
| 675 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
|
7 | if ((m_height % 4) == 0) |
| 676 | 5 | rEach = 4; | |
| 677 | |||
| 678 | 7 | int sleepTime = 800 * 1000 * rEach / m_height; | |
| 679 | |||
| 680 | 7 | m_bufferLock.lock(); | |
| 681 |
2/2✓ Branch 0 taken 74 times.
✓ Branch 1 taken 7 times.
|
81 | for (int i = 0; i < m_height;) { |
| 682 | 74 | CopyRows(i, rEach); | |
| 683 | 74 | SyncDisplay(); | |
| 684 | |||
| 685 | 74 | usleep(sleepTime); | |
| 686 | |||
| 687 | 74 | i += rEach; | |
| 688 | } | ||
| 689 | 7 | m_bufferLock.unlock(); | |
| 690 | 7 | } | |
| 691 | |||
| 692 | /* | ||
| 693 | * | ||
| 694 | */ | ||
| 695 | 4 | void FrameBuffer::FBDrawWipeLeft(void) { | |
| 696 | 4 | int BPP = m_bpp / 8; | |
| 697 | |||
| 698 | 4 | m_bufferLock.lock(); | |
| 699 | |||
| 700 |
2/2✓ Branch 0 taken 224 times.
✓ Branch 1 taken 4 times.
|
228 | for (int x = m_width - 1; x >= 0; x--) { |
| 701 |
2/2✓ Branch 0 taken 7264 times.
✓ Branch 1 taken 224 times.
|
7488 | for (int y = 0; y < m_height; y++) { |
| 702 | 7264 | memcpy(FB_CURRENT_PAGE_PTR + (m_rowStride * y) + (x * BPP), | |
| 703 | 7264 | m_outputBuffer + (m_rowStride * y) + (x * BPP), BPP); | |
| 704 | } | ||
| 705 | 224 | SyncDisplay(); | |
| 706 | } | ||
| 707 | |||
| 708 | 4 | m_bufferLock.unlock(); | |
| 709 | 4 | } | |
| 710 | |||
| 711 | /* | ||
| 712 | * | ||
| 713 | */ | ||
| 714 | 4 | void FrameBuffer::FBDrawWipeRight(void) { | |
| 715 | 4 | int BPP = m_bpp / 8; | |
| 716 | |||
| 717 | 4 | m_bufferLock.lock(); | |
| 718 | |||
| 719 |
2/2✓ Branch 0 taken 224 times.
✓ Branch 1 taken 4 times.
|
228 | for (int x = 0; x < m_width; x++) { |
| 720 |
2/2✓ Branch 0 taken 7264 times.
✓ Branch 1 taken 224 times.
|
7488 | for (int y = 0; y < m_height; y++) { |
| 721 | 7264 | memcpy(FB_CURRENT_PAGE_PTR + (m_rowStride * y) + (x * BPP), | |
| 722 | 7264 | m_outputBuffer + (m_rowStride * y) + (x * BPP), BPP); | |
| 723 | } | ||
| 724 | 224 | SyncDisplay(); | |
| 725 | } | ||
| 726 | |||
| 727 | 4 | m_bufferLock.unlock(); | |
| 728 | 4 | } | |
| 729 | |||
| 730 | /* | ||
| 731 | * | ||
| 732 | */ | ||
| 733 | 4 | void FrameBuffer::FBDrawWipeToHCenter(void) { | |
| 734 | 4 | int rEach = 2; | |
| 735 | 4 | int mid = m_height / 2; | |
| 736 | |||
| 737 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | if ((m_height % 4) == 0) |
| 738 | 2 | rEach = 4; | |
| 739 | |||
| 740 | 4 | int sleepTime = 800 * 1000 * rEach / m_height * 2; | |
| 741 | |||
| 742 | 4 | m_bufferLock.lock(); | |
| 743 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 4 times.
|
28 | for (int i = 0; i < mid; i += rEach) { |
| 744 | 24 | CopyRows(i, rEach); | |
| 745 | 24 | CopyRows(m_height - i - rEach, rEach); | |
| 746 | 24 | SyncDisplay(); | |
| 747 | |||
| 748 | 24 | usleep(sleepTime); | |
| 749 | } | ||
| 750 | 4 | m_bufferLock.unlock(); | |
| 751 | 4 | } | |
| 752 | |||
| 753 | /* | ||
| 754 | * | ||
| 755 | */ | ||
| 756 | 4 | void FrameBuffer::FBDrawWipeFromHCenter(void) { | |
| 757 | 4 | int mid = m_height / 2; | |
| 758 | 4 | int rowsEachUpdate = 2; | |
| 759 | 4 | int sleepTime = 800 * 1000 * rowsEachUpdate / m_height * 2; | |
| 760 | |||
| 761 | 4 | m_bufferLock.lock(); | |
| 762 | |||
| 763 | 4 | int t = mid - 2; | |
| 764 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 4 times.
|
38 | for (int b = mid; b < m_height; b += rowsEachUpdate, t -= rowsEachUpdate) { |
| 765 | 34 | CopyRows(b, rowsEachUpdate); | |
| 766 | 34 | CopyRows(t, rowsEachUpdate); | |
| 767 | |||
| 768 | 34 | SyncDisplay(); | |
| 769 | |||
| 770 | 34 | usleep(sleepTime); | |
| 771 | } | ||
| 772 | |||
| 773 | 4 | m_bufferLock.unlock(); | |
| 774 | 4 | } | |
| 775 | |||
| 776 | /* | ||
| 777 | * | ||
| 778 | */ | ||
| 779 | 4 | void FrameBuffer::FBDrawHorzBlindsOpen(void) { | |
| 780 | 4 | int rowsEachUpdate = 2; | |
| 781 | 4 | int blindSize = 32; | |
| 782 | 4 | int sleepTime = 800 * 1000 * rowsEachUpdate / blindSize; | |
| 783 | |||
| 784 | 4 | m_bufferLock.lock(); | |
| 785 |
2/2✓ Branch 0 taken 64 times.
✓ Branch 1 taken 4 times.
|
68 | for (int i = blindSize - rowsEachUpdate; i >= 0; i -= rowsEachUpdate) { |
| 786 |
2/2✓ Branch 0 taken 66 times.
✓ Branch 1 taken 64 times.
|
130 | for (int y = i; y < m_height; y += blindSize) { |
| 787 | 66 | CopyRows(y, rowsEachUpdate); | |
| 788 | } | ||
| 789 | |||
| 790 | 64 | SyncDisplay(); | |
| 791 | |||
| 792 | 64 | usleep(sleepTime); | |
| 793 | } | ||
| 794 | 4 | m_bufferLock.unlock(); | |
| 795 | 4 | } | |
| 796 | |||
| 797 | /* | ||
| 798 | * | ||
| 799 | */ | ||
| 800 | 4 | void FrameBuffer::FBDrawHorzBlindsClose(void) { | |
| 801 | 4 | int rowsEachUpdate = 2; | |
| 802 | 4 | int blindSize = 32; | |
| 803 | 4 | int sleepTime = 800 * 1000 * rowsEachUpdate / blindSize; | |
| 804 | |||
| 805 | 4 | m_bufferLock.lock(); | |
| 806 |
2/2✓ Branch 0 taken 64 times.
✓ Branch 1 taken 4 times.
|
68 | for (int i = 0; i < blindSize; i += rowsEachUpdate) { |
| 807 |
2/2✓ Branch 0 taken 66 times.
✓ Branch 1 taken 64 times.
|
130 | for (int y = i; y < m_height; y += blindSize) { |
| 808 | 66 | CopyRows(y, rowsEachUpdate); | |
| 809 | } | ||
| 810 | |||
| 811 | 64 | SyncDisplay(); | |
| 812 | |||
| 813 | 64 | usleep(sleepTime); | |
| 814 | } | ||
| 815 | 4 | m_bufferLock.unlock(); | |
| 816 | 4 | } | |
| 817 | |||
| 818 | /* | ||
| 819 | * | ||
| 820 | */ | ||
| 821 | 4 | void FrameBuffer::FBDrawMosaic(void) { | |
| 822 | 4 | int squareSize = 32; | |
| 823 | 4 | int xSquares = (m_width + squareSize - 1) / squareSize; | |
| 824 | 4 | int ySquares = (m_height + squareSize - 1) / squareSize; | |
| 825 | 4 | std::vector<std::vector<int>> squares; | |
| 826 |
2/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | squares.resize(ySquares, std::vector<int>(xSquares, 0)); |
| 827 | |||
| 828 | 4 | int count = xSquares * ySquares; | |
| 829 | 4 | int sleepTime = 800 * 1000 / count; | |
| 830 | |||
| 831 | |||
| 832 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | m_bufferLock.lock(); |
| 833 | |||
| 834 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 4 times.
|
124 | for (int i = 0; i < (count * 10); i++) { |
| 835 | 120 | int square = rand_r(&m_typeSeed) % count; | |
| 836 | 120 | int x = square % xSquares; | |
| 837 | 120 | int y = square / xSquares; | |
| 838 | |||
| 839 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 108 times.
|
120 | if (!squares[y][x]) { |
| 840 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | DrawSquare(x * squareSize, y * squareSize, squareSize, squareSize); |
| 841 | 12 | squares[y][x] = 1; | |
| 842 | |||
| 843 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | SyncDisplay(); |
| 844 | |||
| 845 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | usleep(sleepTime); |
| 846 | } | ||
| 847 | } | ||
| 848 | |||
| 849 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
|
10 | for (int y = 0; y < ySquares; y++) { |
| 850 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | for (int x = 0; x < xSquares; x++) { |
| 851 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!squares[y][x]) { |
| 852 | ✗ | DrawSquare(x * squareSize, y * squareSize, squareSize, | |
| 853 | squareSize); | ||
| 854 | ✗ | squares[y][x] = 1; | |
| 855 | |||
| 856 | ✗ | SyncDisplay(); | |
| 857 | |||
| 858 | ✗ | usleep(sleepTime); | |
| 859 | } | ||
| 860 | } | ||
| 861 | } | ||
| 862 | |||
| 863 | 4 | m_bufferLock.unlock(); | |
| 864 | 4 | } | |
| 865 | |||
| 866 | ///////////////////////////////////////////////////////////////////////////// | ||
| 867 | |||
| 868 | /* | ||
| 869 | * | ||
| 870 | */ | ||
| 871 | 236 | void FrameBuffer::DrawSquare(int dx, int dy, int w, int h, int sx, int sy) { | |
| 872 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 224 times.
|
236 | if (sx == -1) |
| 873 | 12 | sx = dx; | |
| 874 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 224 times.
|
236 | if (sy == -1) |
| 875 | 12 | sy = dy; | |
| 876 | // Both rectangles stay on the page: a square past the right or bottom | ||
| 877 | // edge ran into the next row, and off the end of the buffers. | ||
| 878 |
1/2✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
|
236 | w = std::min({ w, m_width - dx, m_width - sx }); |
| 879 |
1/2✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
|
236 | h = std::min({ h, m_height - dy, m_height - sy }); |
| 880 |
6/12✓ Branch 0 taken 236 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 236 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 236 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 236 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 236 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 236 times.
|
236 | if (dx < 0 || dy < 0 || sx < 0 || sy < 0 || w <= 0 || h <= 0) |
| 881 | ✗ | return; | |
| 882 | |||
| 883 | 236 | int dRowOffset = dx * m_bpp / 8; | |
| 884 | 236 | int sRowOffset = sx * m_bpp / 8; | |
| 885 | 236 | int bytesWide = w * m_bpp / 8; | |
| 886 | |||
| 887 |
2/2✓ Branch 0 taken 7524 times.
✓ Branch 1 taken 236 times.
|
7760 | for (int i = 0; i < h; i++) { |
| 888 | 7524 | memcpy(FB_CURRENT_PAGE_PTR + (m_rowStride * (dy + i)) + dRowOffset, | |
| 889 | 7524 | m_outputBuffer + (m_rowStride * (sy + i)) + sRowOffset, bytesWide); | |
| 890 | } | ||
| 891 | } | ||
| 892 | |||
| 893 | // Rows [y, y + rows) of the new image onto the page, clipped to it: the | ||
| 894 | // row-band transitions step in fixed sizes that need not divide the height. | ||
| 895 | 370 | void FrameBuffer::CopyRows(int y, int rows) { | |
| 896 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 368 times.
|
370 | if (y < 0) { |
| 897 | 2 | rows += y; | |
| 898 | 2 | y = 0; | |
| 899 | } | ||
| 900 | 370 | rows = std::min(rows, m_height - y); | |
| 901 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 368 times.
|
370 | if (rows <= 0) |
| 902 | 2 | return; | |
| 903 | 368 | memcpy(FB_CURRENT_PAGE_PTR + (m_rowStride * y), m_outputBuffer + (m_rowStride * y), m_rowStride * rows); | |
| 904 | } | ||
| 905 | |||
| 906 | 13 | void FrameBuffer::ClearAllPages() { | |
| 907 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 13 times.
|
52 | for (int x = 0; x < 3; x++) { |
| 908 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 13 times.
|
39 | if (m_pageBuffers[x]) { |
| 909 | 26 | memset(m_pageBuffers[x], 0, m_pageSize); | |
| 910 | } | ||
| 911 | } | ||
| 912 | 13 | } | |
| 913 | |||
| 914 | ✗ | void FrameBuffer::Clear() { | |
| 915 | ✗ | memset(FB_CURRENT_PAGE_PTR, 0, m_pageSize); | |
| 916 | ✗ | } | |
| 917 | |||
| 918 | 845 | uint8_t* FrameBuffer::BufferPage(int page) { | |
| 919 | 845 | return m_pageBuffers[page]; | |
| 920 | } | ||
| 921 |