feat: vector zoom for SVG in image viewer app
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* svg_doc.cpp
|
||||
* SvgDoc: source-backed rasterizable SVG document.
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include "svg_doc.hpp"
|
||||
|
||||
#include <montauk/syscall.h>
|
||||
#include <montauk/heap.h>
|
||||
#include <gui/gui.hpp>
|
||||
#include <gui/svg.hpp>
|
||||
|
||||
namespace imageviewer {
|
||||
|
||||
bool svgdoc_init(SvgDoc* doc, uint8_t* source, int source_len) {
|
||||
if (!doc) return false;
|
||||
*doc = {};
|
||||
if (!source || source_len <= 0) return false;
|
||||
|
||||
doc->source = source;
|
||||
doc->source_len = source_len;
|
||||
gui::svg_get_natural_size((const char*)source, source_len,
|
||||
&doc->natural_w, &doc->natural_h);
|
||||
if (doc->natural_w < 1) doc->natural_w = 1;
|
||||
if (doc->natural_h < 1) doc->natural_h = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool svgdoc_render(SvgDoc* doc, float target_scale, int max_edge) {
|
||||
if (!doc || !doc->source) return false;
|
||||
if (target_scale <= 0.0f) target_scale = 1.0f;
|
||||
if (max_edge < 1) max_edge = 1;
|
||||
|
||||
// Clamp so the longest edge of the rasterized output stays within max_edge.
|
||||
int natural_long = doc->natural_w > doc->natural_h ? doc->natural_w : doc->natural_h;
|
||||
float max_scale = (float)max_edge / (float)natural_long;
|
||||
if (target_scale > max_scale) target_scale = max_scale;
|
||||
if (target_scale < 0.01f) target_scale = 0.01f;
|
||||
|
||||
// Skip if cache is already close enough (avoids re-render on float jitter).
|
||||
if (doc->pixels && doc->render_scale > 0.0f) {
|
||||
float ratio = target_scale / doc->render_scale;
|
||||
if (ratio > 0.995f && ratio < 1.005f) return true;
|
||||
}
|
||||
|
||||
int new_w = (int)((float)doc->natural_w * target_scale);
|
||||
int new_h = (int)((float)doc->natural_h * target_scale);
|
||||
if (new_w < 1) new_w = 1;
|
||||
if (new_h < 1) new_h = 1;
|
||||
|
||||
gui::SvgIcon icon = gui::svg_render(
|
||||
(const char*)doc->source, doc->source_len,
|
||||
new_w, new_h, gui::Color::from_rgb(0, 0, 0));
|
||||
if (!icon.pixels) return false;
|
||||
|
||||
if (doc->pixels) montauk::mfree(doc->pixels);
|
||||
doc->pixels = icon.pixels;
|
||||
doc->render_w = icon.width;
|
||||
doc->render_h = icon.height;
|
||||
doc->render_scale = target_scale;
|
||||
return true;
|
||||
}
|
||||
|
||||
void svgdoc_free(SvgDoc* doc) {
|
||||
if (!doc) return;
|
||||
if (doc->pixels) montauk::mfree(doc->pixels);
|
||||
if (doc->source) montauk::mfree(doc->source);
|
||||
*doc = {};
|
||||
}
|
||||
|
||||
} // namespace imageviewer
|
||||
Reference in New Issue
Block a user