Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image library enhancements #609

Merged
merged 5 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/c/parts/video/image/build.mk
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ IMAGE_SRCS := \
IMAGE_OBJS := $(patsubst %.cpp,$(PATH_INTERNAL_C)/parts/video/image/%.o,$(IMAGE_SRCS))

$(PATH_INTERNAL_C)/parts/video/image/%.o: $(PATH_INTERNAL_C)/parts/video/image/%.cpp
$(CXX) -O2 $(CXXFLAGS) -DDEPENDENCY_CONSOLE_ONLY -Wall $< -c -o $@
$(CXX) -O3 $(CXXFLAGS) -DDEPENDENCY_CONSOLE_ONLY -Wall $< -c -o $@

IMAGE_LIB := $(PATH_INTERNAL_C)/parts/video/image/image.a

Expand Down
54 changes: 31 additions & 23 deletions internal/c/parts/video/image/image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ extern const uint8_t charset8x8[256][8][8]; // used by func__saveimage
extern const uint8_t charset8x16[256][16][8]; // used by func__saveimage

/// @brief Pixel scaler algorithms
enum class ImageScaler { NONE = 0, SXBR2, MMPX2, HQ2XA, HQ2XB, HQ3XA, HQ3XB };
enum class ImageScaler { NONE = 0, SXBR2, SXBR3, SXBR4, MMPX2, HQ2XA, HQ2XB, HQ3XA, HQ3XB };
/// @brief This is the scaling factors for ImageScaler enum
static const int g_ImageScaleFactor[] = {1, 2, 2, 2, 2, 3, 3};
static const int g_ImageScaleFactor[] = {1, 2, 3, 4, 2, 2, 2, 3, 3};
/// @brief Pixel scaler names for ImageScaler enum
static const char *g_ImageScalerName[] = {"NONE", "SXBR2", "MMPX2", "HQ2XA", "HQ2XB", "HQ3XA", "HQ3XB"};
static const char *g_ImageScalerName[] = {"NONE", "SXBR2", "SXBR3", "SXBR4", "MMPX2", "HQ2XA", "HQ2XB", "HQ3XA", "HQ3XB"};

