Haio 0.2.X
Homebrew Asset IO/CDN
Loading...
Searching...
No Matches
haio_transform.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "haio_codec.hpp"
4
5#include <algorithm>
6#include <stdexcept>
7
8/**
9 * geometry on addressable pixels. the colour no longer has to be rgba8888: the only
10 * thing these ever needed from it was how wide a pixel is, which PixelStride answers,
11 * so a crop on rgb565 stops going through a conversion it never used.
12 */
13namespace Haio {
14
15/**
16 * a transformed picture keeps everything about the original except its pixels.
17 *
18 * building a fresh Image would drop whatever else the colour carries, which for a
19 * palette is the palette: a cropped picture would come back as indices into nothing.
20 */
21template <Color P>
22Image<P> withPixels(const Image<P>& from, int width, int height, std::vector<uint8_t> pixels) {
23 Image<P> out = from;
24 out.width = width;
25 out.height = height;
26 out.data = std::move(pixels);
27 return out;
28}
29
30template <Color P>
31 requires Addressable<P>
32Image<P> cropImage(const Image<P>& image, Rect rect) {
33 constexpr auto stride = strideOf(P);
34
35 const int x0 = std::clamp(rect.x, 0, image.width);
36 const int y0 = std::clamp(rect.y, 0, image.height);
37 const int x1 = std::clamp(rect.x + rect.width, x0, image.width);
38 const int y1 = std::clamp(rect.y + rect.height, y0, image.height);
39 const int width = x1 - x0;
40 const int height = y1 - y0;
41
42 std::vector<uint8_t> out(static_cast<size_t>(width) * static_cast<size_t>(height) * stride);
43 for (int y = 0; y < height; y++) {
44 const auto src = (static_cast<size_t>(y0 + y) * static_cast<size_t>(image.width) + static_cast<size_t>(x0)) * stride;
45 const auto dst = static_cast<size_t>(y) * static_cast<size_t>(width) * stride;
46 std::copy_n(image.data.data() + src, static_cast<size_t>(width) * stride, out.data() + dst);
47 }
48 return withPixels(image, width, height, std::move(out));
49}
50
51/** nearest neighbour: one side may be left at zero and follows the other */
52template <Color P>
53 requires Addressable<P>
54Image<P> resizeImage(const Image<P>& image, Size size) {
55 constexpr auto stride = strideOf(P);
56
57 if (size.width <= 0 && size.height <= 0) throw std::runtime_error("resize expects a positive size");
58
59 int width = size.width;
60 int height = size.height;
61 if (width <= 0) width = std::max(1, image.width * height / image.height);
62 if (height <= 0) height = std::max(1, image.height * width / image.width);
63
64 std::vector<uint8_t> out(static_cast<size_t>(width) * static_cast<size_t>(height) * stride);
65 for (int y = 0; y < height; y++) {
66 const int sy = std::min(image.height - 1, y * image.height / height);
67 for (int x = 0; x < width; x++) {
68 const int sx = std::min(image.width - 1, x * image.width / width);
69 const auto src = (static_cast<size_t>(sy) * static_cast<size_t>(image.width) + static_cast<size_t>(sx)) * stride;
70 const auto dst = (static_cast<size_t>(y) * static_cast<size_t>(width) + static_cast<size_t>(x)) * stride;
71 std::copy_n(image.data.data() + src, stride, out.data() + dst);
72 }
73 }
74 return withPixels(image, width, height, std::move(out));
75}
76
77/**
78 * clears the alpha outside a rounded corner, so this one asks for more than the other
79 * two: a colour with no alpha has no way to say "not here", and asking for square
80 * corners back would be a worse answer than refusing to compile.
81 */
82template <Color P>
83 requires Maskable<P>
84Image<P> roundImageCorners(const Image<P>& image, int radius) {
85 constexpr auto stride = strideOf(P);
86 constexpr auto alpha = static_cast<size_t>(alphaOffsetOf(P));
87
88 if (radius <= 0) return image;
89
90 Image<P> out = image;
91 const int r = std::min(radius, std::min(image.width, image.height) / 2);
92 const int r2 = r * r;
93
94 auto maskCorner = [&](int x, int y, int cx, int cy) {
95 const int dx = x - cx;
96 const int dy = y - cy;
97 if (dx * dx + dy * dy > r2) {
98 const auto off = (static_cast<size_t>(y) * static_cast<size_t>(out.width) + static_cast<size_t>(x)) * stride + alpha;
99 out.data[off] = 0;
100 }
101 };
102
103 for (int y = 0; y < r; y++) {
104 for (int x = 0; x < r; x++) {
105 maskCorner(x, y, r - 1, r - 1);
106 maskCorner(out.width - 1 - x, y, out.width - r, r - 1);
107 maskCorner(x, out.height - 1 - y, r - 1, out.height - r);
108 maskCorner(out.width - 1 - x, out.height - 1 - y, out.width - r, out.height - r);
109 }
110 }
111
112 return out;
113}
114
115}
Definition haio.hpp:11
Image< P > cropImage(const Image< P > &image, Rect rect)
constexpr size_t strideOf(Color color)
constexpr int alphaOffsetOf(Color color)
Image< P > roundImageCorners(const Image< P > &image, int radius)
Image< P > withPixels(const Image< P > &from, int width, int height, std::vector< uint8_t > pixels)
Image< P > resizeImage(const Image< P > &image, Size size)
std::vector< uint8_t > data