ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
zoom.c File Reference

Viewport state machine for the tap-to-zoom image viewer (#478). More...

#include "zoom.h"
#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_check.h"
#include "ra8_err.h"
#include "ra8_ui.h"
#include "zoom_internal.h"
Include dependency graph for zoom.c:

Go to the source code of this file.

Enumerations

enum  zoom_step_t : uint8_t {
  k_zoom_overlap_div = 8U ,
  k_zoom_scale_dbl = 2U
}
 Pan-step geometry and the ladder multiplier (no magic numbers). More...

Functions

void priv_zoom_axis (int32_t anchor, int32_t extent, int32_t scale, int32_t src_dim, zoom_axis_t *out)
 Resolve one viewport axis against the image at the current magnification.
static bool internal_dim_ok (uint32_t dim)
 Whether a source dimension is inside the engine's addressable range.
static int32_t internal_sat_add (int32_t a, int32_t b)
 Saturating signed addition (never wraps at the int32 boundary).
static int32_t internal_clamp_anchor (int32_t anchor, int32_t plane, int32_t extent)
 Clamp one anchor against the magnified plane and the viewport extent.
static void internal_reclamp (zoom_view_t *v)
 Re-clamp both anchors against the current source, scale and viewport.
static void internal_touch (zoom_view_t *v, uint32_t now_ms)
 Record an interactive viewport change and schedule the flush it owes.
static ra8_err_t internal_cfg_ptrs_ok (const zoom_view_cfg_t *cfg)
 Validate every pointer a configuration must carry.
static ra8_err_t internal_cfg_extent_ok (const zoom_view_cfg_t *cfg)
 Validate the viewport extent and the source extent.
static ra8_err_t internal_cfg_scratch_ok (const zoom_view_cfg_t *cfg, uint16_t *out_strip_rows)
 Derive the strip height from the scratch budget and check every capacity.
static ra8_err_t internal_cfg_ladder_ok (const zoom_view_cfg_t *cfg, uint8_t *out_scale, uint8_t *out_ceiling)
 Resolve and validate the magnification ladder a configuration asks for.
ra8_err_t zoom_source_init (zoom_source_t *out, zoom_read_fn read, void *ctx, uint32_t width, uint32_t height)
 Bind a gray8 sub-rectangle reader and its extent as a magnifiable source.
static ra8_err_t internal_cfg_ok (const zoom_view_cfg_t *cfg, uint16_t *out_strip_rows, uint8_t *out_scale, uint8_t *out_ceiling)
 Run every configuration check, in the order they depend on each other.
static void internal_view_adopt (zoom_view_t *v, const zoom_view_cfg_t *cfg, uint8_t opening, uint8_t ceiling, uint16_t strip_rows)
 Publish a validated configuration into a view and open it.
ra8_err_t zoom_view_open (zoom_view_t *v, const zoom_view_cfg_t *cfg)
 Open a viewport onto a source, centred on a source-pixel focus.
ra8_err_t zoom_view_rebind (zoom_view_t *v, const zoom_source_t *src, uint32_t now_ms)
 Point an open view at a different source, keeping zoom and position.
ra8_err_t zoom_view_invalidate (zoom_view_t *v, uint32_t now_ms)
 Declare the viewport's pixels stale for a reason the engine cannot see.
ra8_err_t zoom_view_close (zoom_view_t *v)
 Close a view; it stops owing the panel anything.
bool zoom_view_active (const zoom_view_t *v)
 Whether a view is open.
uint8_t zoom_scale_cycle (uint8_t scale, uint8_t min, uint8_t max)
 Next magnification on the doubling ladder, wrapping back to the minimum.
ra8_err_t zoom_view_set_scale (zoom_view_t *v, uint8_t scale, int32_t focus_x, int32_t focus_y, uint32_t now_ms)
 Set the magnification, keeping a panel point fixed under the finger.
ra8_err_t zoom_view_pan (zoom_view_t *v, int32_t dx, int32_t dy, uint32_t now_ms)
 Pan the viewport by a destination-pixel delta.
ra8_err_t zoom_view_pan_dir (zoom_view_t *v, zoom_pan_t dir, uint32_t now_ms)
 Pan by a discrete step in one direction (the tap-band interaction).
ra8_err_t zoom_view_window (const zoom_view_t *v, ra8_ui_rect_t *out)
 The source rectangle currently visible, in source pixels.
bool zoom_view_tick (zoom_view_t *v, uint32_t now_ms)
 Advance the settle timer; promote a bi-level view to full quality.
ra8_err_t zoom_view_present (zoom_view_t *v, zoom_present_t *out)
 Take the pending flush plan, clearing it.

Variables

static const char *const s_tag = "zoom"
 Component tag for RA8_CHECK_* log lines.

Detailed Description

Viewport state machine for the tap-to-zoom image viewer (#478).

Implements the non-rendering half of zoom.h: opening a viewport onto a source, the integer magnification ladder, anchor clamping in the magnified plane, discrete pan steps, and the dirty/waveform bookkeeping the reader turns into a display flush. Pure integer arithmetic with no MMIO and no allocation, so it behaves identically on the unit-test host, in ra8_emulator and on silicon. The strip composite lives in zoom_render.c.

[Ring 4 / Domain] {World: NS}

Since
0.1.0

Definition in file zoom.c.

Enumeration Type Documentation

◆ zoom_step_t

enum zoom_step_t : uint8_t

Pan-step geometry and the ladder multiplier (no magic numbers).

One discrete pan step moves the viewport by its own extent less extent / k_zoom_overlap_div, so a strip of the previous view stays on screen and the reader never loses their place. Expressed as a divisor rather than a pixel count so it scales with the viewport: a 320 px loupe keeps 40 px, a 1024 px full-screen view keeps 128 px.

Invariant
k_zoom_overlap_div > 1, so a step always advances.
Example:
const int32_t step = v->dst.w - (v->dst.w / (int32_t)k_zoom_overlap_div);
@ k_zoom_overlap_div
Viewport fraction retained across a pan step.
Definition zoom.c:55
See also
zoom_view_pan_dir
Since
0.1.0
Enumerator
k_zoom_overlap_div 

Viewport fraction retained across a pan step.

k_zoom_scale_dbl 

Ladder multiplier (doubling, not increment).

Definition at line 54 of file zoom.c.

Function Documentation

◆ internal_cfg_extent_ok()

ra8_err_t internal_cfg_extent_ok ( const zoom_view_cfg_t * cfg)
static

Validate the viewport extent and the source extent.

Runs after the pointer check and before the scratch sizing, which divides by the viewport width: an empty viewport has to be rejected first. The source bound is what makes the magnified-plane arithmetic overflow-free downstream.

Parameters
[in]cfgCandidate configuration (already known non-NULL).
Returns
ra8_err_t Error code.
Return values
k_ra8_okBoth extents are usable.
k_ra8_err_invalid_argAn extent is empty, or the source is too large.
Precondition
cfg is non-NULL.
internal_cfg_ptrs_ok has already passed for cfg.
Postcondition
No state is modified.
On k_ra8_ok the magnified plane cannot overflow int32.
Note
Not thread-safe (logs).
Since
0.1.0

Definition at line 277 of file zoom.c.

References zoom_view_cfg_t::dst, ra8_ui_rect_t::h, zoom_source_t::height, internal_dim_ok(), k_ra8_err_invalid_arg, k_ra8_ok, ra8_log_error, s_tag, zoom_view_cfg_t::src, ra8_ui_rect_t::w, and zoom_source_t::width.

Referenced by internal_cfg_ok().

◆ internal_cfg_ladder_ok()

ra8_err_t internal_cfg_ladder_ok ( const zoom_view_cfg_t * cfg,
uint8_t * out_scale,
uint8_t * out_ceiling )
static

Resolve and validate the magnification ladder a configuration asks for.

Resolves the two "0 means default" fields before validating them, so an all-zero configuration opens at 1:1 with the engine ceiling instead of being rejected, while an out-of-range explicit value still fails closed.

Parameters
[in]cfgCandidate configuration (already known non-NULL).
[out]out_scaleReceives the opening magnification.
[out]out_ceilingReceives the effective ladder ceiling.
Returns
ra8_err_t Error code.
Return values
k_ra8_okBoth values are on the engine ladder.
k_ra8_err_invalid_argThe ceiling or the opening scale is off it.
Precondition
cfg is non-NULL.
out_scale and out_ceiling address writable storage.
Postcondition
On k_ra8_ok k_zoom_scale_min <= *out_scale <= *out_ceiling.
On k_ra8_ok *out_ceiling <= k_zoom_scale_max.
Note
Not thread-safe (logs).
Since
0.1.0

Definition at line 352 of file zoom.c.

References k_ra8_err_invalid_arg, k_ra8_ok, k_zoom_scale_max, k_zoom_scale_min, ra8_log_error, s_tag, zoom_view_cfg_t::scale, and zoom_view_cfg_t::scale_max.

Referenced by internal_cfg_ok().

◆ internal_cfg_ok()

ra8_err_t internal_cfg_ok ( const zoom_view_cfg_t * cfg,
uint16_t * out_strip_rows,
uint8_t * out_scale,
uint8_t * out_ceiling )
static

Run every configuration check, in the order they depend on each other.

The order is not stylistic. Pointers first, because the extent check reads through them; extents second, because the scratch sizing divides by the viewport width; the ladder last, because it is the only check with no dependency on the others. Split out of zoom_view_open so that function is "check, then adopt".

Parameters
[in]cfgCandidate configuration (already known non-NULL).
[out]out_strip_rowsReceives the resolved composite strip height.
[out]out_scaleReceives the resolved opening magnification.
[out]out_ceilingReceives the resolved ladder ceiling.
Returns
ra8_err_t Error code.
Return values
k_ra8_okEvery check passed; all three outputs are resolved.
k_ra8_err_*The first failing check's code, verbatim.
Precondition
cfg is non-NULL.
All three out-parameters address writable storage.
Postcondition
No state outside the out-parameters is modified.
On any error the out-parameters hold no meaningful value.
Note
Not thread-safe (logs).
See also
zoom_view_open
Since
0.1.0

Definition at line 418 of file zoom.c.

References internal_cfg_extent_ok(), internal_cfg_ladder_ok(), internal_cfg_ptrs_ok(), internal_cfg_scratch_ok(), RA8_RETURN_ON_ERROR, and s_tag.

Referenced by zoom_view_open().

◆ internal_cfg_ptrs_ok()

ra8_err_t internal_cfg_ptrs_ok ( const zoom_view_cfg_t * cfg)
static

Validate every pointer a configuration must carry.

Split out of zoom_view_open so each validation stage stays inside the project's function-size bar, and so a missing pointer is rejected before any sizing arithmetic reads through it.

Parameters
[in]cfgCandidate configuration (already known non-NULL).
Returns
ra8_err_t Error code.
Return values
k_ra8_okEvery required pointer is present.
k_ra8_err_null_ptrOne is missing; the log line names it.
Precondition
cfg is non-NULL.
The caller has not yet published any of cfg into a view.
Postcondition
No state is modified.
On k_ra8_ok the source seam and all three scratch buffers are non-NULL.
Note
Not thread-safe (logs).
Since
0.1.0

Definition at line 250 of file zoom.c.

References k_ra8_ok, zoom_scratch_t::packed, RA8_CHECK_NULL_PTR, zoom_source_t::read, zoom_scratch_t::row, s_tag, zoom_view_cfg_t::scratch, zoom_view_cfg_t::src, and zoom_scratch_t::strip.

Referenced by internal_cfg_ok().

◆ internal_cfg_scratch_ok()

ra8_err_t internal_cfg_scratch_ok ( const zoom_view_cfg_t * cfg,
uint16_t * out_strip_rows )
static

Derive the strip height from the scratch budget and check every capacity.

Derives the strip height from the strip budget rather than taking it as a parameter, so the caller tunes the composite by sizing a buffer and cannot ask for a strip its packed buffer could not hold. The height is capped by the module maximum and by the viewport itself.

Parameters
[in]cfgCandidate configuration (already known non-NULL).
[out]out_strip_rowsDestination rows one composite strip can hold.
Returns
ra8_err_t Error code.
Return values
k_ra8_okout_strip_rows holds a workable strip height.
k_ra8_err_no_memA buffer is too small for this viewport width.
Precondition
cfg passed internal_cfg_extent_ok, so dst.w > 0.
out_strip_rows addresses writable storage.
Postcondition
On k_ra8_ok *out_strip_rows is in [1, k_zoom_strip_rows_max].
On k_ra8_ok the packed buffer can hold one strip of that height.
Note
Not thread-safe (logs).
Since
0.1.0

Definition at line 309 of file zoom.c.

References zoom_view_cfg_t::dst, ra8_ui_rect_t::h, k_ra8_err_no_mem, k_ra8_ok, k_zoom_strip_rows_max, zoom_scratch_t::packed_cap, ra8_log_error, zoom_scratch_t::row_cap, s_tag, zoom_view_cfg_t::scratch, zoom_scratch_t::strip_cap, and ra8_ui_rect_t::w.

Referenced by internal_cfg_ok().

◆ internal_clamp_anchor()

int32_t internal_clamp_anchor ( int32_t anchor,
int32_t plane,
int32_t extent )
static

Clamp one anchor against the magnified plane and the viewport extent.

Two regimes, decided by whether the magnified image is bigger than the viewport. Bigger: the anchor is held inside [0, plane - extent] so the viewport never shows past an edge. Smaller: the image is centred and the anchor goes negative, which the render reads as a letterbox rather than as an error.

Parameters
[in]anchorProposed magnified-plane coordinate at viewport offset 0.
[in]planeMagnified plane extent on this axis (src_dim * scale).
[in]extentViewport extent on this axis, destination pixels.
Returns
The clamped anchor.
Return values
negativeThe image is smaller than the viewport: the centring offset.
[0, plane - extent]Otherwise, the in-range anchor.
Precondition
extent is positive (validated at open).
plane is src_dim * scale for the same axis.
Postcondition
The result satisfies the anchor invariant of zoom_view_t.
No state is mutated.
Note
Pure; thread-safe.
Since
0.1.0

Definition at line 167 of file zoom.c.

Referenced by internal_reclamp().

◆ internal_dim_ok()

bool internal_dim_ok ( uint32_t dim)
static

Whether a source dimension is inside the engine's addressable range.

The single place the engine's addressable range is decided. Both bounds matter: zero has no pixels to magnify, and a dimension past the cap could overflow the magnified plane at maximum zoom.

Parameters
[in]dimCandidate width or height in pixels.
Returns
True when dim is in [1, k_zoom_dim_max].
Return values
trueThe dimension is usable.
falseZero, or large enough that the magnified plane could overflow.
Precondition
None – every uint32 value has a defined answer.
dim is a pixel count, not a byte count.
Postcondition
No state is modified (pure).
A true result guarantees dim * k_zoom_scale_max fits in int32.
Note
Pure; thread-safe.
Since
0.1.0

Definition at line 111 of file zoom.c.

References k_zoom_dim_max.

Referenced by internal_cfg_extent_ok(), zoom_source_init(), and zoom_view_rebind().

◆ internal_reclamp()

void internal_reclamp ( zoom_view_t * v)
static

Re-clamp both anchors against the current source, scale and viewport.

Called after anything that can invalidate an anchor – a scale change, a pan, a rebind onto a differently-sized page – so the clamp invariant is re-established in exactly one place instead of at every mutation site.

Parameters
[in,out]vOpen view whose anchors may be out of range.
Returns
Nothing.
Precondition
v is non-NULL and open.
v->scale is already the scale the anchors should be clamped for.
Postcondition
Both anchors satisfy the clamp invariant of zoom_view_t.
Nothing but the anchors is modified.
Note
Not thread-safe.
Since
0.1.0

Definition at line 199 of file zoom.c.

References zoom_view_t::anchor_x, zoom_view_t::anchor_y, zoom_view_t::dst, ra8_ui_rect_t::h, zoom_source_t::height, internal_clamp_anchor(), zoom_view_t::scale, zoom_view_t::src, ra8_ui_rect_t::w, and zoom_source_t::width.

Referenced by internal_view_adopt(), zoom_view_pan(), zoom_view_rebind(), and zoom_view_set_scale().

◆ internal_sat_add()

int32_t internal_sat_add ( int32_t a,
int32_t b )
static

Saturating signed addition (never wraps at the int32 boundary).

A pan delta is caller-supplied and may be arbitrary, so the anchor addition is done in 64 bits and clamped rather than allowed to wrap – a wrapped anchor would clamp to the wrong edge.

Parameters
[in]aFirst addend.
[in]bSecond addend.
Returns
The sum, clamped to the int32 range.
Return values
INT32_MAXThe true sum overflowed positive.
INT32_MINThe true sum overflowed negative.
Precondition
a and b are valid signed integers.
The result is only ever consumed by the anchor clamp.
Postcondition
No state is mutated.
The result never wraps around the int32 boundary.
Note
Pure; thread-safe.
Since
0.1.0

Definition at line 134 of file zoom.c.

Referenced by zoom_view_pan().

◆ internal_touch()

void internal_touch ( zoom_view_t * v,
uint32_t now_ms )
static

Record an interactive viewport change and schedule the flush it owes.

The one place the dirty flag and the waveform class are set, so the policy is applied uniformly: under the responsive policy an interactive change owes a fast (bi-level) flush and arms the settle timer; under the quality policy it owes a full 16-level flush and arms nothing.

Parameters
[in,out]vOpen view.
[in]now_msCurrent millisecond timestamp.
Returns
Nothing.
Precondition
v is non-NULL and open.
now_ms comes from a monotonic millisecond source.
Postcondition
v->pending is true and v->pending_kind matches v->policy.
v->settle_armed is true only under k_zoom_policy_responsive.
Note
Not thread-safe.
Since
0.1.0

Definition at line 224 of file zoom.c.

References k_zoom_policy_responsive, k_zoom_refresh_fast, k_zoom_refresh_quality, zoom_view_t::last_input_ms, zoom_view_t::pending, zoom_view_t::pending_kind, zoom_view_t::policy, and zoom_view_t::settle_armed.

Referenced by zoom_view_invalidate(), zoom_view_pan(), zoom_view_rebind(), and zoom_view_set_scale().

◆ internal_view_adopt()

void internal_view_adopt ( zoom_view_t * v,
const zoom_view_cfg_t * cfg,
uint8_t opening,
uint8_t ceiling,
uint16_t strip_rows )
static

Publish a validated configuration into a view and open it.

The last step of zoom_view_open, split out so the open path reads as "validate, validate, validate, adopt" rather than as one long function. Nothing here can fail: every value has already been checked, so this is assignment plus the initial clamp. The anchor is derived from the focus point in SOURCE pixels – scaled into the magnified plane, then backed off by half a viewport so the focus lands in the middle rather than the corner.

Parameters
[out]vView to populate (its previous contents are discarded).
[in]cfgThe validated configuration.
[in]openingResolved opening magnification.
[in]ceilingResolved ladder ceiling.
[in]strip_rowsResolved composite strip height.
Returns
Nothing.
Precondition
cfg passed every ::zoom_cfg_* check.
opening, ceiling and strip_rows are their resolved values.
Postcondition
v->active is true and a quality flush is owed.
Both anchors satisfy the clamp invariant of zoom_view_t.
Note
Not thread-safe.
See also
zoom_view_open
Since
0.1.0

Definition at line 458 of file zoom.c.

References zoom_view_t::active, zoom_view_t::anchor_x, zoom_view_t::anchor_y, zoom_view_cfg_t::dst, zoom_view_t::dst, zoom_view_cfg_t::focus_x, zoom_view_cfg_t::focus_y, ra8_ui_rect_t::h, internal_reclamp(), k_zoom_refresh_quality, k_zoom_settle_ms_default, zoom_view_t::last_input_ms, zoom_view_t::pending, zoom_view_t::pending_kind, zoom_view_cfg_t::policy, zoom_view_t::policy, zoom_view_t::scale, zoom_view_t::scale_max, zoom_view_cfg_t::scratch, zoom_view_t::scratch, zoom_view_t::settle_armed, zoom_view_cfg_t::settle_ms, zoom_view_t::settle_ms, zoom_view_cfg_t::src, zoom_view_t::src, zoom_view_t::strip_rows, and ra8_ui_rect_t::w.

Referenced by zoom_view_open().

◆ priv_zoom_axis()

void priv_zoom_axis ( int32_t anchor,
int32_t extent,
int32_t scale,
int32_t src_dim,
zoom_axis_t * out )

Resolve one viewport axis against the image at the current magnification.

The single definition of the viewer's geometry. Destination offset d maps to magnified-plane coordinate anchor + d and therefore to source index (anchor + d) / scale; the covered span is where that plane coordinate lies inside [0, src_dim * scale). Both the render (which columns to fill, which source row segment to read) and zoom_view_window (which source pixels are live) are views onto this one result. The division is only ever performed on a non-negative plane coordinate, so it never depends on the implementation of signed truncation.

Parameters
[in]anchorMagnified-plane coordinate shown at viewport offset 0.
[in]extentViewport extent on this axis, destination pixels (> 0).
[in]scaleInteger magnification (>= 1).
[in]src_dimSource extent on this axis, pixels (>= 1).
[out]outReceives the resolved axis.
Returns
Nothing; out is always fully populated.
Precondition
out is non-NULL (callers are inside this module and hold storage).
scale >= 1 and src_dim * scale fits in int32 (guaranteed by k_zoom_dim_max and k_zoom_scale_max).
Postcondition
Every invariant of zoom_axis_t holds.
No input is modified.
Note
Pure; thread-safe.
See also
zoom_axis_t
Since
0.1.0

Definition at line 60 of file zoom.c.

References zoom_axis_t::count, zoom_axis_t::d0, zoom_axis_t::d1, and zoom_axis_t::s0.

Referenced by zoom_view_render(), and zoom_view_window().

◆ zoom_scale_cycle()

uint8_t zoom_scale_cycle ( uint8_t scale,
uint8_t min,
uint8_t max )
nodiscard

Next magnification on the doubling ladder, wrapping back to the minimum.

The tap-to-zoom cycle every reader in this tree uses: min -> 2*min -> 4*min -> ... -> max -> min. Doubling rather than incrementing because a 1x -> 2x step is a visible change and a 7x -> 8x step is not. The last rung is max exactly even when max is not a power of two, so a ceiling of 3 gives 1 -> 2 -> 3 -> 1.

Parameters
[in]scaleCurrent magnification.
[in]minLadder floor (0 is read as k_zoom_scale_min).
[in]maxLadder ceiling (below min is read as min).
Returns
The next magnification on the ladder.
Return values
minscale was already at or above max.
2 * scale or maxOtherwise, whichever is smaller.
Precondition
None – every input combination has a defined answer.
min and max are magnifications, not plane coordinates.
Postcondition
The result is in [min, max].
No state is modified (pure function).
Note
Thread-safe: pure integer arithmetic on its arguments.
See also
zoom_view_set_scale
Since
0.1.0

Definition at line 539 of file zoom.c.

References k_zoom_scale_dbl, k_zoom_scale_min, and min.

Referenced by ez_scene_tap().

◆ zoom_source_init()

ra8_err_t zoom_source_init ( zoom_source_t * out,
zoom_read_fn read,
void * ctx,
uint32_t width,
uint32_t height )
nodiscard

Bind a gray8 sub-rectangle reader and its extent as a magnifiable source.

Validates the reader and the extent once, here, so nothing downstream has to re-check them: the engine's plane arithmetic is overflow-free precisely because width and height passed k_zoom_dim_max at this seam.

Parameters
[out]outSource descriptor to populate.
[in]readgray8 sub-rectangle reader (non-NULL).
[in]ctxOpaque context handed back to read (may be NULL).
[in]widthFull-resolution source width, [1, k_zoom_dim_max].
[in]heightFull-resolution source height, [1, k_zoom_dim_max].
Returns
ra8_err_t Error code.
Return values
k_ra8_okout describes the source.
k_ra8_err_null_ptrout or read is NULL.
k_ra8_err_invalid_argwidth or height is 0 or exceeds the cap.
Precondition
out addresses writable storage for one zoom_source_t.
read remains valid for the lifetime of every view bound to this source.
Postcondition
On k_ra8_ok every field of out is set and out->read != NULL.
On any error out is left untouched.
Note
Not thread-safe.
See also
zoom_view_open
Since
0.1.0

Definition at line 375 of file zoom.c.

References zoom_source_t::ctx, zoom_source_t::height, internal_dim_ok(), k_ra8_err_invalid_arg, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_log_error, zoom_source_t::read, s_tag, and zoom_source_t::width.

◆ zoom_view_active()

bool zoom_view_active ( const zoom_view_t * v)
nodiscard

Whether a view is open.

Parameters
[in]vView to query (NULL reads as closed).
Returns
Openness of the view.
Return values
truev is non-NULL and open.
falsev is NULL or closed.
Precondition
None – NULL is an accepted input and answers false.
v, if non-NULL, addresses a zoom_view_t.
Postcondition
No state is modified (pure query).
The result is exactly v != NULL && v->active.
Note
Thread-safe with respect to this module: reads one bool.
See also
zoom_view_open
Since
0.1.0

Definition at line 534 of file zoom.c.

References zoom_view_t::active.

◆ zoom_view_close()

ra8_err_t zoom_view_close ( zoom_view_t * v)
nodiscard

Close a view; it stops owing the panel anything.

Parameters
[in,out]vView to close (may already be closed).
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe view is closed.
k_ra8_err_null_ptrv is NULL.
Precondition
v addresses writable storage for one zoom_view_t.
The caller has already flushed anything it intends the panel to keep.
Postcondition
v->active is false and v->pending is false.
The caller's scratch buffers and source are untouched and may be reused.
Note
Not thread-safe. Closing twice is not an error.
See also
zoom_view_open
Since
0.1.0

Definition at line 525 of file zoom.c.

References zoom_view_t::active, k_ra8_ok, zoom_view_t::pending, RA8_CHECK_NULL_PTR, s_tag, and zoom_view_t::settle_armed.

◆ zoom_view_invalidate()

ra8_err_t zoom_view_invalidate ( zoom_view_t * v,
uint32_t now_ms )
nodiscard

Declare the viewport's pixels stale for a reason the engine cannot see.

The engine marks itself dirty when it changes the view, and deliberately does not when nothing moved – a pan clamped against an edge owes the panel nothing, which is what stops an e-ink refresh per tap. That is exactly right for the engine's own state and exactly wrong for everything a compositor does around it: drawing a loupe over the viewport, lifting one off it, or repainting chrome that overlaps it all invalidate pixels the engine has no way to know about.

Without this the reader hits a silent class of bug that a continuously-scanned LCD hides completely: the framebuffer is correct, no flush is ever requested, and on an e-ink panel the change simply never appears. Call it whenever something outside the engine has damaged the viewport.

Parameters
[in,out]vOpen view.
[in]now_msCurrent millisecond timestamp for the settle timer.
Returns
ra8_err_t Error code.
Return values
k_ra8_okA flush of the whole viewport is now owed.
k_ra8_err_null_ptrv is NULL.
k_ra8_err_invalid_statev is not open.
Precondition
v was opened by zoom_view_open.
The caller genuinely damaged (or is about to repaint) the viewport.
Postcondition
v->pending is true and v->pending_kind follows v->policy.
The anchor and the scale are untouched – only the flush bookkeeping moves.
Note
Not thread-safe.
Example:
s->lens_on = !s->lens_on; // chrome over the page
(void)zoom_view_invalidate(&s->page, ra8_time_ms()); // ...so repaint it
uint32_t ra8_time_ms(void)
Get the current 1 kHz tick count.
Definition ra8_time.c:108
ra8_err_t zoom_view_invalidate(zoom_view_t *v, uint32_t now_ms)
Declare the viewport's pixels stale for a reason the engine cannot see.
Definition zoom.c:514
See also
zoom_view_present
Since
0.1.0

Definition at line 514 of file zoom.c.

References zoom_view_t::active, internal_touch(), k_ra8_err_invalid_state, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_log_error, and s_tag.

Referenced by ez_scene_tap(), and ez_selftest_lens().

◆ zoom_view_open()

ra8_err_t zoom_view_open ( zoom_view_t * v,
const zoom_view_cfg_t * cfg )
nodiscard

Open a viewport onto a source, centred on a source-pixel focus.

Derives everything the engine needs and never re-derives it: the strip height from the scratch capacity, the effective ladder ceiling, the settle window, and the initial anchor from cfg->focus_x / focus_y clamped to the image. The view opens owing a k_zoom_refresh_quality flush regardless of policy – the first frame a reader sees must be the good one.

Parameters
[out]vView to open (overwritten wholesale).
[in]cfgConfiguration; see zoom_view_cfg_t.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe view is open and owes a quality flush.
k_ra8_err_null_ptrv, cfg, cfg->src.read, or a scratch pointer is NULL.
k_ra8_err_invalid_argEmpty viewport, out-of-range scale, or a bad source extent.
k_ra8_err_no_memA scratch buffer is too small for this viewport width.
Precondition
cfg->src was populated by zoom_source_init or an adapter binder.
Every scratch buffer in cfg outlives the view.
Postcondition
On k_ra8_ok v->active is true, v->pending is true and v->pending_kind == k_zoom_refresh_quality.
On any error v is not left half-open: v->active is false.
Note
Not thread-safe. Does not touch the framebuffer – call zoom_view_render for that.
Example:
if (zoom_view_open(&s_view, &cfg) == k_ra8_ok) { (void)zoom_view_render(&s_view); }
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_t zoom_view_render(zoom_view_t *v)
Composite the visible window into the bound ra8_gfx framebuffer.
ra8_err_t zoom_view_open(zoom_view_t *v, const zoom_view_cfg_t *cfg)
Open a viewport onto a source, centred on a source-pixel focus.
Definition zoom.c:482
See also
zoom_view_close
zoom_view_render
Since
0.1.0

Definition at line 482 of file zoom.c.

References zoom_view_t::active, internal_cfg_ok(), internal_view_adopt(), k_ra8_ok, RA8_CHECK_NULL_PTR, RA8_RETURN_ON_ERROR, and s_tag.

Referenced by ez_open_views().

◆ zoom_view_pan()

ra8_err_t zoom_view_pan ( zoom_view_t * v,
int32_t dx,
int32_t dy,
uint32_t now_ms )
nodiscard

Pan the viewport by a destination-pixel delta.

Moves the anchor and re-clamps it. Because the anchor lives in the magnified plane the delta is in destination pixels at every zoom, so a one-pixel pan is one pixel of panel movement whether the view is at 1:1 or 8x. A delta that would leave the image is clamped rather than rejected, and a clamped-to-no-movement pan owes no flush – panning into an edge does not cost an e-ink refresh.

Parameters
[in,out]vOpen view.
[in]dxHorizontal movement in destination pixels (+ moves content left).
[in]dyVertical movement in destination pixels (+ moves content up).
[in]now_msCurrent millisecond timestamp for the settle timer.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe anchor moved, or was already against the edge.
k_ra8_err_null_ptrv is NULL.
k_ra8_err_invalid_statev is not open.
Precondition
v was opened by zoom_view_open.
dx and dy are destination pixels, not source pixels.
Postcondition
The anchor satisfies the clamp invariant of zoom_view_t.
A flush is owed if and only if the anchor actually changed.
Note
Not thread-safe.
See also
zoom_view_pan_dir
Since
0.1.0

Definition at line 595 of file zoom.c.

References zoom_view_t::active, zoom_view_t::anchor_x, zoom_view_t::anchor_y, internal_reclamp(), internal_sat_add(), internal_touch(), k_ra8_err_invalid_state, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_log_error, and s_tag.

Referenced by zoom_view_pan_dir().

◆ zoom_view_pan_dir()

ra8_err_t zoom_view_pan_dir ( zoom_view_t * v,
zoom_pan_t dir,
uint32_t now_ms )
nodiscard

Pan by a discrete step in one direction (the tap-band interaction).

One step is zoom_view_t::dst minus a one-eighth overlap on the moving axis, so consecutive taps keep a strip of the previous view on screen and the reader never loses their place. The overlap is a fraction of the viewport rather than a constant so it scales with the lens size in loupe mode.

Parameters
[in,out]vOpen view.
[in]dirDirection; k_zoom_pan_none is a no-op.
[in]now_msCurrent millisecond timestamp for the settle timer.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe step was applied (possibly clamped to nothing).
k_ra8_err_null_ptrv is NULL.
k_ra8_err_invalid_statev is not open.
Precondition
v was opened by zoom_view_open.
dir is a zoom_pan_t enumerator.
Postcondition
The anchor satisfies the clamp invariant of zoom_view_t.
k_zoom_pan_none leaves the view byte-identical.
Note
Not thread-safe.
See also
zoom_view_pan
Since
0.1.0

Definition at line 614 of file zoom.c.

References zoom_view_t::active, zoom_view_t::dst, ra8_ui_rect_t::h, k_ra8_err_invalid_state, k_zoom_overlap_div, k_zoom_pan_down, k_zoom_pan_left, k_zoom_pan_none, k_zoom_pan_right, k_zoom_pan_up, RA8_CHECK_NULL_PTR, ra8_log_error, s_tag, ra8_ui_rect_t::w, and zoom_view_pan().

Referenced by ez_apply_pan(), and ez_selftest_pan().

◆ zoom_view_present()

ra8_err_t zoom_view_present ( zoom_view_t * v,
zoom_present_t * out )
nodiscard

Take the pending flush plan, clearing it.

The consume half of the dirty-tracking pair: it reports what the panel owes and immediately marks it paid, so a caller that ignores the plan simply drops a frame rather than flushing the same rectangle forever. Call it after zoom_view_render, not before.

Parameters
[in,out]vOpen view.
[out]outReceives the plan; out->present is false when none is owed.
Returns
ra8_err_t Error code.
Return values
k_ra8_okout holds the plan (possibly present == false).
k_ra8_err_null_ptrv or out is NULL.
k_ra8_err_invalid_statev is not open.
Precondition
v was opened by zoom_view_open.
The framebuffer already holds the pixels this plan describes.
Postcondition
v->pending is false.
A second immediate call reports present == false.
Note
Not thread-safe.
See also
zoom_view_render
Since
0.1.0

Definition at line 684 of file zoom.c.

References zoom_view_t::active, zoom_view_t::dst, k_ra8_err_invalid_state, k_ra8_ok, zoom_view_t::pending, zoom_view_t::pending_kind, zoom_present_t::present, RA8_CHECK_NULL_PTR, ra8_log_error, zoom_present_t::rect, zoom_present_t::refresh, and s_tag.

Referenced by ez_scene_present().

◆ zoom_view_rebind()

ra8_err_t zoom_view_rebind ( zoom_view_t * v,
const zoom_source_t * src,
uint32_t now_ms )
nodiscard

Point an open view at a different source, keeping zoom and position.

The page-turn path, and a deliberate product choice: turning the page while magnified keeps the magnification and the viewport anchor rather than dropping back to the whole page. A reader who zoomed in because the text is small wants the next page at the same magnification; the alternative (a turn exits zoom) is strictly less capable and is recoverable by calling zoom_view_close instead. The anchor is re-clamped to the new extent, so a shorter page simply pins to its bottom edge rather than showing past it.

Parameters
[in,out]vOpen view.
[in]srcNew source (validated as at open).
[in]now_msCurrent millisecond timestamp for the settle timer.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe view now reads src.
k_ra8_err_null_ptrv or src is NULL, or src->read is NULL.
k_ra8_err_invalid_argsrc has a zero or oversized extent.
k_ra8_err_invalid_statev is not open.
Precondition
v was opened by zoom_view_open.
src outlives the view.
Postcondition
On k_ra8_ok v->scale is unchanged and the anchor is clamped to src.
On k_ra8_ok a flush is owed (v->pending is true).
Note
Not thread-safe.
See also
zoom_view_open
Since
0.1.0

Definition at line 495 of file zoom.c.

References zoom_view_t::active, zoom_source_t::height, internal_dim_ok(), internal_reclamp(), internal_touch(), k_ra8_err_invalid_arg, k_ra8_err_invalid_state, k_ra8_ok, RA8_CHECK_NULL_PTR, ra8_log_error, zoom_source_t::read, s_tag, zoom_view_t::src, and zoom_source_t::width.

◆ zoom_view_set_scale()

ra8_err_t zoom_view_set_scale ( zoom_view_t * v,
uint8_t scale,
int32_t focus_x,
int32_t focus_y,
uint32_t now_ms )
nodiscard

Set the magnification, keeping a panel point fixed under the finger.

Tap-to-zoom, literally: the plane point currently displayed at (focus_x, focus_y) – framebuffer coordinates, i.e. where the user tapped – is the point that stays put. The plane coordinate is rescaled by new_scale / old_scale and the anchor is recomputed so that the same image feature lands under the same pixel, then clamped. A focus outside the viewport still works and simply pulls that off-screen feature toward the tapped edge.

Setting the scale it already has is a no-op and owes no flush, so a reader can call this unconditionally from a tap handler.

Parameters
[in,out]vOpen view.
[in]scaleRequested magnification, [k_zoom_scale_min, v->scale_max].
[in]focus_xFramebuffer column to keep fixed.
[in]focus_yFramebuffer row to keep fixed.
[in]now_msCurrent millisecond timestamp for the settle timer.
Returns
ra8_err_t Error code.
Return values
k_ra8_okThe scale is now scale (or already was).
k_ra8_err_null_ptrv is NULL.
k_ra8_err_invalid_statev is not open.
k_ra8_err_out_of_rangescale is outside the ladder.
Precondition
v was opened by zoom_view_open.
scale is on the integer ladder (fractional zoom is not representable).
Postcondition
On k_ra8_ok v->scale == scale and the anchor is clamped to the new plane.
On k_ra8_ok with a changed scale, a flush is owed.
Note
Not thread-safe.
Example:
const uint8_t next = zoom_scale_cycle(view.scale, k_zoom_scale_min, view.scale_max);
(void)zoom_view_set_scale(&view, next, tap_x, tap_y, ra8_time_ms());
@ k_zoom_scale_min
1:1 – one source pixel per panel pixel.
Definition zoom.h:118
uint8_t zoom_scale_cycle(uint8_t scale, uint8_t min, uint8_t max)
Next magnification on the doubling ladder, wrapping back to the minimum.
Definition zoom.c:539
ra8_err_t zoom_view_set_scale(zoom_view_t *v, uint8_t scale, int32_t focus_x, int32_t focus_y, uint32_t now_ms)
Set the magnification, keeping a panel point fixed under the finger.
Definition zoom.c:556
See also
zoom_scale_cycle
Since
0.1.0

Definition at line 556 of file zoom.c.

References zoom_view_t::active, zoom_view_t::anchor_x, zoom_view_t::anchor_y, zoom_view_t::dst, internal_reclamp(), internal_touch(), k_ra8_err_invalid_state, k_ra8_err_out_of_range, k_ra8_ok, k_zoom_scale_min, RA8_CHECK_NULL_PTR, ra8_log_error, s_tag, zoom_view_t::scale, zoom_view_t::scale_max, ra8_ui_rect_t::x, and ra8_ui_rect_t::y.

Referenced by ez_scene_tap(), and ez_selftest_zoom().

◆ zoom_view_tick()

bool zoom_view_tick ( zoom_view_t * v,
uint32_t now_ms )

Advance the settle timer; promote a bi-level view to full quality.

Call once per main-loop iteration. Under k_zoom_policy_responsive an interactive change leaves the view armed; once zoom_view_t::settle_ms of input silence have passed this arms a k_zoom_refresh_quality repaint of the same rectangle and disarms itself, so the reader repaints once and not per tick. The elapsed-time comparison is unsigned, so a millisecond counter that wraps produces one early settle rather than a hang.

Parameters
[in,out]vView (NULL is accepted and answers false).
[in]now_msCurrent millisecond timestamp.
Returns
Whether a quality repaint just became due.
Return values
trueThe caller should re-render and present.
falseNothing to do this tick.
Precondition
None – NULL, closed and disarmed views all answer false.
now_ms comes from a monotonic millisecond source.
Postcondition
On true, v->pending is true and v->settle_armed is false.
On false, v is unmodified.
Note
Not thread-safe.
See also
zoom_view_present
Since
0.1.0

Definition at line 664 of file zoom.c.

References zoom_view_t::active, k_zoom_refresh_quality, zoom_view_t::last_input_ms, zoom_view_t::pending, zoom_view_t::pending_kind, zoom_view_t::settle_armed, and zoom_view_t::settle_ms.

Referenced by ez_scene_tick().

◆ zoom_view_window()

ra8_err_t zoom_view_window ( const zoom_view_t * v,
ra8_ui_rect_t * out )
nodiscard

The source rectangle currently visible, in source pixels.

The residency question in one call: these are exactly the pixels the next zoom_view_render will read, so a tiled adapter can turn it into a tile rectangle and prove that nothing outside it is pinned. The rectangle is clipped to the image, so a letterboxed view reports the image's own extent rather than the viewport's.

Parameters
[in]vOpen view.
[out]outReceives the visible source rectangle.
Returns
ra8_err_t Error code.
Return values
k_ra8_okout holds the visible source rectangle.
k_ra8_err_null_ptrv or out is NULL.
k_ra8_err_invalid_statev is not open.
Precondition
v was opened by zoom_view_open.
out addresses writable storage for one ra8_ui_rect_t.
Postcondition
out->x + out->w <= (int32_t)v->src.width and likewise for y/h.
out->w >= 1 and out->h >= 1 (a view always shows at least one pixel).
Note
Thread-safe with respect to this module: reads only.
See also
zoom_tiles_prefetch
Since
0.1.0

Definition at line 645 of file zoom.c.

References zoom_view_t::active, zoom_view_t::anchor_x, zoom_view_t::anchor_y, zoom_axis_t::count, zoom_view_t::dst, ra8_ui_rect_t::h, zoom_source_t::height, k_ra8_err_invalid_state, k_ra8_ok, priv_zoom_axis(), RA8_CHECK_NULL_PTR, ra8_log_error, zoom_axis_t::s0, s_tag, zoom_view_t::scale, zoom_view_t::src, ra8_ui_rect_t::w, zoom_source_t::width, ra8_ui_rect_t::x, and ra8_ui_rect_t::y.

Referenced by ez_apply_pan().

Variable Documentation

◆ s_tag

const char* const s_tag = "zoom"
static

Component tag for RA8_CHECK_* log lines.

Definition at line 32 of file zoom.c.