119 lines
4.0 KiB
C++
119 lines
4.0 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();
|
|
void 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;
|
|
};
|