/// @brief Runs a pixel scaler algorithm on raw image pixels. It will free 'data' if scaling occurs!
/// @param data In + Out: The source raw image data in RGBA format
Expand All @@ -60,8 +60,8 @@ static const char *g_ImageScalerName[] = {"NONE", "SXBR2", "MMPX2", "HQ2XA", "HQ
/// @return A pointer to the scaled image or 'data' if there is no change
static uint32_t *image_scale(uint32_t *data, int32_t *xOut, int32_t *yOut, ImageScaler scaler) {
if (scaler > ImageScaler::NONE) {
auto newX = *xOut * g_ImageScaleFactor[(int)(scaler)];
auto newY = *yOut * g_ImageScaleFactor[(int)(scaler)];
auto newX = *xOut * g_ImageScaleFactor[size_t(scaler)];
auto newY = *yOut * g_ImageScaleFactor[size_t(scaler)];

auto pixels = (uint32_t *)malloc(sizeof(uint32_t) * newX * newY);
if (pixels) {
Expand All @@ -72,6 +72,14 @@ static uint32_t *image_scale(uint32_t *data, int32_t *xOut, int32_t *yOut, Image
scaleSuperXBR2(data, *xOut, *yOut, pixels);
break;

case ImageScaler::SXBR3:
scaleSuperXBR3(data, *xOut, *yOut, pixels);
break;

case ImageScaler::SXBR4:
scaleSuperXBR4(data, *xOut, *yOut, pixels);
break;

case ImageScaler::MMPX2:
mmpx_scale2x(data, pixels, *xOut, *yOut);
break;
Expand Down Expand Up @@ -123,8 +131,8 @@ static uint32_t *image_svg_load(NSVGimage *image, int32_t *xOut, int32_t *yOut,
return nullptr;
}

auto w = (int32_t)image->width * g_ImageScaleFactor[(int)(scaler)];
auto h = (int32_t)image->height * g_ImageScaleFactor[(int)(scaler)];
auto w = (int32_t)image->width * g_ImageScaleFactor[size_t(scaler)];
auto h = (int32_t)image->height * g_ImageScaleFactor[size_t(scaler)];

auto pixels = (uint32_t *)malloc(sizeof(uint32_t) * w * h);
if (!pixels) {
Expand All @@ -133,7 +141,7 @@ static uint32_t *image_svg_load(NSVGimage *image, int32_t *xOut, int32_t *yOut,
return nullptr;
}

nsvgRasterize(rast, image, 0, 0, g_ImageScaleFactor[(int)(scaler)], reinterpret_cast<unsigned char *>(pixels), w, h, sizeof(uint32_t) * w);
nsvgRasterize(rast, image, 0, 0, g_ImageScaleFactor[size_t(scaler)], reinterpret_cast<unsigned char *>(pixels), w, h, sizeof(uint32_t) * w);
nsvgDeleteRasterizer(rast);
nsvgDelete(image);

Expand Down Expand Up @@ -179,7 +187,7 @@ static uint32_t *image_svg_load_from_file(const char *fileName, int32_t *xOut, i
return nullptr;
}

if (fread(svgString, 1, size, fp) != size) {
if (long(fread(svgString, sizeof(uint8_t), size, fp)) != size) {
free(svgString);
fclose(fp);
return nullptr;
Expand Down Expand Up @@ -451,7 +459,7 @@ static uint8_t *image_extract_8bpp(const uint32_t *src, int32_t w, int32_t h, ui

auto uniqueColors = 0; // as long as this is < 256 we will keep going until we are done
size_t size = w * h;
for (auto i = 0; i < size; i++) {
for (size_t i = 0; i < size; i++) {
auto srcColor = src[i]; // get the 32bpp pixel

// Check if the src color exists in our palette
Expand Down Expand Up @@ -579,11 +587,11 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int
}

// Parse scaler string
for (auto i = 0; i < _countof(g_ImageScalerName); i++) {
for (size_t i = 0; i < _countof(g_ImageScalerName); i++) {
image_log_info("Checking for: %s", g_ImageScalerName[i]);
if (requirements.find(g_ImageScalerName[i]) != std::string::npos) {
scaler = (ImageScaler)i;
image_log_info("%s scaler selected", g_ImageScalerName[(int)scaler]);
image_log_info("%s scaler selected", g_ImageScalerName[size_t(scaler)]);
break;
}
}
Expand All @@ -604,7 +612,7 @@ int32_t func__loadimage(qbs *qbsFileName, int32_t bpp, qbs *qbsRequirements, int

// Convert RGBA to BGRA
size_t size = x * y;
for (auto i = 0; i < size; i++)
for (size_t i = 0; i < size; i++)
pixels[i] = image_swap_red_blue(pixels[i]);

int32_t i; // Image handle to be returned
Expand Down Expand Up @@ -748,17 +756,17 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,

image_log_info("Parsing requirements string: %s", requirements.c_str());

for (auto i = 0; i < _countof(formatName); i++) {
for (size_t i = 0; i < _countof(formatName); i++) {
image_log_info("Checking for: %s", formatName[i]);
if (requirements.find(formatName[i]) != std::string::npos) {
format = (SaveFormat)i;
image_log_info("Found: %s", formatName[(int)format]);
image_log_info("Found: %s", formatName[size_t(format)]);
break;
}
}
}

image_log_info("Format selected: %s", formatName[(int)format]);
image_log_info("Format selected: %s", formatName[size_t(format)]);

std::string fileName(reinterpret_cast<char *>(qbsFileName->chr), qbsFileName->len);
filepath_fix_directory(fileName);
Expand All @@ -770,7 +778,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,

image_log_info("File extension: %s", fileExtension.c_str());

int i;
size_t i;
for (i = 0; i < _countof(formatName); i++) {
std::string formatExtension;

Expand All @@ -782,23 +790,23 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
if (fileExtension == formatExtension) {
image_log_info("Extension (%s) matches with format %i", formatExtension.c_str(), i);
format = (SaveFormat)i;
image_log_info("Format selected by extension: %s", formatName[(int)format]);
image_log_info("Format selected by extension: %s", formatName[size_t(format)]);
break;
}
}

if (i >= _countof(formatName)) { // no matches
image_log_info("No matching extension. Adding .%s", formatName[(int)format]);
image_log_info("No matching extension. Adding .%s", formatName[size_t(format)]);

fileName.append(".");
fileName.append(formatName[(int)format]);
fileName.append(formatName[size_t(format)]);
}
} else {
// Simply add the selected format's extension
image_log_info("Adding extension: .%s", formatName[(int)format]);
image_log_info("Adding extension: .%s", formatName[size_t(format)]);

fileName.append(".");
fileName.append(formatName[(int)format]);
fileName.append(formatName[size_t(format)]);
}

// This will hold our raw RGBA pixel data
Expand Down Expand Up @@ -878,7 +886,7 @@ void sub__saveimage(qbs *qbsFileName, int32_t imageHandle, qbs *qbsRequirements,
}
}

image_log_info("Saving to: %s (%i x %i), %llu pixels, %s", fileName.c_str(), width, height, pixels.size(), formatName[(int)format]);
image_log_info("Saving to: %s (%i x %i), %llu pixels, %s", fileName.c_str(), width, height, pixels.size(), formatName[size_t(format)]);

switch (format) {
case SaveFormat::PNG: {
Expand Down
2 changes: 1 addition & 1 deletion internal/c/parts/video/image/nanosvg/nanosvg.h
Original file line number Diff line number Diff line change
Expand Up @@ -1265,7 +1265,7 @@ static unsigned int nsvg__parseColorRGB(const char* str)
while (*str && nsvg__isdigit(*str)) str++; // skip fractional part
}
if (*str == '%') str++; else break;
while (nsvg__isspace(*str)) str++;
while (*str && nsvg__isspace(*str)) str++;
if (*str == delimiter[i]) str++;
else break;
}
Expand Down
18 changes: 8 additions & 10 deletions internal/c/parts/video/image/pixelscalers/hqx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,15 @@ static const uint32_t HQX_VMASK = 0x000000FF;
// All hq-related fcts used to be member fcts of otherwise empty classes.
// Turned into stand-alone fcts.

static inline uint32_t ARGBtoAYUV(uint32_t value) {
uint32_t A, R, G, B, Y, U, V;
static inline constexpr uint32_t ARGBtoAYUV(uint32_t value) {
uint32_t A = value >> 24;
uint32_t R = (value >> 16) & 0xFF;
uint32_t G = (value >> 8) & 0xFF;
uint32_t B = value & 0xFF;

A = value >> 24;
R = (value >> 16) & 0xFF;
G = (value >> 8) & 0xFF;
B = value & 0xFF;

Y = (uint32_t)(0.299 * R + 0.587 * G + 0.114 * B);
U = (uint32_t)(-0.169 * R - 0.331 * G + 0.5 * B) + 128;
V = (uint32_t)(0.5 * R - 0.419 * G - 0.081 * B) + 128;
uint32_t Y = (uint32_t)(0.299 * R + 0.587 * G + 0.114 * B);
uint32_t U = (uint32_t)(-0.169 * R - 0.331 * G + 0.5 * B) + 128;
uint32_t V = (uint32_t)(0.5 * R - 0.419 * G - 0.081 * B) + 128;
return (A << 24) + (Y << 16) + (U << 8) + V;
}

Expand Down
23 changes: 10 additions & 13 deletions internal/c/parts/video/image/pixelscalers/mmpx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,39 @@
https://casual-effects.com/research/McGuire2021PixelArt/index.html
*/

#include <algorithm>
#include <cstdbool>
#include <cstdint>

static inline uint32_t luma(uint32_t color) {
static inline constexpr uint32_t luma(uint32_t color) {
const uint32_t alpha = (color & 0xFF000000) >> 24;
return (((color & 0x00FF0000) >> 16) + ((color & 0x0000FF00) >> 8) + (color & 0x000000FF) + 1) * (256 - alpha);
}

static inline bool all_eq2(uint32_t B, uint32_t A0, uint32_t A1) {
static inline constexpr bool all_eq2(uint32_t B, uint32_t A0, uint32_t A1) {
return ((B ^ A0) | (B ^ A1)) == 0;
}

static inline bool all_eq3(uint32_t B, uint32_t A0, uint32_t A1, uint32_t A2) {
static inline constexpr bool all_eq3(uint32_t B, uint32_t A0, uint32_t A1, uint32_t A2) {
return ((B ^ A0) | (B ^ A1) | (B ^ A2)) == 0;
}

static inline bool all_eq4(uint32_t B, uint32_t A0, uint32_t A1, uint32_t A2, uint32_t A3) {
static inline constexpr bool all_eq4(uint32_t B, uint32_t A0, uint32_t A1, uint32_t A2, uint32_t A3) {
return ((B ^ A0) | (B ^ A1) | (B ^ A2) | (B ^ A3)) == 0;
}

static inline bool any_eq3(uint32_t B, uint32_t A0, uint32_t A1, uint32_t A2) {
static inline constexpr bool any_eq3(uint32_t B, uint32_t A0, uint32_t A1, uint32_t A2) {
return B == A0 || B == A1 || B == A2;
}

static inline bool none_eq2(uint32_t B, uint32_t A0, uint32_t A1) {
static inline constexpr bool none_eq2(uint32_t B, uint32_t A0, uint32_t A1) {
return (B != A0) && (B != A1);
}

static inline bool none_eq4(uint32_t B, uint32_t A0, uint32_t A1, uint32_t A2, uint32_t A3) {
static inline constexpr bool none_eq4(uint32_t B, uint32_t A0, uint32_t A1, uint32_t A2, uint32_t A3) {
return B != A0 && B != A1 && B != A2 && B != A3;
}

static inline int mmpx_clamp(int v, int min, int max) {
return v < min ? min : min > max ? max : v;
}

struct Meta {
const uint32_t *srcBuffer;
uint32_t srcWidth;
Expand All @@ -51,8 +48,8 @@ struct Meta {
static inline uint32_t src(const struct Meta *meta, int x, int y) {
// Clamp to border
if ((uint32_t)x > (uint32_t)meta->srcMaxX || (uint32_t)y > (uint32_t)meta->srcMaxY) {
x = mmpx_clamp(x, 0, meta->srcMaxX);
y = mmpx_clamp(y, 0, meta->srcMaxY);
x = std::clamp<int>(x, 0, meta->srcMaxX);
y = std::clamp<int>(y, 0, meta->srcMaxY);
}

return meta->srcBuffer[y * meta->srcWidth + x];
Expand Down
2 changes: 2 additions & 0 deletions internal/c/parts/video/image/pixelscalers/pixelscalers.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ void hq3xA(uint32_t *img, int w, int h, uint32_t *out);
void hq3xB(uint32_t *img, int w, int h, uint32_t *out);
void mmpx_scale2x(const uint32_t *srcBuffer, uint32_t *dst, uint32_t srcWidth, uint32_t srcHeight);
void scaleSuperXBR2(uint32_t *data, int w, int h, uint32_t *out);
void scaleSuperXBR3(uint32_t *data, int w, int h, uint32_t *out);
void scaleSuperXBR4(uint32_t *data, int w, int h, uint32_t *out);
Loading
Loading