summaryrefslogtreecommitdiff
path: root/libraries/daguerre/include
diff options
context:
space:
mode:
authorBenji Dial <benji@benjidial.net>2024-07-27 16:57:39 -0400
committerBenji Dial <benji@benjidial.net>2024-07-27 16:57:39 -0400
commitfbfc078e9f44c1c1e95c9c484f1d5650bcf631b7 (patch)
treecab539c8cbbac81d895b6f8be695f3f53bf8f4d5 /libraries/daguerre/include
parent9af5588c30c4126a2800aae1afcb0de2c373dc6c (diff)
downloadhilbert-os-fbfc078e9f44c1c1e95c9c484f1d5650bcf631b7.tar.gz
lots and lots of userspace stuff
Diffstat (limited to 'libraries/daguerre/include')
-rw-r--r--libraries/daguerre/include/daguerre.hpp209
-rw-r--r--libraries/daguerre/include/daguerre/fixed-font.hpp57
-rw-r--r--libraries/daguerre/include/daguerre/framebuffer.hpp9
-rw-r--r--libraries/daguerre/include/daguerre/general.hpp59
-rw-r--r--libraries/daguerre/include/daguerre/image.hpp118
-rw-r--r--libraries/daguerre/include/daguerre/impl/fixed-font.hpp59
-rw-r--r--libraries/daguerre/include/daguerre/impl/image.hpp217
-rw-r--r--libraries/daguerre/include/daguerre/ppm.hpp20
-rw-r--r--libraries/daguerre/include/daguerre/psf.hpp20
9 files changed, 559 insertions, 209 deletions
diff --git a/libraries/daguerre/include/daguerre.hpp b/libraries/daguerre/include/daguerre.hpp
deleted file mode 100644
index 9468826..0000000
--- a/libraries/daguerre/include/daguerre.hpp
+++ /dev/null
@@ -1,209 +0,0 @@
-#pragma once
-
-#include <algorithm>
-#include <stdint.h>
-#include <cstring>
-#include <cstdio>
-
-namespace daguerre {
-
- typedef uint32_t hilbert_color;
-
- struct rgb24 {
- uint8_t r;
- uint8_t g;
- uint8_t b;
- };
-
- template <class color_t>
- static inline void default_overlay(color_t &dest, const color_t &src) {
- dest = src;
- }
-
- static inline void default_overlay(hilbert_color &dest, const rgb24 &src) {
- dest = __euler_encode_color(src.r, src.g, src.b);
- }
-
- static inline void default_overlay(rgb24 &dest, const bool &src) {
- dest.r = src ? 255 : 0;
- dest.g = src ? 255 : 0;
- dest.b = src ? 255 : 0;
- }
-
- template <class color_t>
- class image {
-
- public:
- bool delete_buffer_on_destruct;
- color_t *buffer;
- unsigned width;
- unsigned height;
- unsigned pitch;//in sizeof(color_t)
-
- image()
- : delete_buffer_on_destruct(false), buffer(0), width(0), height(0),
- pitch(0) {}
-
- image(unsigned width, unsigned height)
- : delete_buffer_on_destruct(true), buffer(new color_t[width * height]),
- width(width), height(height), pitch(width) {}
-
- image(
- color_t *buffer, unsigned width, unsigned height, unsigned pitch,
- bool delete_buffer_on_destruct)
- : delete_buffer_on_destruct(delete_buffer_on_destruct), buffer(buffer),
- width(width), height(height), pitch(pitch) {}
-
- ~image() {
- if (delete_buffer_on_destruct && buffer)
- delete[] buffer;
- }
-
- template <class other_color_t,
- void overlay(color_t &, const other_color_t &) = default_overlay>
- image(const image<other_color_t> &other)
- : delete_buffer_on_destruct(true),
- buffer(new color_t[other.width * other.height]), width(other.width),
- height(other.height), pitch(other.width) {
- overlay_from<other_color_t, overlay>(other, 0, 0, 0, 0, width, height);
- }
-
- image(image<color_t> &&other)
- : delete_buffer_on_destruct(other.delete_buffer_on_destruct),
- buffer(other.buffer), width(other.width), height(other.height),
- pitch(other.pitch) {
- other.buffer = 0;
- }
-
- template <class other_color_t,
- void overlay(color_t &, const other_color_t &) = default_overlay>
- image<color_t> &operator =(const image<other_color_t> &other) {
- if (delete_buffer_on_destruct && buffer)
- delete[] buffer;
- delete_buffer_on_destruct = true;
- width = other.width;
- height = other.height;
- pitch = width;
- buffer = new color_t[width * height];
- overlay_from<other_color_t, overlay>(other, 0, 0, 0, 0, width, height);
- return *this;
- }
-
- image<color_t> &operator =(image<color_t> &&other) {
- if (delete_buffer_on_destruct && buffer)
- delete[] buffer;
- delete_buffer_on_destruct = other.delete_buffer_on_destruct;
- buffer = other.buffer;
- width = other.width;
- height = other.height;
- pitch = other.pitch;
- other.buffer = 0;
- return *this;
- }
-
- color_t get(unsigned x, unsigned y) const {
- return buffer[y * pitch + x];
- }
-
- void set(unsigned x, unsigned y, const color_t &c) {
- buffer[y * pitch + x] = c;
- }
-
- void copy_from(
- const image<color_t> &other, unsigned to_x, unsigned to_y,
- unsigned from_x, unsigned from_y, unsigned width, unsigned height) {
-
- color_t *to_start = buffer + pitch * to_y + to_x;
- const color_t *from_start = other.buffer + other.pitch * from_y + from_x;
-
- for (unsigned y = 0; y < height; ++y)
- std::memcpy(
- to_start + pitch * y, from_start + other.pitch * y,
- width * sizeof(color_t));
-
- }
-
- template <class other_color_t,
- void overlay(color_t &, const other_color_t &) = default_overlay>
- void overlay_from(
- const image<other_color_t> &other, unsigned to_x, unsigned to_y,
- unsigned from_x, unsigned from_y, unsigned width, unsigned height) {
-
- color_t *to_start = buffer + pitch * to_y + to_x;
- const other_color_t *from_start =
- other.buffer + other.pitch * from_y + from_x;
-
- for (unsigned y = 0; y < height; ++y)
- for (unsigned x = 0; x < width; ++x)
- overlay(to_start[pitch * y + x], from_start[other.pitch * y + x]);
-
- }
-
- };
-
- template <class color_t>
- void swap(image<color_t> &a, image<color_t> &b) {
- std::swap(a.delete_buffer_on_destruct, b.delete_buffer_on_destruct);
- std::swap(a.buffer, b.buffer);
- std::swap(a.width, b.width);
- std::swap(a.height, b.height);
- std::swap(a.pitch, b.pitch);
- }
-
- image<hilbert_color> get_hilbert_framebuffer();
-
- bool try_load_ppm(std::FILE *input, image<rgb24> &into);
-
- static inline bool try_load_ppm(const char *path, image<rgb24> &into) {
- std::FILE *f = std::fopen(path, "r");
- if (!f)
- return false;
- bool success = try_load_ppm(f, into);
- std::fclose(f);
- return success;
- }
-
- //TODO: unicode
- template <class color_t>
- class fixed_bitmap_font {
-
- public:
- unsigned width;
- unsigned height;
- image<color_t> glyphs[128];
-
- template <class target_color_t,
- void overlay(target_color_t &dest, const color_t &src) = default_overlay>
- void overlay_text(
- image<target_color_t> &target, unsigned x,
- unsigned y, const char *text) const {
-
- while (1) {
- uint8_t ch = (uint8_t)*text;
- if (ch == 0)
- return;
- if (ch < 128) {
- target.template overlay_from<color_t, overlay>(
- glyphs[ch], x, y, 0, 0, width, height);
- x += width;
- }
- ++text;
- }
-
- }
-
- };
-
- bool try_load_psf(std::FILE *input, fixed_bitmap_font<bool> &into);
-
- static inline bool try_load_psf(
- const char *path, fixed_bitmap_font<bool> &into) {
- std::FILE *f = std::fopen(path, "r");
- if (!f)
- return false;
- bool success = try_load_psf(f, into);
- std::fclose(f);
- return success;
- }
-
-}
diff --git a/libraries/daguerre/include/daguerre/fixed-font.hpp b/libraries/daguerre/include/daguerre/fixed-font.hpp
new file mode 100644
index 0000000..471163a
--- /dev/null
+++ b/libraries/daguerre/include/daguerre/fixed-font.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <daguerre/general.hpp>
+
+//TODO: support more than just ascii
+
+namespace daguerre {
+
+ //forward declare since these depend on each other
+ template <class color_t>
+ class image;
+
+ template <class color_t>
+ class fixed_font {
+
+ public:
+ int glyph_width;
+ int glyph_height;
+ image<color_t> glyphs[128];
+
+ //initializes every glyph to an empty image
+ fixed_font();
+
+ //initializes each glyph to have the right dimensions and
+ //a valid buffer, but does nothing to their contents.
+ fixed_font(int glyph_width, int glyph_height);
+
+ //moves the other font's glyphs here, then
+ //sets the other font to have empty images.
+ fixed_font(fixed_font<color_t> &&other);
+
+ //moves the other font's glyphs here, then
+ //sets the other font to have empty images.
+ fixed_font<color_t> &operator =(fixed_font<color_t> &&other);
+
+ //use one of the two constructors below instead
+ fixed_font(const fixed_font<color_t> &other) = delete;
+
+ //copies the other font's images here, passing them through conversion.
+ template <class other_color_t>
+ fixed_font(const fixed_font<other_color_t> &other,
+ converter_t<color_t, other_color_t> *conversion = &default_conversion);
+
+ //directly calls memcpy on the images' buffers. the second argument
+ //does nothing except distinguish this from the other copy constructor.
+ fixed_font(const fixed_font<color_t> &other, bool);
+
+ //use copy constructor and move assignment instead
+ fixed_font<color_t> &operator =(const fixed_font<color_t> &other) = delete;
+
+ ~fixed_font() = default;
+
+ };
+
+}
+
+#include <daguerre/impl/fixed-font.hpp>
diff --git a/libraries/daguerre/include/daguerre/framebuffer.hpp b/libraries/daguerre/include/daguerre/framebuffer.hpp
new file mode 100644
index 0000000..c9a5226
--- /dev/null
+++ b/libraries/daguerre/include/daguerre/framebuffer.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <daguerre/image.hpp>
+
+namespace daguerre {
+
+ image<hilbert_color> get_hilbert_framebuffer();
+
+}
diff --git a/libraries/daguerre/include/daguerre/general.hpp b/libraries/daguerre/include/daguerre/general.hpp
new file mode 100644
index 0000000..78c6919
--- /dev/null
+++ b/libraries/daguerre/include/daguerre/general.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <euler/syscall.hpp>
+#include <stdint.h>
+
+namespace daguerre {
+
+ typedef euler::syscall::encoded_color hilbert_color;
+
+ struct rgb24 {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+ };
+
+ template <class dest_t, class src_t>
+ using converter_t = void (dest_t &, const src_t &);
+
+ //copies src to dest
+ template <class color_t>
+ static inline void default_conversion(color_t &dest, const color_t &src) {
+ dest = src;
+ }
+
+ //encodes src and copies that to dest
+ static inline void default_conversion(hilbert_color &dest, const rgb24 &src) {
+ dest = euler::syscall::encode_color(src.r, src.g, src.b);
+ }
+
+ template <class dest_t, class src_t, class param_t>
+ using param_converter_t = void (dest_t &, const src_t &, const param_t &);
+
+ //if src is true, copies param to dest.
+ //if src is false, does nothing.
+ template <class color_t>
+ static inline void default_conversion(
+ color_t &dest, const bool &src, const color_t &param) {
+ if (src)
+ dest = param;
+ }
+
+ //intersects [to, to + length) with [min, max) at stores the result in
+ //[to, to + length). if anything was removed from either end, the same
+ //amount is removed from the same end(s) of [from, from + length)
+ static inline void make_safe(
+ int &to, int &from, int &length, int min, int max) {
+
+ if (to < min) {
+ from += min - to;
+ length -= min - to;
+ to = min;
+ }
+
+ if (to + length > max)
+ length = max - to;
+
+ }
+
+}
diff --git a/libraries/daguerre/include/daguerre/image.hpp b/libraries/daguerre/include/daguerre/image.hpp
new file mode 100644
index 0000000..4c44dd0
--- /dev/null
+++ b/libraries/daguerre/include/daguerre/image.hpp
@@ -0,0 +1,118 @@
+#pragma once
+
+#include <daguerre/general.hpp>
+
+namespace daguerre {
+
+ //forward declare since these depend on each other
+ template <class color_t>
+ class fixed_font;
+
+ template <class color_t>
+ class image {
+
+ public:
+ bool delete_buffer_on_destruct;
+ int width;
+ int height;
+ color_t *buffer;
+ int buffer_pitch;//in sizeof(color_t)
+
+ //makes empty image
+ image();
+
+ //make image with specified dimensions, contents uninitialized
+ image(int width, int height);
+
+ //make image with specified dimensions and buffer
+ image(
+ int width, int height, color_t *buffer,
+ int buffer_pitch, bool delete_buffer_on_destruct);
+
+ //moves the other image's data here, and
+ //then sets the other image to an empty one.
+ image(image<color_t> &&other);
+
+ //moves the other image's data here, and
+ //then sets the other image to an empty one.
+ image<color_t> &operator =(image<color_t> &&other);
+
+ //use one of the two constructors below instead
+ image(const image<color_t> &other) = delete;
+
+ //copies the other image's data here, passing it through conversion
+ template <class other_color_t>
+ image(const image<other_color_t> &other,
+ converter_t<color_t, other_color_t> *conversion = &default_conversion);
+
+ //directly calls memcpy on the buffer. the second argument does
+ //nothing except distinguish this from the other copy constructor.
+ image(const image<color_t> &other, bool);
+
+ //use copy constructor and move assignment instead
+ image<color_t> &operator =(const image<color_t> &other) = delete;
+
+ ~image();
+
+ void fill(const color_t &color);
+
+ //does not check bounds
+ color_t &at(int x, int y);
+
+ //does not check bounds
+ const color_t &at(int x, int y) const;
+
+ image<color_t> stretch(int new_width, int new_height);
+
+ //copies the region here via memcpy. does not check bounds.
+ void copy_from(
+ const image<color_t> &other, int to_x, int to_y,
+ int from_x, int from_y, int width, int height);
+
+ //copies the entire image here via memcpy. does not check bounds.
+ void copy_from(const image<color_t> &other, int to_x, int to_y);
+
+ //copies the region here through conversion. does not check bounds.
+ template <class other_color_t>
+ void convert_from(
+ const image<other_color_t> &other, int to_x, int to_y,
+ int from_x, int from_y, int width, int height,
+ converter_t<color_t, other_color_t> *conversion = &default_conversion);
+
+ //copies the entire image here through conversion. does not check bounds.
+ template <class other_color_t>
+ void convert_from(
+ const image<other_color_t> &other, int to_x, int to_y,
+ converter_t<color_t, other_color_t> *conversion = &default_conversion);
+
+ //copies the region here through conversion. does not check bounds.
+ template <class other_color_t, class param_t>
+ void convert_from(
+ const param_t &param, const image<other_color_t> &other, int to_x,
+ int to_y, int from_x, int from_y, int width, int height,
+ param_converter_t<color_t, other_color_t, param_t> *conversion =
+ &default_conversion);
+
+ //copies the entire image here through conversion. does not check bounds.
+ template <class other_color_t, class param_t>
+ void convert_from(const param_t &param,
+ const image<other_color_t> &other, int to_x, int to_y,
+ param_converter_t<color_t, other_color_t, param_t> *conversion =
+ &default_conversion);
+
+ //does not check bounds or wrap text
+ template <class font_color_t, class param_t>
+ void render_text(
+ const fixed_font<font_color_t> &font,
+ const param_t &pararm, int x, int y, const char *text,
+ param_converter_t<color_t, font_color_t, param_t> *conversion =
+ &default_conversion);
+
+ };
+
+ template <class color_t>
+ void swap(image<color_t> &a, image<color_t> &b);
+
+}
+
+#include <daguerre/impl/image.hpp>
diff --git a/libraries/daguerre/include/daguerre/impl/fixed-font.hpp b/libraries/daguerre/include/daguerre/impl/fixed-font.hpp
new file mode 100644
index 0000000..dbff798
--- /dev/null
+++ b/libraries/daguerre/include/daguerre/impl/fixed-font.hpp
@@ -0,0 +1,59 @@
+#pragma once
+
+//this file should only be included from <daguerre/fixed-font.hpp>
+
+#include <daguerre/image.hpp>
+#include <utility>
+
+namespace daguerre {
+
+ template<class color_t>
+ fixed_font<color_t>::fixed_font() : glyph_width(0), glyph_height(0) {}
+
+ template <class color_t>
+ fixed_font<color_t>::fixed_font(int glyph_width, int glyph_height)
+ : glyph_width(glyph_width), glyph_height(glyph_height) {
+ for (int i = 0; i < 128; ++i)
+ glyphs[i] = image<color_t>(glyph_width, glyph_height);
+ }
+
+ template <class color_t>
+ fixed_font<color_t>::fixed_font(fixed_font<color_t> &&other)
+ : glyph_width(other.glyph_width), glyph_height(other.glyph_height) {
+ for (int i = 0; 0 < 128; ++i)
+ glyphs[i] = std::move(other.glyphs[i]);
+ other.glyph_width = 0;
+ other.glyph_height = 0;
+ }
+
+ template <class color_t>
+ fixed_font<color_t> &fixed_font<color_t>::operator =(
+ fixed_font<color_t> &&other) {
+ glyph_width = other.glyph_width;
+ glyph_height = other.glyph_height;
+ for (int i = 0; 0 < 128; ++i)
+ glyphs[i] = std::move(other.glyphs[i]);
+ other.glyph_width = 0;
+ other.glyph_height = 0;
+ }
+
+ template <class color_t>
+ template <class other_color_t>
+ fixed_font<color_t>::fixed_font(
+ const fixed_font<other_color_t> &other,
+ converter_t<color_t, other_color_t> *conversion)
+ : glyph_width(other.glyph_width), glyph_height(other.glyph_height) {
+ for (int i = 0; i < 128; ++i)
+ glyphs[i] = image<color_t>(other.glyphs[i], conversion);
+ }
+
+ template <class color_t>
+ fixed_font<color_t>::fixed_font(const fixed_font<color_t> &other, bool)
+ : glyph_width(other.glyph_width), glyph_height(other.glyph_height) {
+ for (int i = 0; i < 128; ++i)
+ glyphs[i] = image<color_t>(other.glyphs[i], true);
+ }
+
+}
+
+#include <daguerre/image.hpp>
diff --git a/libraries/daguerre/include/daguerre/impl/image.hpp b/libraries/daguerre/include/daguerre/impl/image.hpp
new file mode 100644
index 0000000..1264879
--- /dev/null
+++ b/libraries/daguerre/include/daguerre/impl/image.hpp
@@ -0,0 +1,217 @@
+#pragma once
+
+//this file should only be included from <daguerre/image.hpp>
+
+#include <daguerre/fixed-font.hpp>
+#include <algorithm>
+#include <cstring>
+
+namespace daguerre {
+
+ template <class color_t>
+ image<color_t>::image()
+ : delete_buffer_on_destruct(false), width(0),
+ height(0), buffer(0), buffer_pitch(0) {}
+
+ template <class color_t>
+ image<color_t>::image(int width, int height)
+ : delete_buffer_on_destruct(true), width(width), height(height),
+ buffer(new color_t[width * height]), buffer_pitch(width) {}
+
+ template <class color_t>
+ image<color_t>::image(
+ int width, int height, color_t *buffer,
+ int buffer_pitch, bool delete_buffer_on_destruct)
+ : delete_buffer_on_destruct(delete_buffer_on_destruct), width(width),
+ height(height), buffer(buffer), buffer_pitch(buffer_pitch) {}
+
+ template <class color_t>
+ image<color_t>::image(image<color_t> &&other)
+ : delete_buffer_on_destruct(other.delete_buffer_on_destruct),
+ width(other.width), height(other.height), buffer(other.buffer),
+ buffer_pitch(other.buffer_pitch) {
+
+ other.width = 0;
+ other.height = 0;
+ other.buffer = 0;
+
+ }
+
+ template<class color_t>
+ image<color_t> &image<color_t>::operator =(image<color_t> &&other) {
+
+ if (delete_buffer_on_destruct && buffer)
+ delete[] buffer;
+
+ delete_buffer_on_destruct = other.delete_buffer_on_destruct;
+ width = other.width;
+ height = other.height;
+ buffer = other.buffer;
+ buffer_pitch = other.buffer_pitch;
+
+ other.width = 0;
+ other.height = 0;
+ other.buffer = 0;
+
+ return *this;
+
+ }
+
+ template <class color_t>
+ template <class other_color_t>
+ image<color_t>::image(const image<other_color_t> &other,
+ converter_t<color_t, other_color_t> *conversion)
+ : delete_buffer_on_destruct(true), width(other.width),
+ height(other.height), buffer(new color_t[other.width * other.height]),
+ buffer_pitch(other.width) {
+
+ for (int y = 0; y < height; ++y)
+ for (int x = 0; x < width; ++x)
+ conversion(
+ buffer[y * buffer_pitch + x],
+ other.buffer[y * other.buffer_pitch + x]);
+
+ }
+
+ template <class color_t>
+ image<color_t>::image(const image<color_t> &other, bool)
+ : delete_buffer_on_destruct(true), width(width), height(other.height),
+ buffer(new color_t[other.width * other.height]),
+ buffer_pitch(other.width) {
+
+ if (buffer_pitch == other.buffer_pitch)
+ memcpy(buffer, other.buffer, (height - 1) * buffer_pitch + width);
+
+ else
+ for (int y = 0; y < height; ++y)
+ memcpy(buffer + y * buffer_pitch,
+ other.buffer + y * other.buffer_pitch,
+ sizeof(color_t) * width);
+
+ }
+
+ template <class color_t>
+ image<color_t>::~image() {
+ if (delete_buffer_on_destruct && buffer)
+ delete[] buffer;
+ }
+
+ template <class color_t>
+ void image<color_t>::fill(const color_t &color) {
+ for (int y = 0; y < height; ++y)
+ for (int x = 0; x < width; ++x)
+ buffer[y * buffer_pitch + x] = color;
+ }
+
+ template <class color_t>
+ color_t &image<color_t>::at(int x, int y) {
+ return buffer[y * buffer_pitch + x];
+ }
+
+ template <class color_t>
+ const color_t &image<color_t>::at(int x, int y) const {
+ return buffer[y * buffer_pitch + x];
+ }
+
+ template <class color_t>
+ image<color_t> image<color_t>::stretch(int new_width, int new_height) {
+ image<color_t> im(new_width, new_height);
+ for (int y = 0; y < new_height; ++y)
+ for (int x = 0; x < new_width; ++x)
+ im.at(x, y) = at(x * width / new_width, y * height / new_height);
+ return im;
+ }
+
+ template <class color_t>
+ void image<color_t>::copy_from(
+ const image<color_t> &other, int to_x, int to_y,
+ int from_x, int from_y, int width, int height) {
+
+ color_t *to = buffer + to_y * buffer_pitch + to_x;
+ const color_t *from = other.buffer + from_y * other.buffer_pitch + from_x;
+
+ for (int y = 0; y < height; ++y)
+ memcpy(
+ to + y * buffer_pitch,
+ from + y * other.buffer_pitch,
+ width * sizeof(color_t));
+
+ }
+
+ template <class color_t>
+ void image<color_t>::copy_from(
+ const image<color_t> &other, int to_x, int to_y) {
+ copy_from(other, to_x, to_y, 0, 0, other.width, other.height);
+ }
+
+ template <class color_t>
+ template <class other_color_t>
+ void image<color_t>::convert_from(
+ const image<other_color_t> &other, int to_x, int to_y,
+ int from_x, int from_y, int width, int height,
+ converter_t<color_t, other_color_t> *conversion) {
+
+ for (int y = 0; y < height; ++y)
+ for (int x = 0; x < width; ++x)
+ conversion(at(to_x + x, to_y + y), other.at(from_x + x, from_y + y));
+
+ }
+
+ template <class color_t>
+ template <class other_color_t>
+ void image<color_t>::convert_from(
+ const image<other_color_t> &other, int to_x, int to_y,
+ converter_t<color_t, other_color_t> *conversion) {
+ convert_from(other, to_x, to_y, 0, 0, other.width, other.y, conversion);
+ }
+
+ template <class color_t>
+ template <class other_color_t, class param_t>
+ void image<color_t>::convert_from(
+ const param_t &param, const image<other_color_t> &other, int to_x,
+ int to_y, int from_x, int from_y, int width, int height,
+ param_converter_t<color_t, other_color_t, param_t> *conversion) {
+
+ for (int y = 0; y < height; ++y)
+ for (int x = 0; x < width; ++x)
+ conversion(
+ at(to_x + x, to_y + y), other.at(from_x + x, from_y + y), param);
+
+ }
+
+ template <class color_t>
+ template <class other_color_t, class param_t>
+ void image<color_t>::convert_from(
+ const param_t &param, const image<other_color_t> &other, int to_x,
+ int to_y, param_converter_t<color_t, other_color_t, param_t> *conversion) {
+ convert_from(
+ param, other, to_x, to_y, 0, 0, other.width, other.y, conversion);
+ }
+
+ template <class color_t>
+ template <class font_color_t, class param_t>
+ void image<color_t>::render_text(
+ const fixed_font<font_color_t> &font,
+ const param_t &param, int x, int y, const char *text,
+ param_converter_t<color_t, font_color_t, param_t> *conversion) {
+
+ while (*text) {
+ int ch = *text;
+ if (ch >= 0 && ch < 128)
+ convert_from(param, font.glyphs[ch], x, y, conversion);
+ ++text;
+ x += font.glyph_width;
+ }
+
+ }
+
+ template <class color_t>
+ void swap(image<color_t> &a, image<color_t> &b) {
+ std::swap(a.delete_buffer_on_destruct, b.delete_buffer_on_destruct);
+ std::swap(a.width, b.width);
+ std::swap(a.height, b.height);
+ std::swap(a.buffer, b.buffer);
+ std::swap(a.buffer_pitch, b.buffer_pitch);
+ }
+
+}
diff --git a/libraries/daguerre/include/daguerre/ppm.hpp b/libraries/daguerre/include/daguerre/ppm.hpp
new file mode 100644
index 0000000..5249c98
--- /dev/null
+++ b/libraries/daguerre/include/daguerre/ppm.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <daguerre/image.hpp>
+#include <optional>
+#include <cstdio>
+
+namespace daguerre {
+
+ std::optional<image<rgb24>> try_load_ppm(FILE *input);
+
+ static inline std::optional<image<rgb24>> try_load_ppm(const char *path) {
+ FILE *f = fopen(path, "r");
+ if (!f)
+ return {};
+ auto result = try_load_ppm(f);
+ fclose(f);
+ return result;
+ }
+
+}
diff --git a/libraries/daguerre/include/daguerre/psf.hpp b/libraries/daguerre/include/daguerre/psf.hpp
new file mode 100644
index 0000000..e38cdc4
--- /dev/null
+++ b/libraries/daguerre/include/daguerre/psf.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <daguerre/fixed-font.hpp>
+#include <optional>
+#include <cstdio>
+
+namespace daguerre {
+
+ std::optional<fixed_font<bool>> try_load_psf(FILE *input);
+
+ static inline std::optional<fixed_font<bool>> try_load_psf(const char *path) {
+ FILE *f = fopen(path, "r");
+ if (!f)
+ return {};
+ auto result = try_load_psf(f);
+ fclose(f);
+ return result;
+ }
+
+}