| 205 | } |
| 206 | |
| 207 | void XtcReaderActivity::renderPage() { |
| 208 | const uint16_t pageWidth = xtc->getPageWidth(); |
| 209 | const uint16_t pageHeight = xtc->getPageHeight(); |
| 210 | const uint8_t bitDepth = xtc->getBitDepth(); |
| 211 | |
| 212 | // Calculate buffer size for one page |
| 213 | // XTG (1-bit): Row-major, ((width+7)/8) * height bytes |
| 214 | // XTH (2-bit): Two bit planes, column-major, ((width * height + 7) / 8) * 2 bytes |
| 215 | size_t pageBufferSize; |
| 216 | if (bitDepth == 2) { |
| 217 | pageBufferSize = ((static_cast<size_t>(pageWidth) * pageHeight + 7) / 8) * 2; |
| 218 | } else { |
| 219 | pageBufferSize = ((pageWidth + 7) / 8) * pageHeight; |
| 220 | } |
| 221 | |
| 222 | // Allocate page buffer |
| 223 | uint8_t* pageBuffer = static_cast<uint8_t*>(malloc(pageBufferSize)); |
| 224 | if (!pageBuffer) { |
| 225 | LOG_ERR("XTR", "Failed to allocate page buffer (%lu bytes)", pageBufferSize); |
| 226 | renderer.clearScreen(); |
| 227 | renderer.drawCenteredText(UI_12_FONT_ID, 300, tr(STR_MEMORY_ERROR), true, EpdFontFamily::BOLD); |
| 228 | renderer.displayBuffer(); |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | // Load page data |
| 233 | size_t bytesRead = xtc->loadPage(currentPage, pageBuffer, pageBufferSize); |
| 234 | if (bytesRead == 0) { |
| 235 | LOG_ERR("XTR", "Failed to load page %lu: bufferSize=%lu bitDepth=%u error=%s", currentPage, pageBufferSize, |
| 236 | bitDepth, xtc::errorToString(xtc->getLastError())); |
| 237 | free(pageBuffer); |
| 238 | renderer.clearScreen(); |
| 239 | renderer.drawCenteredText(UI_12_FONT_ID, 300, tr(STR_PAGE_LOAD_ERROR), true, EpdFontFamily::BOLD); |
| 240 | renderer.displayBuffer(); |
| 241 | return; |
| 242 | } |
| 243 | |
| 244 | // Clear screen first |
| 245 | renderer.clearScreen(); |
| 246 | |
| 247 | // Copy page bitmap using GfxRenderer's drawPixel |
| 248 | // XTC/XTCH pages are pre-rendered with status bar included, so render full page |
| 249 | const uint16_t maxSrcY = pageHeight; |
| 250 | |
| 251 | if (bitDepth == 2) { |
| 252 | // XTH 2-bit mode: Two bit planes, column-major order |
| 253 | // - Columns scanned right to left (x = width-1 down to 0) |
| 254 | // - 8 vertical pixels per byte (MSB = topmost pixel in group) |
| 255 | // - First plane: Bit1, Second plane: Bit2 |
| 256 | // - Pixel value = (bit1 << 1) | bit2 |
| 257 | // - Grayscale: 0=White, 1=Dark Grey, 2=Light Grey, 3=Black |
| 258 | |
| 259 | const size_t planeSize = (static_cast<size_t>(pageWidth) * pageHeight + 7) / 8; |
| 260 | const uint8_t* plane1 = pageBuffer; // Bit1 plane |
| 261 | const uint8_t* plane2 = pageBuffer + planeSize; // Bit2 plane |
| 262 | const size_t colBytes = (pageHeight + 7) / 8; // Bytes per column (100 for 800 height) |
| 263 | |
| 264 | // Lambda to get pixel value at (x, y) |
nothing calls this directly
no test coverage detected