mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 04:28:49 +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 <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
@ -5,13 +7,12 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
||||||
#include "stb_image_write.h"
|
|
||||||
#include <doctest.h>
|
#include <doctest.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
|
|
||||||
|
namespace {
|
||||||
constexpr std::array<char, 84> int_to_b83{
|
constexpr std::array<char, 84> int_to_b83{
|
||||||
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"};
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~"};
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ decodeMaxAC(std::string_view maxAC)
|
||||||
int
|
int
|
||||||
encodeMaxAC(float maxAC)
|
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
|
float
|
||||||
|
@ -224,13 +225,29 @@ decodeAC(std::string_view value, float maximumValue)
|
||||||
return decodeAC(decode83(value), maximumValue);
|
return decodeAC(decode83(value), maximumValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace blurhash {
|
Color
|
||||||
struct Image
|
multiplyBasisFunction(Components components, int width, int height, unsigned char *pixels)
|
||||||
{
|
{
|
||||||
size_t width, height;
|
Color c{};
|
||||||
std::vector<unsigned char> image; // pixels rgb
|
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
|
Image
|
||||||
decode(std::string_view blurhash, size_t width, size_t height)
|
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;
|
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
|
#ifdef DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
|
||||||
|
@ -386,4 +455,16 @@ TEST_CASE("decode")
|
||||||
CHECK(i1.height == 0);
|
CHECK(i1.height == 0);
|
||||||
CHECK(i1.image.size() == 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
|
#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',
|
version : '0.1',
|
||||||
default_options : ['warning_level=3', 'cpp_std=c++17'])
|
default_options : ['warning_level=3', 'cpp_std=c++17'])
|
||||||
|
|
||||||
static_library('blurhash',
|
lib = static_library('blurhash',
|
||||||
'blurhash.cpp',
|
'blurhash.cpp',
|
||||||
install : true)
|
install : true)
|
||||||
|
|
||||||
|
@ -14,3 +14,6 @@ tests = executable('blurhash-tests',
|
||||||
dependencies: doctest_dep,
|
dependencies: doctest_dep,
|
||||||
install : false)
|
install : false)
|
||||||
test('blurhash-tests', tests)
|
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