| 351 | } |
| 352 | |
| 353 | void TxtReaderActivity::renderPage() { |
| 354 | const int lineHeight = renderer.getLineHeight(cachedFontId); |
| 355 | const int contentWidth = viewportWidth; |
| 356 | |
| 357 | // Render text lines with alignment |
| 358 | auto renderLines = [&]() { |
| 359 | int y = cachedOrientedMarginTop; |
| 360 | for (const auto& line : currentPageLines) { |
| 361 | if (!line.empty()) { |
| 362 | int x = cachedOrientedMarginLeft; |
| 363 | const bool lineIsRtl = BidiUtils::startsWithRtl(line.c_str(), BidiUtils::RTL_PARAGRAPH_PROBE_DEPTH); |
| 364 | uint8_t effectiveAlignment = cachedParagraphAlignment; |
| 365 | if (lineIsRtl && (effectiveAlignment == CrossPointSettings::LEFT_ALIGN || |
| 366 | effectiveAlignment == CrossPointSettings::JUSTIFIED)) { |
| 367 | effectiveAlignment = CrossPointSettings::RIGHT_ALIGN; |
| 368 | } |
| 369 | const int textWidth = renderer.getTextAdvanceX(cachedFontId, line.c_str(), EpdFontFamily::REGULAR); |
| 370 | |
| 371 | // Apply text alignment |
| 372 | switch (effectiveAlignment) { |
| 373 | case CrossPointSettings::LEFT_ALIGN: |
| 374 | default: |
| 375 | // x already set to left margin |
| 376 | break; |
| 377 | case CrossPointSettings::CENTER_ALIGN: { |
| 378 | x = cachedOrientedMarginLeft + (contentWidth - textWidth) / 2; |
| 379 | break; |
| 380 | } |
| 381 | case CrossPointSettings::RIGHT_ALIGN: { |
| 382 | x = cachedOrientedMarginLeft + contentWidth - textWidth; |
| 383 | break; |
| 384 | } |
| 385 | case CrossPointSettings::JUSTIFIED: |
| 386 | // For plain text, justified is treated as left-aligned |
| 387 | // (true justification would require word spacing adjustments) |
| 388 | break; |
| 389 | } |
| 390 | |
| 391 | renderer.drawText(cachedFontId, x, y, line.c_str()); |
| 392 | } |
| 393 | y += lineHeight; |
| 394 | } |
| 395 | }; |
| 396 | |
| 397 | // Font prewarm: scan pass accumulates text, then prewarm, then real render |
| 398 | auto* fcm = renderer.getFontCacheManager(); |
| 399 | auto scope = fcm->createPrewarmScope(); |
| 400 | renderLines(); // scan pass — text accumulated, no drawing |
| 401 | scope.endScanAndPrewarm(); |
| 402 | |
| 403 | // BW rendering |
| 404 | renderLines(); |
| 405 | renderStatusBar(); |
| 406 | |
| 407 | ReaderUtils::displayWithRefreshCycle(renderer, pagesUntilFullRefresh); |
| 408 | |
| 409 | if (SETTINGS.textAntiAliasing) { |
| 410 | ReaderUtils::renderAntiAliased(renderer, [&renderLines]() { renderLines(); }); |
nothing calls this directly
no test coverage detected