add image scaler (box sampling)

This commit is contained in:
Green Sky
2025-11-05 00:01:42 +01:00
parent 2afa7a88a5
commit e8a15a58dd
3 changed files with 152 additions and 0 deletions

View File

@@ -53,6 +53,8 @@ target_sources(tomato PUBLIC
./image_loader_qoi.cpp ./image_loader_qoi.cpp
./image_loader_sdl_image.hpp ./image_loader_sdl_image.hpp
./image_loader_sdl_image.cpp ./image_loader_sdl_image.cpp
./image_scaler.hpp
./image_scaler.cpp
./texture_uploader.hpp ./texture_uploader.hpp
./sdlrenderer_texture_uploader.hpp ./sdlrenderer_texture_uploader.hpp

144
src/image_scaler.cpp Normal file
View File

@@ -0,0 +1,144 @@
#include "./image_scaler.hpp"
#include <cmath>
#include <cassert>
// requires ColorTmp to have * and + operators
struct ColorCanvas8888;
struct ColorFloat4 {
float v[4]{};
ColorFloat4& operator*=(const float scalar) {
v[0] *= scalar;
v[1] *= scalar;
v[2] *= scalar;
v[3] *= scalar;
return *this;
}
ColorFloat4 operator*(const float scalar) const {
ColorFloat4 newcf = *this;
newcf.v[0] *= scalar;
newcf.v[1] *= scalar;
newcf.v[2] *= scalar;
newcf.v[3] *= scalar;
return newcf;
}
ColorFloat4& operator/=(const float scalar) {
v[0] /= scalar;
v[1] /= scalar;
v[2] /= scalar;
v[3] /= scalar;
return *this;
}
ColorFloat4& operator+=(const ColorFloat4& color) {
v[0] += color.v[0];
v[1] += color.v[1];
v[2] += color.v[2];
v[3] += color.v[3];
return *this;
}
};
struct ColorCanvas8888 {
uint8_t* ptr {nullptr};
ColorFloat4 operator[](size_t i) const {
return {
{
float(ptr[i*4+0])/255.f,
float(ptr[i*4+1])/255.f,
float(ptr[i*4+2])/255.f,
float(ptr[i*4+3])/255.f,
}
};
}
void set(size_t i, const ColorFloat4& color) {
ptr[i*4+0] = std::round(color.v[0]*255.f);
ptr[i*4+1] = std::round(color.v[1]*255.f);
ptr[i*4+2] = std::round(color.v[2]*255.f);
ptr[i*4+3] = std::round(color.v[3]*255.f);
}
};
template<typename ColorCanvas, typename ColorTmp>
void image_scale(ColorCanvas& dst, const int dst_w, const int dst_h, const ColorCanvas& src, const int src_w, const int src_h) {
// Box sampling - Imagine projecting the new, smaller pixels onto the larger source, covering multiple pixel.
for (int y = 0; y < dst_h; y++) {
for (int x = 0; x < dst_w; x++) {
// We perform a weighted mean.
ColorTmp color;
float weight_sum = 0.f;
// Walk from upper edge to bottom edge (vertical)
const float edge_up = ((float)y * src_h) / dst_h;
const float edge_down = ((y + 1.f) * src_h) / dst_h;
for (float frac_pos_y = edge_up; frac_pos_y < edge_down;) {
const int src_y = (int)std::floor(frac_pos_y); assert(src_y < src_h);
const float frac_y = 1.f - (frac_pos_y - src_y);
// Walk from left edge to right edge (horizontal)
const float edge_left = ((float)x * src_w) / dst_w;
const float edge_right = ((x + 1.f) * src_w) / dst_w;
for (float frac_pos_x = edge_left; frac_pos_x < edge_right;) {
const int src_x = (int)std::floor(frac_pos_x); assert(src_x < src_w);
const float frac_x = 1.f - (frac_pos_x - src_x);
const float src_pixel_weight = frac_x * frac_y;
//const ColorTmp pixel_color = ImGui::ColorConvertU32ToFloat4(src[src_y * src_w + src_x]);
const ColorTmp pixel_color = src[src_y * src_w + src_x];
color += pixel_color * src_pixel_weight;
weight_sum += src_pixel_weight;
frac_pos_x += frac_x;
}
frac_pos_y += frac_y;
}
color /= weight_sum;
dst.set(y * dst_w + x, color);
}
}
}
bool image_scale(SDL_Surface* dst, SDL_Surface* src) {
if (dst->format != src->format) {
return false;
}
// TODO: handle other numbers of components beside 4
if (
src->format != SDL_PIXELFORMAT_RGBA8888 &&
src->format != SDL_PIXELFORMAT_ARGB8888 &&
src->format != SDL_PIXELFORMAT_BGRA8888 &&
src->format != SDL_PIXELFORMAT_ABGR8888 &&
src->format != SDL_PIXELFORMAT_RGBX8888 &&
src->format != SDL_PIXELFORMAT_XRGB8888 &&
src->format != SDL_PIXELFORMAT_BGRX8888 &&
src->format != SDL_PIXELFORMAT_XBGR8888
) {
return false;
}
ColorCanvas8888 dst_c{reinterpret_cast<uint8_t*>(dst->pixels)};
const ColorCanvas8888 src_c{reinterpret_cast<uint8_t*>(src->pixels)};
image_scale<ColorCanvas8888, ColorFloat4>(
dst_c,
dst->w, dst->h,
src_c,
src->w, src->h
);
return true;
}

6
src/image_scaler.hpp Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include <SDL3/SDL.h>
bool image_scale(SDL_Surface* dst, SDL_Surface* src);