File indexing completed on 2024-10-06 10:23:48
0001 // SPDX-FileCopyrightText: 2018 Wolt Enterprises 0002 // SPDX-License-Identifier: MIT 0003 0004 #include "blurhash.h" 0005 0006 #include <math.h> 0007 #include <stdbool.h> 0008 #include <stdlib.h> 0009 #include <string.h> 0010 #include <vector> 0011 0012 namespace 0013 { 0014 const char chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"; 0015 0016 struct Color { 0017 float r = 0; 0018 float g = 0; 0019 float b = 0; 0020 }; 0021 0022 inline int linearTosRGB(float value) 0023 { 0024 float v = fmaxf(0, fminf(1, value)); 0025 if (v <= 0.0031308) 0026 return v * 12.92 * 255 + 0.5; 0027 else 0028 return (1.055 * powf(v, 1 / 2.4) - 0.055) * 255 + 0.5; 0029 } 0030 0031 inline float sRGBToLinear(int value) 0032 { 0033 float v = (float)value / 255; 0034 if (v <= 0.04045) 0035 return v / 12.92; 0036 else 0037 return powf((v + 0.055) / 1.055, 2.4); 0038 } 0039 0040 inline float signPow(float value, float exp) 0041 { 0042 return copysignf(powf(fabsf(value), exp), value); 0043 } 0044 0045 inline uint8_t clampToUByte(int *src) 0046 { 0047 if (*src >= 0 && *src <= 255) { 0048 return *src; 0049 } 0050 return (*src < 0) ? 0 : 255; 0051 } 0052 0053 inline uint8_t *createByteArray(int size) 0054 { 0055 return (uint8_t *)malloc(size * sizeof(uint8_t)); 0056 } 0057 0058 int decodeToInt(const char *string, int start, int end) 0059 { 0060 int value = 0; 0061 for (int iter1 = start; iter1 < end; iter1++) { 0062 int index = -1; 0063 for (int iter2 = 0; iter2 < 83; iter2++) { 0064 if (chars[iter2] == string[iter1]) { 0065 index = iter2; 0066 break; 0067 } 0068 } 0069 if (index == -1) { 0070 return -1; 0071 } 0072 value = value * 83 + index; 0073 } 0074 return value; 0075 } 0076 0077 void decodeDC(int value, Color *color) 0078 { 0079 color->r = sRGBToLinear(value >> 16); 0080 color->g = sRGBToLinear((value >> 8) & 255); 0081 color->b = sRGBToLinear(value & 255); 0082 } 0083 0084 void decodeAC(int value, float maximumValue, Color *color) 0085 { 0086 int quantR = (int)floorf(value / (19 * 19)); 0087 int quantG = (int)floorf(value / 19) % 19; 0088 int quantB = (int)value % 19; 0089 0090 color->r = signPow(((float)quantR - 9) / 9, 2.0) * maximumValue; 0091 color->g = signPow(((float)quantG - 9) / 9, 2.0) * maximumValue; 0092 color->b = signPow(((float)quantB - 9) / 9, 2.0) * maximumValue; 0093 } 0094 0095 int decodeToArray(const char *blurhash, int width, int height, int punch, int nChannels, uint8_t *pixelArray) 0096 { 0097 if (!isValidBlurhash(blurhash)) { 0098 return -1; 0099 } 0100 if (punch < 1) { 0101 punch = 1; 0102 } 0103 0104 int sizeFlag = decodeToInt(blurhash, 0, 1); 0105 int numY = (int)floorf(sizeFlag / 9) + 1; 0106 int numX = (sizeFlag % 9) + 1; 0107 int iter = 0; 0108 0109 Color color; 0110 int quantizedMaxValue = decodeToInt(blurhash, 1, 2); 0111 if (quantizedMaxValue == -1) { 0112 return -1; 0113 } 0114 0115 const float maxValue = ((float)(quantizedMaxValue + 1)) / 166; 0116 0117 const int colors_size = numX * numY; 0118 0119 std::vector<Color> colors(colors_size, {0, 0, 0}); 0120 0121 for (iter = 0; iter < colors_size; iter++) { 0122 if (iter == 0) { 0123 int value = decodeToInt(blurhash, 2, 6); 0124 if (value == -1) { 0125 return -1; 0126 } 0127 decodeDC(value, &color); 0128 colors[iter] = color; 0129 } else { 0130 int value = decodeToInt(blurhash, 4 + iter * 2, 6 + iter * 2); 0131 if (value == -1) { 0132 return -1; 0133 } 0134 decodeAC(value, maxValue * punch, &color); 0135 colors[iter] = color; 0136 } 0137 } 0138 0139 int bytesPerRow = width * nChannels; 0140 int x = 0, y = 0, i = 0, j = 0; 0141 int intR = 0, intG = 0, intB = 0; 0142 0143 for (y = 0; y < height; y++) { 0144 for (x = 0; x < width; x++) { 0145 float r = 0, g = 0, b = 0; 0146 0147 for (j = 0; j < numY; j++) { 0148 for (i = 0; i < numX; i++) { 0149 float basics = cos((M_PI * x * i) / width) * cos((M_PI * y * j) / height); 0150 int idx = i + j * numX; 0151 r += colors[idx].r * basics; 0152 g += colors[idx].g * basics; 0153 b += colors[idx].b * basics; 0154 } 0155 } 0156 0157 intR = linearTosRGB(r); 0158 intG = linearTosRGB(g); 0159 intB = linearTosRGB(b); 0160 0161 pixelArray[nChannels * x + 0 + y * bytesPerRow] = clampToUByte(&intR); 0162 pixelArray[nChannels * x + 1 + y * bytesPerRow] = clampToUByte(&intG); 0163 pixelArray[nChannels * x + 2 + y * bytesPerRow] = clampToUByte(&intB); 0164 0165 if (nChannels == 4) { 0166 pixelArray[nChannels * x + 3 + y * bytesPerRow] = 255; 0167 } 0168 } 0169 } 0170 return 0; 0171 } 0172 } 0173 0174 uint8_t *decode(const char *blurhash, int width, int height, int punch, int nChannels) 0175 { 0176 int bytesPerRow = width * nChannels; 0177 uint8_t *pixelArray = createByteArray(bytesPerRow * height); 0178 0179 if (decodeToArray(blurhash, width, height, punch, nChannels, pixelArray) == -1) { 0180 return NULL; 0181 } 0182 return pixelArray; 0183 } 0184 0185 bool isValidBlurhash(const char *blurhash) 0186 { 0187 const int hashLength = strlen(blurhash); 0188 0189 if (!blurhash || strlen(blurhash) < 6) { 0190 return false; 0191 } 0192 0193 int sizeFlag = decodeToInt(blurhash, 0, 1); 0194 int numY = (int)floorf(sizeFlag / 9) + 1; 0195 int numX = (sizeFlag % 9) + 1; 0196 0197 return hashLength == 4 + 2 * numX * numY; 0198 }