8#include "stb_image_write.h"
13 : w_(width), h_(height), pixels_(width * height * 4, 0)
18 : w_(width), h_(height), pixels_(bgraData, bgraData + width * height * 4)
24 if (
empty() || newWidth <= 0 || newHeight <= 0)
27 if (newWidth ==
w_ && newHeight ==
h_)
32 const float xRatio =
static_cast<float>(
w_) / newWidth;
33 const float yRatio =
static_cast<float>(
h_) / newHeight;
35 for (
int y = 0; y < newHeight; ++y)
37 const float srcY = y * yRatio;
38 const int y0 =
static_cast<int>(srcY);
39 const int y1 = std::min(y0 + 1,
h_ - 1);
40 const float fy = srcY - y0;
42 for (
int x = 0; x < newWidth; ++x)
44 const float srcX = x * xRatio;
45 const int x0 =
static_cast<int>(srcX);
46 const int x1 = std::min(x0 + 1,
w_ - 1);
47 const float fx = srcX - x0;
49 const uint8_t* p00 = &
pixels_[(y0 *
w_ + x0) * 4];
50 const uint8_t* p10 = &
pixels_[(y0 *
w_ + x1) * 4];
51 const uint8_t* p01 = &
pixels_[(y1 *
w_ + x0) * 4];
52 const uint8_t* p11 = &
pixels_[(y1 *
w_ + x1) * 4];
54 uint8_t* dst = &result.
pixels_[(y * newWidth + x) * 4];
55 for (
int c = 0; c < 4; ++c)
57 const float top = p00[c] * (1.0f - fx) + p10[c] * fx;
58 const float bot = p01[c] * (1.0f - fx) + p11[c] * fx;
59 dst[c] =
static_cast<uint8_t
>(std::clamp(top * (1.0f - fy) + bot * fy, 0.0f, 255.0f));
73 const int rowBytes =
w_ * 4;
74 for (
int y = 0; y <
h_; ++y)
75 std::memcpy(&result.
pixels_[y * rowBytes], &
pixels_[(
h_ - 1 - y) * rowBytes], rowBytes);
81static void bgraToRgba(
const uint8_t* src, uint8_t* dst,
int count)
83 for (
int i = 0; i < count; ++i)
85 dst[i * 4 + 0] = src[i * 4 + 2];
86 dst[i * 4 + 1] = src[i * 4 + 1];
87 dst[i * 4 + 2] = src[i * 4 + 0];
88 dst[i * 4 + 3] = src[i * 4 + 3];
97 const int count =
w_ *
h_;
98 std::vector<uint8_t> rgba(count * 4);
101 return stbi_write_png(path.c_str(),
w_,
h_, 4, rgba.data(),
w_ * 4) != 0;
108 utf8.reserve(path.size());
109 for (
wchar_t ch : path)
112 utf8.push_back(
static_cast<char>(ch));
115 utf8.push_back(
static_cast<char>(0xC0 | (ch >> 6)));
116 utf8.push_back(
static_cast<char>(0x80 | (ch & 0x3F)));
120 utf8.push_back(
static_cast<char>(0xE0 | (ch >> 12)));
121 utf8.push_back(
static_cast<char>(0x80 | ((ch >> 6) & 0x3F)));
122 utf8.push_back(
static_cast<char>(0x80 | (ch & 0x3F)));
131 return static_cast<uint8_t
>(std::clamp(v, 0.0f, 255.0f));
138 return 2.0f * base * blend;
140 return 1.0f - 2.0f * (1.0f - base) * (1.0f - blend);
149 const int srcW = std::min(src.
w_,
w_ - destX);
150 const int srcH = std::min(src.
h_,
h_ - destY);
152 if (srcW <= 0 || srcH <= 0)
155 for (
int y = 0; y < srcH; ++y)
157 for (
int x = 0; x < srcW; ++x)
159 const uint8_t* s = &src.
pixels_[(y * src.
w_ + x) * 4];
160 uint8_t* d = &
pixels_[((destY + y) *
w_ + (destX + x)) * 4];
162 const float sa = s[3] / 255.0f;
167 for (
int c = 0; c < 3; ++c)
169 const float sc = s[c] / 255.0f;
170 const float dc = d[c] / 255.0f;
171 const float blended = sc * dc;
172 d[c] =
clampByte((blended * sa + dc * (1.0f - sa)) * 255.0f);
174 d[3] =
clampByte((sa + (d[3] / 255.0f) * (1.0f - sa)) * 255.0f);
176 else if (blendMode == 6)
178 for (
int c = 0; c < 3; ++c)
180 const float sc = s[c] / 255.0f;
181 const float dc = d[c] / 255.0f;
183 d[c] =
clampByte((blended * sa + dc * (1.0f - sa)) * 255.0f);
185 d[3] =
clampByte((sa + (d[3] / 255.0f) * (1.0f - sa)) * 255.0f);
189 const float da = d[3] / 255.0f;
190 const float outA = sa + da * (1.0f - sa);
193 for (
int c = 0; c < 3; ++c)
195 const float sc = s[c] / 255.0f;
196 const float dc = d[c] / 255.0f;
197 d[c] =
clampByte(((sc * sa + dc * da * (1.0f - sa)) / outA) * 255.0f);
216 uint8_t* rgb = stbi_load_from_memory(
data, size, &w, &h, &channels, 4);
220 LOG_ERROR <<
"SoftwareImage::loadFromMemory failed:" << stbi_failure_reason();
226 const int count = w * h;
227 for (
int i = 0; i < count; ++i)
229 img.
pixels_[i * 4 + 0] = rgb[i * 4 + 2];
230 img.
pixels_[i * 4 + 1] = rgb[i * 4 + 1];
231 img.
pixels_[i * 4 + 2] = rgb[i * 4 + 0];
232 img.
pixels_[i * 4 + 3] = rgb[i * 4 + 3];
235 stbi_image_free(rgb);
static void bgraToRgba(const uint8_t *src, uint8_t *dst, int count)
static float overlayChannel(float base, float blend)
static uint8_t clampByte(float v)
CPU-side image buffer storing BGRA pixel data.
std::vector< uint8_t > pixels_
void composite(const SoftwareImage &src, int destX, int destY, int blendMode=1)
Composite a source image onto this image at the given position.
SoftwareImage mirrored() const
Return a vertically mirrored copy.
bool savePNG(const std::string &path) const
Save as PNG to the given file path.
bool empty() const
True if the image has zero dimensions.
void assign(const SoftwareImage &src)
Replace contents entirely with a copy of src.
static SoftwareImage loadFromMemory(const uint8_t *data, int size)
Load a JPEG image from a memory buffer.
SoftwareImage scaled(int newWidth, int newHeight) const
Return a scaled copy using bilinear interpolation.
uint8_t * data()
Mutable pointer to the raw BGRA pixel buffer.