File indexing completed on 2024-05-12 05:04:17

0001 // SPDX-FileCopyrightText: 2022 DeepBlueV7.X <https://github.com/deepbluev7>
0002 // SPDX-License-Identifier: BSL-1.0
0003 
0004 #include "blurhash.hpp"
0005 
0006 #include <algorithm>
0007 #include <array>
0008 #include <cassert>
0009 #include <cmath>
0010 #include <stdexcept>
0011 
0012 #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
0013 #if __has_include(<doctest.h>)
0014 #include <doctest.h>
0015 #else
0016 #include <doctest/doctest.h>
0017 #endif
0018 #endif
0019 
0020 using namespace std::literals;
0021 
0022 namespace {
0023 constexpr std::array<char, 84> int_to_b83{
0024   "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"};
0025 
0026 std::string
0027 leftPad(std::string str, size_t len)
0028 {
0029         if (str.size() >= len)
0030                 return str;
0031         return str.insert(0, len - str.size(), '0');
0032 }
0033 
0034 constexpr std::array<int, 255> b83_to_int = []() constexpr
0035 {
0036         std::array<int, 255> a{};
0037 
0038         for (auto &e : a)
0039                 e = -1;
0040 
0041         for (int i = 0; i < 83; i++) {
0042                 a[static_cast<unsigned char>(int_to_b83[i])] = i;
0043         }
0044 
0045         return a;
0046 }
0047 ();
0048 
0049 std::string
0050 encode83(int value)
0051 {
0052         std::string buffer;
0053 
0054         do {
0055                 buffer += int_to_b83[value % 83];
0056         } while ((value = value / 83));
0057 
0058         std::reverse(buffer.begin(), buffer.end());
0059         return buffer;
0060 }
0061 
0062 struct Components
0063 {
0064         int x, y;
0065 };
0066 
0067 int
0068 packComponents(const Components &c) noexcept
0069 {
0070         return (c.x - 1) + (c.y - 1) * 9;
0071 }
0072 
0073 Components
0074 unpackComponents(int c) noexcept
0075 {
0076         return {c % 9 + 1, c / 9 + 1};
0077 }
0078 
0079 int
0080 decode83(std::string_view value)
0081 {
0082         int temp = 0;
0083 
0084         for (char c : value)
0085                 if (b83_to_int[static_cast<unsigned char>(c)] < 0)
0086                         throw std::invalid_argument("invalid character in blurhash");
0087 
0088         for (char c : value)
0089                 temp = temp * 83 + b83_to_int[static_cast<unsigned char>(c)];
0090         return temp;
0091 }
0092 
0093 float
0094 decodeMaxAC(int quantizedMaxAC) noexcept
0095 {
0096         return static_cast<float>(quantizedMaxAC + 1) / 166.f;
0097 }
0098 
0099 float
0100 decodeMaxAC(std::string_view maxAC)
0101 {
0102         assert(maxAC.size() == 1);
0103         return decodeMaxAC(decode83(maxAC));
0104 }
0105 
0106 int
0107 encodeMaxAC(float maxAC) noexcept
0108 {
0109         return std::max(0, std::min(82, int(maxAC * 166 - 0.5f)));
0110 }
0111 
0112 float
0113 srgbToLinear(int value) noexcept
0114 {
0115         auto srgbToLinearF = [](float x) {
0116                 if (x <= 0.0f)
0117                         return 0.0f;
0118                 else if (x >= 1.0f)
0119                         return 1.0f;
0120                 else if (x < 0.04045f)
0121                         return x / 12.92f;
0122                 else
0123                         return std::pow((x + 0.055f) / 1.055f, 2.4f);
0124         };
0125 
0126         return srgbToLinearF(static_cast<float>(value) / 255.f);
0127 }
0128 
0129 int
0130 linearToSrgb(float value) noexcept
0131 {
0132         auto linearToSrgbF = [](float x) -> float {
0133                 if (x <= 0.0f)
0134                         return 0.0f;
0135                 else if (x >= 1.0f)
0136                         return 1.0f;
0137                 else if (x < 0.0031308f)
0138                         return x * 12.92f;
0139                 else
0140                         return std::pow(x, 1.0f / 2.4f) * 1.055f - 0.055f;
0141         };
0142 
0143         return int(linearToSrgbF(value) * 255.f + 0.5f);
0144 }
0145 
0146 struct Color
0147 {
0148         float r, g, b;
0149 
0150         Color &operator*=(float scale)
0151         {
0152                 r *= scale;
0153                 g *= scale;
0154                 b *= scale;
0155                 return *this;
0156         }
0157         friend Color operator*(Color lhs, float rhs) { return (lhs *= rhs); }
0158         Color &operator/=(float scale)
0159         {
0160                 r /= scale;
0161                 g /= scale;
0162                 b /= scale;
0163                 return *this;
0164         }
0165         Color &operator+=(const Color &rhs)
0166         {
0167                 r += rhs.r;
0168                 g += rhs.g;
0169                 b += rhs.b;
0170                 return *this;
0171         }
0172 };
0173 
0174 Color
0175 decodeDC(int value)
0176 {
0177         const int intR = value >> 16;
0178         const int intG = (value >> 8) & 255;
0179         const int intB = value & 255;
0180         return {srgbToLinear(intR), srgbToLinear(intG), srgbToLinear(intB)};
0181 }
0182 
0183 Color
0184 decodeDC(std::string_view value)
0185 {
0186         assert(value.size() == 4);
0187         return decodeDC(decode83(value));
0188 }
0189 
0190 int
0191 encodeDC(const Color &c)
0192 {
0193         return (linearToSrgb(c.r) << 16) + (linearToSrgb(c.g) << 8) + linearToSrgb(c.b);
0194 }
0195 
0196 float
0197 signPow(float value, float exp)
0198 {
0199         return std::copysign(std::pow(std::abs(value), exp), value);
0200 }
0201 
0202 int
0203 encodeAC(const Color &c, float maximumValue)
0204 {
0205         auto quantR =
0206           int(std::max(0., std::min(18., std::floor(signPow(c.r / maximumValue, 0.5) * 9 + 9.5))));
0207         auto quantG =
0208           int(std::max(0., std::min(18., std::floor(signPow(c.g / maximumValue, 0.5) * 9 + 9.5))));
0209         auto quantB =
0210           int(std::max(0., std::min(18., std::floor(signPow(c.b / maximumValue, 0.5) * 9 + 9.5))));
0211 
0212         return quantR * 19 * 19 + quantG * 19 + quantB;
0213 }
0214 
0215 Color
0216 decodeAC(int value, float maximumValue)
0217 {
0218         auto quantR = value / (19 * 19);
0219         auto quantG = (value / 19) % 19;
0220         auto quantB = value % 19;
0221 
0222         return {signPow((float(quantR) - 9) / 9, 2) * maximumValue,
0223                 signPow((float(quantG) - 9) / 9, 2) * maximumValue,
0224                 signPow((float(quantB) - 9) / 9, 2) * maximumValue};
0225 }
0226 
0227 Color
0228 decodeAC(std::string_view value, float maximumValue)
0229 {
0230         return decodeAC(decode83(value), maximumValue);
0231 }
0232 
0233 std::vector<float>
0234 bases_for(size_t dimension, size_t components)
0235 {
0236         std::vector<float> bases(dimension * components, 0.f);
0237         auto scale = M_PI / float(dimension);
0238         for (size_t x = 0; x < dimension; x++) {
0239                 for (size_t nx = 0; nx < size_t(components); nx++) {
0240                         bases[x * components + nx] = std::cos(scale * float(nx * x));
0241                 }
0242         }
0243         return bases;
0244 }
0245 }
0246 
0247 namespace blurhash {
0248 Image
0249 decode(std::string_view blurhash, size_t width, size_t height, size_t bytesPerPixel) noexcept
0250 {
0251         Image i{};
0252 
0253         if (blurhash.size() < 10)
0254                 return i;
0255 
0256         Components components{};
0257         std::vector<Color> values;
0258         values.reserve(blurhash.size() / 2);
0259         try {
0260                 components = unpackComponents(decode83(blurhash.substr(0, 1)));
0261 
0262                 if (components.x < 1 || components.y < 1 ||
0263                     blurhash.size() != size_t(1 + 1 + 4 + (components.x * components.y - 1) * 2))
0264                         return {};
0265 
0266                 auto maxAC    = decodeMaxAC(blurhash.substr(1, 1));
0267                 Color average = decodeDC(blurhash.substr(2, 4));
0268 
0269                 values.push_back(average);
0270                 for (size_t c = 6; c < blurhash.size(); c += 2)
0271                         values.push_back(decodeAC(blurhash.substr(c, 2), maxAC));
0272         } catch (std::invalid_argument &) {
0273                 return {};
0274         }
0275 
0276         i.image = decltype(i.image)(height * width * bytesPerPixel, 255);
0277 
0278         std::vector<float> basis_x = bases_for(width, components.x);
0279         std::vector<float> basis_y = bases_for(height, components.y);
0280 
0281         for (size_t y = 0; y < height; y++) {
0282                 for (size_t x = 0; x < width; x++) {
0283                         Color c{};
0284 
0285                         for (size_t nx = 0; nx < size_t(components.x); nx++) {
0286                                 for (size_t ny = 0; ny < size_t(components.y); ny++) {
0287                                         float basis = basis_x[x * components.x + nx] *
0288                                                       basis_y[y * components.y + ny];
0289                                         c += values[nx + ny * components.x] * basis;
0290                                 }
0291                         }
0292 
0293                         i.image[(y * width + x) * bytesPerPixel + 0] =
0294                           static_cast<unsigned char>(linearToSrgb(c.r));
0295                         i.image[(y * width + x) * bytesPerPixel + 1] =
0296                           static_cast<unsigned char>(linearToSrgb(c.g));
0297                         i.image[(y * width + x) * bytesPerPixel + 2] =
0298                           static_cast<unsigned char>(linearToSrgb(c.b));
0299                 }
0300         }
0301 
0302         i.height = height;
0303         i.width  = width;
0304 
0305         return i;
0306 }
0307 
0308 std::string
0309 encode(unsigned char *image, size_t width, size_t height, int components_x, int components_y)
0310 {
0311         if (width < 1 || height < 1 || components_x < 1 || components_x > 9 || components_y < 1 ||
0312             components_y > 9 || !image)
0313                 return "";
0314 
0315         std::vector<float> basis_x = bases_for(width, components_x);
0316         std::vector<float> basis_y = bases_for(height, components_y);
0317 
0318         std::vector<Color> factors(components_x * components_y, Color{});
0319         for (size_t y = 0; y < height; y++) {
0320                 for (size_t x = 0; x < width; x++) {
0321                         Color linear{srgbToLinear(image[3 * x + 0 + y * width * 3]),
0322                                      srgbToLinear(image[3 * x + 1 + y * width * 3]),
0323                                      srgbToLinear(image[3 * x + 2 + y * width * 3])};
0324 
0325                         // other half of normalization.
0326                         linear *= 1.f / static_cast<float>(width);
0327 
0328                         for (size_t ny = 0; ny < size_t(components_y); ny++) {
0329                                 for (size_t nx = 0; nx < size_t(components_x); nx++) {
0330                                         float basis = basis_x[x * size_t(components_x) + nx] *
0331                                                       basis_y[y * size_t(components_y) + ny];
0332                                         factors[ny * components_x + nx] += linear * basis;
0333                                 }
0334                         }
0335                 }
0336         }
0337 
0338         // scale by normalization. Half the scaling is done in the previous loop to prevent going
0339         // too far outside the float range.
0340         for (size_t i = 0; i < factors.size(); i++) {
0341                 float normalisation = (i == 0) ? 1 : 2;
0342                 float scale         = normalisation / static_cast<float>(height);
0343                 factors[i] *= scale;
0344         }
0345 
0346         assert(factors.size() > 0);
0347 
0348         auto dc = factors.front();
0349         factors.erase(factors.begin());
0350 
0351         std::string h;
0352 
0353         h += leftPad(encode83(packComponents({components_x, components_y})), 1);
0354 
0355         float maximumValue;
0356         if (!factors.empty()) {
0357                 float actualMaximumValue = 0;
0358                 for (auto ac : factors) {
0359                         actualMaximumValue = std::max({
0360                           std::abs(ac.r),
0361                           std::abs(ac.g),
0362                           std::abs(ac.b),
0363                           actualMaximumValue,
0364                         });
0365                 }
0366 
0367                 int quantisedMaximumValue = encodeMaxAC(actualMaximumValue);
0368                 maximumValue              = ((float)quantisedMaximumValue + 1) / 166;
0369                 h += leftPad(encode83(quantisedMaximumValue), 1);
0370         } else {
0371                 maximumValue = 1;
0372                 h += leftPad(encode83(0), 1);
0373         }
0374 
0375         h += leftPad(encode83(encodeDC(dc)), 4);
0376 
0377         for (auto ac : factors)
0378                 h += leftPad(encode83(encodeAC(ac, maximumValue)), 2);
0379 
0380         return h;
0381 }
0382 }
0383 
0384 #ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
0385 TEST_CASE("component packing")
0386 {
0387         for (int i = 0; i < 9 * 9; i++)
0388                 CHECK(packComponents(unpackComponents(i)) == i);
0389 }
0390 
0391 TEST_CASE("encode83")
0392 {
0393         CHECK(encode83(0) == "0");
0394 
0395         CHECK(encode83(packComponents({4, 3})) == "L");
0396         CHECK(encode83(packComponents({4, 4})) == "U");
0397         CHECK(encode83(packComponents({8, 4})) == "Y");
0398         CHECK(encode83(packComponents({2, 1})) == "1");
0399 }
0400 
0401 TEST_CASE("decode83")
0402 {
0403         CHECK(packComponents({4, 3}) == decode83("L"));
0404         CHECK(packComponents({4, 4}) == decode83("U"));
0405         CHECK(packComponents({8, 4}) == decode83("Y"));
0406         CHECK(packComponents({2, 1}) == decode83("1"));
0407 }
0408 
0409 TEST_CASE("maxAC")
0410 {
0411         for (int i = 0; i < 83; i++)
0412                 CHECK(encodeMaxAC(decodeMaxAC(i)) == i);
0413 
0414         CHECK(std::abs(decodeMaxAC("l"sv) - 0.289157f) < 0.00001f);
0415 }
0416 
0417 TEST_CASE("DC")
0418 {
0419         CHECK(encode83(encodeDC(decodeDC("MF%n"))) == "MF%n"sv);
0420         CHECK(encode83(encodeDC(decodeDC("HV6n"))) == "HV6n"sv);
0421         CHECK(encode83(encodeDC(decodeDC("F5]+"))) == "F5]+"sv);
0422         CHECK(encode83(encodeDC(decodeDC("Pj0^"))) == "Pj0^"sv);
0423         CHECK(encode83(encodeDC(decodeDC("O2?U"))) == "O2?U"sv);
0424 }
0425 
0426 TEST_CASE("AC")
0427 {
0428         auto h = "00%#MwS|WCWEM{R*bbWBbH"sv;
0429         for (size_t i = 0; i < h.size(); i += 2) {
0430                 auto s           = h.substr(i, 2);
0431                 const auto maxAC = 0.289157f;
0432                 CHECK(leftPad(encode83(encodeAC(decodeAC(decode83(s), maxAC), maxAC)), 2) == s);
0433         }
0434 }
0435 
0436 TEST_CASE("decode")
0437 {
0438         blurhash::Image i1 = blurhash::decode("LEHV6nWB2yk8pyoJadR*.7kCMdnj", 360, 200);
0439         CHECK(i1.width == 360);
0440         CHECK(i1.height == 200);
0441         CHECK(i1.image.size() == i1.height * i1.width * 3);
0442         CHECK(i1.image[0] == 135);
0443         CHECK(i1.image[1] == 164);
0444         CHECK(i1.image[2] == 177);
0445         CHECK(i1.image[10000] == 173);
0446         CHECK(i1.image[10001] == 176);
0447         CHECK(i1.image[10002] == 163);
0448         // stbi_write_bmp("test.bmp", i1.width, i1.height, 3, (void *)i1.image.data());
0449 
0450         i1 = blurhash::decode("LGF5]+Yk^6#M@-5c,1J5@[or[Q6.", 360, 200);
0451         CHECK(i1.width == 360);
0452         CHECK(i1.height == 200);
0453         CHECK(i1.image.size() == i1.height * i1.width * 3);
0454         // stbi_write_bmp("test2.bmp", i1.width, i1.height, 3, (void *)i1.image.data());
0455 
0456         // invalid inputs
0457         i1 = blurhash::decode(" LGF5]+Yk^6#M@-5c,1J5@[or[Q6.", 360, 200);
0458         CHECK(i1.width == 0);
0459         CHECK(i1.height == 0);
0460         CHECK(i1.image.size() == 0);
0461         i1 = blurhash::decode("  LGF5]+Yk^6#M@-5c,1J5@[or[Q6.", 360, 200);
0462         CHECK(i1.width == 0);
0463         CHECK(i1.height == 0);
0464         CHECK(i1.image.size() == 0);
0465 
0466         i1 = blurhash::decode("LGF5]+Yk^6# M@-5c,1J5@[or[Q6.", 360, 200);
0467         CHECK(i1.width == 0);
0468         CHECK(i1.height == 0);
0469         CHECK(i1.image.size() == 0);
0470         i1 = blurhash::decode("LGF5]+Yk^6#  M@-5c,1J5@[or[Q6.", 360, 200);
0471         CHECK(i1.width == 0);
0472         CHECK(i1.height == 0);
0473         CHECK(i1.image.size() == 0);
0474 
0475         i1 = blurhash::decode("LGF5]+Yk^6# @-5c,1J5@[or[Q6.", 360, 200);
0476         CHECK(i1.width == 0);
0477         CHECK(i1.height == 0);
0478         CHECK(i1.image.size() == 0);
0479         i1 = blurhash::decode(" GF5]+Yk^6#M@-5c,1J5@[or[Q6.", 360, 200);
0480         CHECK(i1.width == 0);
0481         CHECK(i1.height == 0);
0482         CHECK(i1.image.size() == 0);
0483 }
0484 
0485 TEST_CASE("encode")
0486 {
0487         CHECK(blurhash::encode(nullptr, 360, 200, 4, 3) == "");
0488 
0489         std::vector<unsigned char> black(360 * 200 * 3, 0);
0490         CHECK(blurhash::encode(black.data(), 0, 200, 4, 3) == "");
0491         CHECK(blurhash::encode(black.data(), 360, 0, 4, 3) == "");
0492         CHECK(blurhash::encode(black.data(), 360, 200, 0, 3) == "");
0493         CHECK(blurhash::encode(black.data(), 360, 200, 4, 0) == "");
0494         CHECK(blurhash::encode(black.data(), 360, 200, 4, 3) == "L00000fQfQfQfQfQfQfQfQfQfQfQ");
0495 }
0496 #endif