blurhash-c-wasm/blurhash/decode.c
Fries f7bbb92d85 change up api, and bump down version
the api now requires you to call an async function initWasm to make the
functions work instead of using top level await. i also optimized the
wasm module to have O3 and fast-math (which the original blurhash
makefile had and the results do seem to be the same with the javascript
blurhash).

I also gave the wasm instance functions inside typescript types and i
fixed a bug where i didnt free the pointers in order so if you called
decode too many times, you will run out of memory.
2023-08-14 19:24:20 -07:00

145 lines
3.6 KiB
C

#include "decode.h"
#include "common.h"
#include <stdint.h>
static char chars[83] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~";
static inline uint8_t clampToUByte(int* src)
{
if (*src >= 0 && *src <= 255)
return *src;
return (*src < 0) ? 0 : 255;
}
int decodeToInt(uint8_t* string, int start, int end)
{
int value = 0, iter1 = 0, iter2 = 0;
for (iter1 = start; iter1 < end; iter1++) {
int index = -1;
for (iter2 = 0; iter2 < 83; iter2++) {
if (chars[iter2] == string[iter1]) {
index = iter2;
break;
}
}
if (index == -1)
return -1;
value = value * 83 + index;
}
return value;
}
bool isValidBlurhash(uint8_t* blurhash)
{
const int hashLength = strlen((const char*)blurhash);
if (!blurhash || strlen((const char*)blurhash) < 6)
return false;
// Get size from first character
int sizeFlag = decodeToInt(blurhash, 0, 1);
int numY = (int)floorf(sizeFlag / 9) + 1;
int numX = (sizeFlag % 9) + 1;
if (hashLength != 4 + 2 * numX * numY)
return false;
return true;
}
void decodeDC(int value, float* r, float* g, float* b)
{
*r = sRGBToLinear(value >> 16); // R-component
*g = sRGBToLinear((value >> 8) & 255); // G-Component
*b = sRGBToLinear(value & 255); // B-Component
}
void decodeAC(int value, float maximumValue, float* r, float* g, float* b)
{
int quantR = (int)floorf(value / (19 * 19));
int quantG = (int)floorf(value / 19) % 19;
int quantB = (int)value % 19;
*r = signPow(((float)quantR - 9) / 9, 2.0) * maximumValue;
*g = signPow(((float)quantG - 9) / 9, 2.0) * maximumValue;
*b = signPow(((float)quantB - 9) / 9, 2.0) * maximumValue;
}
int decodeToArray(uint8_t* blurhash, int width, int height, int punch, int nChannels, uint8_t* pixelArray)
{
if (!isValidBlurhash(blurhash))
return -1;
if (punch < 1)
punch = 1;
int sizeFlag = decodeToInt(blurhash, 0, 1);
int numY = (int)floorf(sizeFlag / 9) + 1;
int numX = (sizeFlag % 9) + 1;
int iter = 0;
float r = 0, g = 0, b = 0;
int quantizedMaxValue = decodeToInt(blurhash, 1, 2);
if (quantizedMaxValue == -1)
return -1;
float maxValue = ((float)(quantizedMaxValue + 1)) / 166;
int colors_size = numX * numY;
float colors[colors_size][3];
for (iter = 0; iter < colors_size; iter++) {
if (iter == 0) {
int value = decodeToInt(blurhash, 2, 6);
if (value == -1)
return -1;
decodeDC(value, &r, &g, &b);
colors[iter][0] = r;
colors[iter][1] = g;
colors[iter][2] = b;
} else {
int value = decodeToInt(blurhash, 4 + iter * 2, 6 + iter * 2);
if (value == -1)
return -1;
decodeAC(value, maxValue * punch, &r, &g, &b);
colors[iter][0] = r;
colors[iter][1] = g;
colors[iter][2] = b;
}
}
int bytesPerRow = width * nChannels;
int x = 0, y = 0, i = 0, j = 0;
int intR = 0, intG = 0, intB = 0;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
float r = 0, g = 0, b = 0;
for (j = 0; j < numY; j++) {
for (i = 0; i < numX; i++) {
float basics = cos((M_PI * x * i) / width) * cos((M_PI * y * j) / height);
int idx = i + j * numX;
r += colors[idx][0] * basics;
g += colors[idx][1] * basics;
b += colors[idx][2] * basics;
}
}
intR = linearTosRGB(r);
intG = linearTosRGB(g);
intB = linearTosRGB(b);
pixelArray[nChannels * x + 0 + y * bytesPerRow] = clampToUByte(&intR);
pixelArray[nChannels * x + 1 + y * bytesPerRow] = clampToUByte(&intG);
pixelArray[nChannels * x + 2 + y * bytesPerRow] = clampToUByte(&intB);
if (nChannels == 4)
// If nChannels=4, treat each pixel as RGBA instead of RGB
pixelArray[nChannels * x + 3 + y * bytesPerRow] = 255;
}
}
return 0;
}