blurhash_ruby 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +15 -0
- data/README.md +1 -0
- data/Rakefile +9 -0
- data/blurhash_ruby.gemspec +17 -0
- data/ext/blurhash_decoder/blurhash_decoder.c +39 -0
- data/ext/blurhash_decoder/decode.c +148 -0
- data/ext/blurhash_decoder/decode.h +54 -0
- data/ext/blurhash_decoder/extconf.rb +6 -0
- data/ext/blurhash_decoder/stb_writer.h +1690 -0
- data/ext/blurhash_encoder/blurhash_encoder.c +52 -0
- data/ext/blurhash_encoder/encode.c +116 -0
- data/ext/blurhash_encoder/encode.h +9 -0
- data/ext/blurhash_encoder/extconf.rb +6 -0
- data/ext/blurhash_encoder/stb_image.h +7177 -0
- data/ext/common.h +26 -0
- data/lib/blurhash_decoder/blurhash_decoder.rb +15 -0
- data/lib/blurhash_encoder/blurhash_encoder.rb +12 -0
- data/lib/blurhash_ruby.rb +15 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ef513fb805c4bdc62e5236843403bb3af8cb06c034c48ac2abc1a9d94a6863e8
|
4
|
+
data.tar.gz: 037752a9951af438c498f2caf27ab31f3717023124fa3c7e4300761cbb7f8c12
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 751b20bc24a4a36e0f97121e1faabef567afdf04e3c9e69763b121a6c7c5c4e9746eeba1b38ef266467109985752868c9b9b26ec5930e2f0afff4f43a9a97e93
|
7
|
+
data.tar.gz: 4b69c4baef24c4e7d696a818b92a6d04d685a01c1c6f8865a84ce31d6102ab2709113ea72070220a87be349c832cc9c2ee49efef8a4e6fd3d82b6380b66eb217
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
## Blurhash Ruby
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# lib = File.expand_path('../lib/', __FILE__)
|
2
|
+
# $:.unshift lib unless $:.include?(lib)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'blurhash_ruby'
|
6
|
+
s.version = '0.0.14'
|
7
|
+
s.summary = "A fast blurhash encoder/decoder gem!"
|
8
|
+
s.description = "A fast blurhash encoder/decoder gem."
|
9
|
+
s.authors = ["Rabin Poudyal"]
|
10
|
+
s.email = 'rabin@trip101.com'
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.require_paths = %w(lib)
|
13
|
+
s.homepage =
|
14
|
+
'https://rubygems.org/gems/blurhash_ruby'
|
15
|
+
s.license = 'MIT'
|
16
|
+
s.extensions = %w[ext/blurhash_decoder/extconf.rb ext/blurhash_encoder/extconf.rb]
|
17
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
#include "decode.h"
|
2
|
+
|
3
|
+
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
4
|
+
#include "stb_writer.h"
|
5
|
+
#include <ruby.h>
|
6
|
+
|
7
|
+
VALUE DECODER = Qnil; /* Ruby Module */
|
8
|
+
|
9
|
+
VALUE method_decode(VALUE self, VALUE blurhash, VALUE heigh, VALUE widt, VALUE punc) {
|
10
|
+
const char * hash = StringValuePtr(blurhash);
|
11
|
+
int height = NUM2INT(heigh);
|
12
|
+
int width = NUM2INT(widt);
|
13
|
+
int punch = NUM2INT(punc);
|
14
|
+
|
15
|
+
const char * output_file = "tmp/out.png";
|
16
|
+
|
17
|
+
const int nChannels = 4;
|
18
|
+
|
19
|
+
uint8_t * bytes = decode(hash, width, height, punch, nChannels);
|
20
|
+
if (!bytes) {
|
21
|
+
fprintf(stderr, "%s is not a valid blurhash, decoding failed.\n", hash);
|
22
|
+
return 1;
|
23
|
+
}
|
24
|
+
|
25
|
+
if (stbi_write_png(output_file, width, height, nChannels, bytes, nChannels * width) == 0) {
|
26
|
+
fprintf(stderr, "Failed to write PNG file %s\n", output_file);
|
27
|
+
return 1;
|
28
|
+
}
|
29
|
+
freePixelArray(bytes);
|
30
|
+
fprintf(stdout, "Decoded blurhash successfully, wrote PNG file %s\n", output_file);
|
31
|
+
return 0;
|
32
|
+
}
|
33
|
+
|
34
|
+
void
|
35
|
+
Init_blurhash_decoder(void) {
|
36
|
+
DECODER = rb_define_module("DECODER");
|
37
|
+
rb_define_method(DECODER, "decode", method_decode, 4);
|
38
|
+
}
|
39
|
+
|
@@ -0,0 +1,148 @@
|
|
1
|
+
#include "decode.h"
|
2
|
+
#include "../common.h"
|
3
|
+
|
4
|
+
static char chars[83] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~";
|
5
|
+
|
6
|
+
static inline uint8_t clampToUByte(int * src) {
|
7
|
+
if( *src >= 0 && *src <= 255 )
|
8
|
+
return *src;
|
9
|
+
return (*src < 0) ? 0 : 255;
|
10
|
+
}
|
11
|
+
|
12
|
+
static inline uint8_t * createByteArray(int size) {
|
13
|
+
return (uint8_t *)malloc(size * sizeof(uint8_t));
|
14
|
+
}
|
15
|
+
|
16
|
+
int decodeToInt(const char * string, int start, int end) {
|
17
|
+
int value = 0, iter1 = 0, iter2 = 0;
|
18
|
+
for( iter1 = start; iter1 < end; iter1 ++) {
|
19
|
+
int index = -1;
|
20
|
+
for(iter2 = 0; iter2 < 83; iter2 ++) {
|
21
|
+
if (chars[iter2] == string[iter1]) {
|
22
|
+
index = iter2;
|
23
|
+
break;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
if (index == -1) return -1;
|
27
|
+
value = value * 83 + index;
|
28
|
+
}
|
29
|
+
return value;
|
30
|
+
}
|
31
|
+
|
32
|
+
bool isValidBlurhash(const char * blurhash) {
|
33
|
+
|
34
|
+
const int hashLength = strlen(blurhash);
|
35
|
+
|
36
|
+
if ( !blurhash || strlen(blurhash) < 6) return false;
|
37
|
+
|
38
|
+
int sizeFlag = decodeToInt(blurhash, 0, 1); //Get size from first character
|
39
|
+
int numY = (int)floorf(sizeFlag / 9) + 1;
|
40
|
+
int numX = (sizeFlag % 9) + 1;
|
41
|
+
|
42
|
+
if (hashLength != 4 + 2 * numX * numY) return false;
|
43
|
+
return true;
|
44
|
+
}
|
45
|
+
|
46
|
+
void decodeDC(int value, float * r, float * g, float * b) {
|
47
|
+
*r = sRGBToLinear(value >> 16); // R-component
|
48
|
+
*g = sRGBToLinear((value >> 8) & 255); // G-Component
|
49
|
+
*b = sRGBToLinear(value & 255); // B-Component
|
50
|
+
}
|
51
|
+
|
52
|
+
void decodeAC(int value, float maximumValue, float * r, float * g, float * b) {
|
53
|
+
int quantR = (int)floorf(value / (19 * 19));
|
54
|
+
int quantG = (int)floorf(value / 19) % 19;
|
55
|
+
int quantB = (int)value % 19;
|
56
|
+
|
57
|
+
*r = signPow(((float)quantR - 9) / 9, 2.0) * maximumValue;
|
58
|
+
*g = signPow(((float)quantG - 9) / 9, 2.0) * maximumValue;
|
59
|
+
*b = signPow(((float)quantB - 9) / 9, 2.0) * maximumValue;
|
60
|
+
}
|
61
|
+
|
62
|
+
int decodeToArray(const char * blurhash, int width, int height, int punch, int nChannels, uint8_t * pixelArray) {
|
63
|
+
if (! isValidBlurhash(blurhash)) return -1;
|
64
|
+
if (punch < 1) punch = 1;
|
65
|
+
|
66
|
+
int sizeFlag = decodeToInt(blurhash, 0, 1);
|
67
|
+
int numY = (int)floorf(sizeFlag / 9) + 1;
|
68
|
+
int numX = (sizeFlag % 9) + 1;
|
69
|
+
int iter = 0;
|
70
|
+
|
71
|
+
float r = 0, g = 0, b = 0;
|
72
|
+
int quantizedMaxValue = decodeToInt(blurhash, 1, 2);
|
73
|
+
if (quantizedMaxValue == -1) return -1;
|
74
|
+
|
75
|
+
float maxValue = ((float)(quantizedMaxValue + 1)) / 166;
|
76
|
+
|
77
|
+
int colors_size = numX * numY;
|
78
|
+
float colors[colors_size][3];
|
79
|
+
|
80
|
+
for(iter = 0; iter < colors_size; iter ++) {
|
81
|
+
if (iter == 0) {
|
82
|
+
int value = decodeToInt(blurhash, 2, 6);
|
83
|
+
if (value == -1) return -1;
|
84
|
+
decodeDC(value, &r, &g, &b);
|
85
|
+
colors[iter][0] = r;
|
86
|
+
colors[iter][1] = g;
|
87
|
+
colors[iter][2] = b;
|
88
|
+
|
89
|
+
} else {
|
90
|
+
int value = decodeToInt(blurhash, 4 + iter * 2, 6 + iter * 2);
|
91
|
+
if (value == -1) return -1;
|
92
|
+
decodeAC(value, maxValue * punch, &r, &g, &b);
|
93
|
+
colors[iter][0] = r;
|
94
|
+
colors[iter][1] = g;
|
95
|
+
colors[iter][2] = b;
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
int bytesPerRow = width * nChannels;
|
100
|
+
int x = 0, y = 0, i = 0, j = 0;
|
101
|
+
int intR = 0, intG = 0, intB = 0;
|
102
|
+
|
103
|
+
for(y = 0; y < height; y ++) {
|
104
|
+
for(x = 0; x < width; x ++) {
|
105
|
+
|
106
|
+
float r = 0, g = 0, b = 0;
|
107
|
+
|
108
|
+
for(j = 0; j < numY; j ++) {
|
109
|
+
for(i = 0; i < numX; i ++) {
|
110
|
+
float basics = cos((M_PI * x * i) / width) * cos((M_PI * y * j) / height);
|
111
|
+
int idx = i + j * numX;
|
112
|
+
r += colors[idx][0] * basics;
|
113
|
+
g += colors[idx][1] * basics;
|
114
|
+
b += colors[idx][2] * basics;
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
intR = linearTosRGB(r);
|
119
|
+
intG = linearTosRGB(g);
|
120
|
+
intB = linearTosRGB(b);
|
121
|
+
|
122
|
+
pixelArray[nChannels * x + 0 + y * bytesPerRow] = clampToUByte(&intR);
|
123
|
+
pixelArray[nChannels * x + 1 + y * bytesPerRow] = clampToUByte(&intG);
|
124
|
+
pixelArray[nChannels * x + 2 + y * bytesPerRow] = clampToUByte(&intB);
|
125
|
+
|
126
|
+
if (nChannels == 4)
|
127
|
+
pixelArray[nChannels * x + 3 + y * bytesPerRow] = 255; // If nChannels=4, treat each pixel as RGBA instead of RGB
|
128
|
+
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
return 0;
|
133
|
+
}
|
134
|
+
|
135
|
+
uint8_t * decode(const char * blurhash, int width, int height, int punch, int nChannels) {
|
136
|
+
int bytesPerRow = width * nChannels;
|
137
|
+
uint8_t * pixelArray = createByteArray(bytesPerRow * height);
|
138
|
+
|
139
|
+
if (decodeToArray(blurhash, width, height, punch, nChannels, pixelArray) == -1)
|
140
|
+
return NULL;
|
141
|
+
return pixelArray;
|
142
|
+
}
|
143
|
+
|
144
|
+
void freePixelArray(uint8_t * pixelArray) {
|
145
|
+
if (pixelArray) {
|
146
|
+
free(pixelArray);
|
147
|
+
}
|
148
|
+
}
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#ifndef __BLURHASH_DECODE_H__
|
2
|
+
|
3
|
+
#define __BLURHASH_DECODE_H
|
4
|
+
|
5
|
+
#include <math.h>
|
6
|
+
#include <stdbool.h>
|
7
|
+
#include <string.h>
|
8
|
+
#include <stdlib.h>
|
9
|
+
#include <stdint.h>
|
10
|
+
|
11
|
+
/*
|
12
|
+
decode : Returns the pixel array of the result image given the blurhash string,
|
13
|
+
Parameters :
|
14
|
+
blurhash : A string representing the blurhash to be decoded.
|
15
|
+
width : Width of the resulting image
|
16
|
+
height : Height of the resulting image
|
17
|
+
punch : The factor to improve the contrast, default = 1
|
18
|
+
nChannels : Number of channels in the resulting image array, 3 = RGB, 4 = RGBA
|
19
|
+
Returns : A pointer to memory region where pixels are stored in (H, W, C) format
|
20
|
+
*/
|
21
|
+
uint8_t * decode(const char * blurhash, int width, int height, int punch, int nChannels);
|
22
|
+
|
23
|
+
/*
|
24
|
+
decodeToArray : Decodes the blurhash and copies the pixels to pixelArray,
|
25
|
+
This method is suggested if you use an external memory allocator for pixelArray.
|
26
|
+
pixelArray should be of size : width * height * nChannels
|
27
|
+
Parameters :
|
28
|
+
blurhash : A string representing the blurhash to be decoded.
|
29
|
+
width : Width of the resulting image
|
30
|
+
height : Height of the resulting image
|
31
|
+
punch : The factor to improve the contrast, default = 1
|
32
|
+
nChannels : Number of channels in the resulting image array, 3 = RGB, 4 = RGBA
|
33
|
+
pixelArray : Pointer to memory region where pixels needs to be copied.
|
34
|
+
Returns : int, -1 if error 0 if successful
|
35
|
+
*/
|
36
|
+
int decodeToArray(const char * blurhash, int width, int height, int punch, int nChannels, uint8_t * pixelArray);
|
37
|
+
|
38
|
+
/*
|
39
|
+
isValidBlurhash : Checks if the Blurhash is valid or not.
|
40
|
+
Parameters :
|
41
|
+
blurhash : A string representing the blurhash
|
42
|
+
Returns : bool (true if it is a valid blurhash, else false)
|
43
|
+
*/
|
44
|
+
bool isValidBlurhash(const char * blurhash);
|
45
|
+
|
46
|
+
/*
|
47
|
+
freePixelArray : Frees the pixel array
|
48
|
+
Parameters :
|
49
|
+
pixelArray : Pixel array pointer which will be freed.
|
50
|
+
Returns : void (None)
|
51
|
+
*/
|
52
|
+
void freePixelArray(uint8_t * pixelArray);
|
53
|
+
|
54
|
+
#endif
|