mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-24 20:18:53 +03:00
Add encoding
This commit is contained in:
parent
12e2d26a60
commit
fb7bbf38cd
7 changed files with 7883 additions and 9 deletions
23
LICENSE
Normal file
23
LICENSE
Normal file
|
@ -0,0 +1,23 @@
|
|||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
97
blurhash.cpp
97
blurhash.cpp
|
@ -1,3 +1,5 @@
|
|||
#include "blurhash.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
|
@ -5,13 +7,12 @@
|
|||
#include <vector>
|
||||
|
||||
#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
#include <doctest.h>
|
||||
#endif
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace {
|
||||
constexpr std::array<char, 84> int_to_b83{
|
||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"};
|
||||
|
||||
|
@ -98,7 +99,7 @@ decodeMaxAC(std::string_view maxAC)
|
|||
int
|
||||
encodeMaxAC(float maxAC)
|
||||
{
|
||||
return int(std::max(0., std::min(82., std::floor(maxAC * 166 - 0.5))));
|
||||
return std::max(0, std::min(82, int(maxAC * 166 - 0.5)));
|
||||
}
|
||||
|
||||
float
|
||||
|
@ -224,13 +225,29 @@ decodeAC(std::string_view value, float maximumValue)
|
|||
return decodeAC(decode83(value), maximumValue);
|
||||
}
|
||||
|
||||
namespace blurhash {
|
||||
struct Image
|
||||
Color
|
||||
multiplyBasisFunction(Components components, int width, int height, unsigned char *pixels)
|
||||
{
|
||||
size_t width, height;
|
||||
std::vector<unsigned char> image; // pixels rgb
|
||||
};
|
||||
Color c{};
|
||||
float normalisation = (components.x == 0 && components.y == 0) ? 1 : 2;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
float basis = std::cos(M_PI * components.x * x / float(width)) *
|
||||
std::cos(M_PI * components.y * y / float(height));
|
||||
c.r += basis * srgbToLinear(pixels[3 * x + 0 + y * width * 3]);
|
||||
c.g += basis * srgbToLinear(pixels[3 * x + 1 + y * width * 3]);
|
||||
c.b += basis * srgbToLinear(pixels[3 * x + 2 + y * width * 3]);
|
||||
}
|
||||
}
|
||||
|
||||
float scale = normalisation / (width * height);
|
||||
c *= scale;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
namespace blurhash {
|
||||
Image
|
||||
decode(std::string_view blurhash, size_t width, size_t height)
|
||||
{
|
||||
|
@ -284,6 +301,58 @@ decode(std::string_view blurhash, size_t width, size_t height)
|
|||
|
||||
return i;
|
||||
}
|
||||
|
||||
std::string
|
||||
encode(unsigned char *image, size_t width, size_t height, int components_x, int components_y)
|
||||
{
|
||||
if (width < 1 || height < 1 || components_x < 1 || components_x > 9 || components_y < 1 ||
|
||||
components_y > 9 || !image)
|
||||
return "";
|
||||
|
||||
std::vector<Color> factors;
|
||||
factors.reserve(components_x * components_y);
|
||||
for (int y = 0; y < components_y; y++) {
|
||||
for (int x = 0; x < components_x; x++) {
|
||||
factors.push_back(multiplyBasisFunction({x, y}, width, height, image));
|
||||
}
|
||||
}
|
||||
|
||||
assert(factors.size() > 0);
|
||||
|
||||
auto dc = factors.front();
|
||||
factors.erase(factors.begin());
|
||||
|
||||
std::string h;
|
||||
|
||||
h += leftPad(encode83(packComponents({components_x, components_y})), 1);
|
||||
|
||||
float maximumValue;
|
||||
if (!factors.empty()) {
|
||||
float actualMaximumValue = 0;
|
||||
for (auto ac : factors) {
|
||||
actualMaximumValue = std::max({
|
||||
std::abs(ac.r),
|
||||
std::abs(ac.g),
|
||||
std::abs(ac.b),
|
||||
actualMaximumValue,
|
||||
});
|
||||
}
|
||||
|
||||
int quantisedMaximumValue = encodeMaxAC(actualMaximumValue);
|
||||
maximumValue = ((float)quantisedMaximumValue + 1) / 166;
|
||||
h += leftPad(encode83(quantisedMaximumValue), 1);
|
||||
} else {
|
||||
maximumValue = 1;
|
||||
h += leftPad(encode83(0), 1);
|
||||
}
|
||||
|
||||
h += leftPad(encode83(encodeDC(dc)), 4);
|
||||
|
||||
for (auto ac : factors)
|
||||
h += leftPad(encode83(encodeAC(ac, maximumValue)), 2);
|
||||
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||
|
@ -386,4 +455,16 @@ TEST_CASE("decode")
|
|||
CHECK(i1.height == 0);
|
||||
CHECK(i1.image.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("encode")
|
||||
{
|
||||
CHECK(blurhash::encode(nullptr, 360, 200, 4, 3) == "");
|
||||
|
||||
std::vector<unsigned char> black(360 * 200 * 3, 0);
|
||||
CHECK(blurhash::encode(black.data(), 0, 200, 4, 3) == "");
|
||||
CHECK(blurhash::encode(black.data(), 360, 0, 4, 3) == "");
|
||||
CHECK(blurhash::encode(black.data(), 360, 200, 0, 3) == "");
|
||||
CHECK(blurhash::encode(black.data(), 360, 200, 4, 0) == "");
|
||||
CHECK(blurhash::encode(black.data(), 360, 200, 4, 3) == "L00000fQfQfQfQfQfQfQfQfQfQfQ");
|
||||
}
|
||||
#endif
|
||||
|
|
21
blurhash.hpp
Normal file
21
blurhash.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace blurhash {
|
||||
struct Image
|
||||
{
|
||||
size_t width, height;
|
||||
std::vector<unsigned char> image; // pixels rgb
|
||||
};
|
||||
|
||||
// Decode a blurhash to an image with size width*height
|
||||
Image
|
||||
decode(std::string_view blurhash, size_t width, size_t height);
|
||||
|
||||
// Encode an image of rgb pixels (without padding) with size width*height into a blurhash with x*y
|
||||
// components
|
||||
std::string
|
||||
encode(unsigned char *image, size_t width, size_t height, int x, int y);
|
||||
}
|
43
blurhash2bmp.cpp
Normal file
43
blurhash2bmp.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "blurhash.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <iostream>
|
||||
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 5) {
|
||||
std::cerr << "Usage: blurhash2bmp [hash] [width] [height] [output name]"
|
||||
<< std::endl;
|
||||
return -2;
|
||||
}
|
||||
|
||||
int height = 0, width = 0;
|
||||
|
||||
std::string_view width_str{argv[2]}, height_str{argv[3]};
|
||||
std::from_chars(height_str.begin(), height_str.end(), height);
|
||||
if (height <= 0) {
|
||||
std::cerr << "Invalid height.";
|
||||
return -2;
|
||||
}
|
||||
std::from_chars(width_str.begin(), width_str.end(), width);
|
||||
if (width <= 0) {
|
||||
std::cerr << "Invalid width.";
|
||||
return -2;
|
||||
}
|
||||
|
||||
blurhash::Image image = blurhash::decode(argv[1], width, height);
|
||||
if (image.image.empty()) {
|
||||
std::cerr << "Decode failed.";
|
||||
return -1;
|
||||
}
|
||||
if (!stbi_write_bmp(argv[4], image.width, image.height, 3, (void *)image.image.data())) {
|
||||
std::cerr << "Image write failed.";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
47
image2blurhash.cpp
Normal file
47
image2blurhash.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "blurhash.hpp"
|
||||
|
||||
#include <charconv>
|
||||
#include <iostream>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 4) {
|
||||
std::cerr << "Usage: blurhash [filename] [num x components] [num y components]"
|
||||
<< std::endl;
|
||||
return -2;
|
||||
}
|
||||
|
||||
int x = 0, y = 0;
|
||||
|
||||
std::string_view x_str{argv[2]}, y_str{argv[3]};
|
||||
std::from_chars(x_str.begin(), x_str.end(), x);
|
||||
if (x <= 0 || x > 9) {
|
||||
std::cerr << "Invalid x components, should be between 1 and 9." << std::endl;
|
||||
return -2;
|
||||
}
|
||||
std::from_chars(y_str.begin(), y_str.end(), y);
|
||||
if (y <= 0 || y > 9) {
|
||||
std::cerr << "Invalid y components, should be between 1 and 9." << std::endl;
|
||||
return -2;
|
||||
}
|
||||
|
||||
int width, height, n;
|
||||
unsigned char *data = stbi_load(argv[1], &width, &height, &n, 3);
|
||||
if (!data) {
|
||||
std::cerr << "Image loading failed for file '" << argv[1] << "'." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
if (n != 3) {
|
||||
std::cerr << "Couldn't decode image to 3 channel rgb." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << blurhash::encode(data, width, height, x, y) << std::endl;
|
||||
|
||||
stbi_image_free(data);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -2,7 +2,7 @@ project('blurhash', 'cpp',
|
|||
version : '0.1',
|
||||
default_options : ['warning_level=3', 'cpp_std=c++17'])
|
||||
|
||||
static_library('blurhash',
|
||||
lib = static_library('blurhash',
|
||||
'blurhash.cpp',
|
||||
install : true)
|
||||
|
||||
|
@ -14,3 +14,6 @@ tests = executable('blurhash-tests',
|
|||
dependencies: doctest_dep,
|
||||
install : false)
|
||||
test('blurhash-tests', tests)
|
||||
|
||||
executable('blurhash2bmp', 'blurhash2bmp.cpp', link_with: lib, install: true)
|
||||
executable('blurhash', 'image2blurhash.cpp', link_with: lib, install: true)
|
||||
|
|
7656
stb_image.h
Normal file
7656
stb_image.h
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue