Files
radiantwave/src/scroller_page.h
T
NeilClack 400c6a782b
build-dev / nix-build (push) Successful in 41s
Scroller: use full window height on rotated/tall displays
Override the LETTERBOX logical presentation while the scroller is
active so it renders into the full physical window.  On a 1920x6480
rotated display the scroller now scrolls through all 6480 pixels of
vertical space instead of being confined to the 1920x1080 letterbox
region.  Text scale is unchanged (same font size, same pixel
rasterization); more lines are simply visible at once.

- onPush: save current logical presentation, switch to DISABLED with
  real window dimensions
- onPop: restore the saved LETTERBOX presentation
- resize handler in main.cpp: skip re-apply while scroller is active
  (it owns its own presentation and restores on pop)
2026-08-25 10:55:53 -05:00

123 lines
4.1 KiB
C++

#pragma once
#include "fibonacci_line_pattern.h"
#include "page.h"
#include "scroller_streams.h"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <deque>
#include <string>
#include <unordered_map>
#include <vector>
struct AppState;
// Full-screen page showing proclamation text scrolling vertically using the
// Fibonacci line-length pattern, per-frequency color palettes, and crossfaded
// solfeggio audio. Proclamation text is never rendered as human-readable type:
// every code point is rasterized and permuted into a scrambled glyph tile by
// the Fibonacci affine map (see scramble/fibonacci_scrambler.h), and words are
// assembled from those tiles. ESC returns to the previous page (Home).
class ScrollerPage : public Page {
public:
void onPush(AppState& state) override;
void onResume(AppState& state) override;
void onPause(AppState& state) override;
void onPop(AppState& state) override;
void handleEvent(const SDL_Event& event) override;
void update(float dt) override;
void render(SDL_Renderer* renderer) override;
private:
struct WordItem {
SDL_Texture* texture = nullptr;
std::string text;
float width = 0;
float height = 0;
float x = 0; // relative to the line's left edge
float y = 0;
};
struct ScrollerLine {
std::vector<WordItem> words;
float width = 0;
uint8_t r = 255;
uint8_t g = 255;
uint8_t b = 255;
};
struct CachedWord {
SDL_Texture* texture = nullptr;
float width = 0;
float height = 0;
};
// A single glyph's scrambled tile -- [This implementation] rendering
// decision: scrambling is per code point so any glyph in the proclamation
// set is permuted exactly once per font size, then reused by every word
// that contains it.
// `surface` is the N x N (N = max(glyphW, glyphH)) RGBA8888 canvas
// produced by the Fibonacci affine scrambler. glyphW x glyphH is the
// size of the ORIGINAL, unscrambled glyph (the top-left W x H
// sub-rectangle of the tile, matching the crop-window display
// convention); word assembly blits exactly that crop. `advance` is the
// glyph's unmodified TTF advance width, so per-glyph assembly keeps word
// layout identical to plain text rendering. Every glyph surface carries
// the font's full line height, so crops align on the shared baseline.
struct CharEntry {
SDL_Surface* surface = nullptr;
float glyphW = 0;
float glyphH = 0;
float advance = 0;
};
void buildStreams(AppState& state);
void createFont(AppState& state);
void createPatterns();
int computePeakWordCount(const std::vector<std::string>& words) const;
void destroyResources();
void rebuildWordTextures();
void prefillLines();
bool addLineAt(float baseY);
void advanceStream();
void switchAudioForCurrentStream();
void updateAutoVelocity(float dt);
void ensureWordTexture(const std::string& word);
void ensureCharTexture(const std::string& glyph);
void clearCharTextures();
float measureWidth(const std::string& text) const;
std::string audioFilePath(const std::string& filename) const;
AppState* state_ = nullptr;
std::vector<radiantwave::scroller::ProclamationStream> streams_;
std::vector<radiantwave::scroller::StreamColor> fallbackPalette_;
std::vector<radiantwave::FibonacciLinePattern> patterns_;
int streamIndex_ = 0;
bool prefilling_ = false;
TTF_Font* font_ = nullptr;
int fontPxSize_ = 32;
float lineHeight_ = 0;
float spaceWidth_ = 0;
float screenW_ = 0;
float screenH_ = 0;
float maxLineWidth_ = 0;
std::deque<ScrollerLine> lines_;
std::unordered_map<std::string, CachedWord> wordTextures_;
std::unordered_map<std::string, CharEntry> charTextures_;
float scrollOffset_ = 0;
float velocity_ = -2750.0f;
float velocityStep_ = 100.0f;
float velocityDelay_ = 2.5f;
int velocityState_ = 0;
float velocityTimer_ = 0.0f;
int savedLogicalW_ = 0;
int savedLogicalH_ = 0;
SDL_RendererLogicalPresentation savedMode_ = SDL_LOGICAL_PRESENTATION_DISABLED;
};