blurhash_decoder 0.0.7 → 0.0.11
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 +4 -4
- data/.gitignore +4 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +15 -0
- data/Rakefile +9 -0
- data/blurhash_decoder.gemspec +17 -0
- data/ext/blurhash_decoder/blurhash_decoder.c +205 -0
- data/ext/blurhash_decoder/stb_writer.h +1690 -0
- data/ext/blurhash_encoder/blurhash_encoder.c +52 -0
- data/ext/blurhash_encoder/common.h +26 -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/lib/blurhash_decoder/blurhash_decoder.bundle +0 -0
- data/lib/blurhash_decoder.rb +11 -3
- data/lib/blurhash_encoder/blurhash_encoder.bundle +0 -0
- data/lib/blurhash_encoder.rb +16 -0
- data/run.sh +4 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b0221d3511057d149926cc067e2a8e2eb51ff0c724476666e618df955160626e
|
4
|
+
data.tar.gz: a271106fec9b1e3872b0ab1a8ec7753e8b4e68a055887ba0dee57e9bd3964d1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbbb92c47bcb19a4445e58c1a22ec038642fce18d01209a9b528b70707c42905385164aaf48b9b0b9f2165f7d7a1118ccef7533b2e58e56e37ca862fba8d98a2
|
7
|
+
data.tar.gz: 125ef0207f696e1bf40055e8ffdbd14419b47ce83e0e7efb9bc7dfb79dbe55b685efa04c4d894a55d17a4cd00c010b7e437b2187167a4d6234df454c4c58ea12
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
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_decoder'
|
6
|
+
s.version = '0.0.11'
|
7
|
+
s.summary = "A blurhash decoder gem!"
|
8
|
+
s.description = "A simple blurhash decoder gem using c."
|
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_decoder'
|
15
|
+
s.license = 'MIT'
|
16
|
+
s.extensions = %w[ext/blurhash_decoder/extconf.rb ext/blurhash_encoder/extconf.rb]
|
17
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include<math.h>
|
4
|
+
#include <stdbool.h>
|
5
|
+
|
6
|
+
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
7
|
+
#include "stb_writer.h"
|
8
|
+
|
9
|
+
#define M_PI 3.14159265358979323846
|
10
|
+
|
11
|
+
VALUE DECODER = Qnil; /* Ruby Module */
|
12
|
+
static char chars[83] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~";
|
13
|
+
|
14
|
+
static inline int linearTosRGB(float value) {
|
15
|
+
float v = fmaxf(0, fminf(1, value));
|
16
|
+
if(v <= 0.0031308) return v * 12.92 * 255 + 0.5;
|
17
|
+
else return (1.055 * powf(v, 1 / 2.4) - 0.055) * 255 + 0.5;
|
18
|
+
}
|
19
|
+
|
20
|
+
static inline float sRGBToLinear(int value) {
|
21
|
+
float v = (float)value / 255;
|
22
|
+
if(v <= 0.04045) return v / 12.92;
|
23
|
+
else return powf((v + 0.055) / 1.055, 2.4);
|
24
|
+
}
|
25
|
+
|
26
|
+
static inline float signPow(float value, float exp) {
|
27
|
+
return copysignf(powf(fabsf(value), exp), value);
|
28
|
+
}
|
29
|
+
|
30
|
+
static inline uint8_t clampToUByte(int * src) {
|
31
|
+
if( *src >= 0 && *src <= 255 )
|
32
|
+
return *src;
|
33
|
+
return (*src < 0) ? 0 : 255;
|
34
|
+
}
|
35
|
+
|
36
|
+
static inline uint8_t * createByteArray(int size) {
|
37
|
+
return (uint8_t *)malloc(size * sizeof(uint8_t));
|
38
|
+
}
|
39
|
+
|
40
|
+
int decodeToInt(const char * string, int start, int end) {
|
41
|
+
int value = 0, iter1 = 0, iter2 = 0;
|
42
|
+
for( iter1 = start; iter1 < end; iter1 ++) {
|
43
|
+
int index = -1;
|
44
|
+
for(iter2 = 0; iter2 < 83; iter2 ++) {
|
45
|
+
if (chars[iter2] == string[iter1]) {
|
46
|
+
index = iter2;
|
47
|
+
break;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
if (index == -1) return -1;
|
51
|
+
value = value * 83 + index;
|
52
|
+
}
|
53
|
+
return value;
|
54
|
+
}
|
55
|
+
|
56
|
+
bool isValidBlurhash(const char * blurhash) {
|
57
|
+
|
58
|
+
const int hashLength = strlen(blurhash);
|
59
|
+
|
60
|
+
if ( !blurhash || strlen(blurhash) < 6) return false;
|
61
|
+
|
62
|
+
int sizeFlag = decodeToInt(blurhash, 0, 1); //Get size from first character
|
63
|
+
int numY = (int)floorf(sizeFlag / 9) + 1;
|
64
|
+
int numX = (sizeFlag % 9) + 1;
|
65
|
+
|
66
|
+
if (hashLength != 4 + 2 * numX * numY) return false;
|
67
|
+
return true;
|
68
|
+
}
|
69
|
+
|
70
|
+
void decodeDC(int value, float * r, float * g, float * b) {
|
71
|
+
*r = sRGBToLinear(value >> 16); // R-component
|
72
|
+
*g = sRGBToLinear((value >> 8) & 255); // G-Component
|
73
|
+
*b = sRGBToLinear(value & 255); // B-Component
|
74
|
+
}
|
75
|
+
|
76
|
+
void decodeAC(int value, float maximumValue, float * r, float * g, float * b) {
|
77
|
+
int quantR = (int)floorf(value / (19 * 19));
|
78
|
+
int quantG = (int)floorf(value / 19) % 19;
|
79
|
+
int quantB = (int)value % 19;
|
80
|
+
|
81
|
+
*r = signPow(((float)quantR - 9) / 9, 2.0) * maximumValue;
|
82
|
+
*g = signPow(((float)quantG - 9) / 9, 2.0) * maximumValue;
|
83
|
+
*b = signPow(((float)quantB - 9) / 9, 2.0) * maximumValue;
|
84
|
+
}
|
85
|
+
|
86
|
+
int decodeToArray(const char * blurhash, int width, int height, int punch, int nChannels, uint8_t * pixelArray) {
|
87
|
+
if (! isValidBlurhash(blurhash)) return -1;
|
88
|
+
if (punch < 1) punch = 1;
|
89
|
+
|
90
|
+
int sizeFlag = decodeToInt(blurhash, 0, 1);
|
91
|
+
int numY = (int)floorf(sizeFlag / 9) + 1;
|
92
|
+
int numX = (sizeFlag % 9) + 1;
|
93
|
+
int iter = 0;
|
94
|
+
|
95
|
+
float r = 0, g = 0, b = 0;
|
96
|
+
int quantizedMaxValue = decodeToInt(blurhash, 1, 2);
|
97
|
+
if (quantizedMaxValue == -1) return -1;
|
98
|
+
|
99
|
+
float maxValue = ((float)(quantizedMaxValue + 1)) / 166;
|
100
|
+
|
101
|
+
int colors_size = numX * numY;
|
102
|
+
float colors[colors_size][3];
|
103
|
+
|
104
|
+
for(iter = 0; iter < colors_size; iter ++) {
|
105
|
+
if (iter == 0) {
|
106
|
+
int value = decodeToInt(blurhash, 2, 6);
|
107
|
+
if (value == -1) return -1;
|
108
|
+
decodeDC(value, &r, &g, &b);
|
109
|
+
colors[iter][0] = r;
|
110
|
+
colors[iter][1] = g;
|
111
|
+
colors[iter][2] = b;
|
112
|
+
|
113
|
+
} else {
|
114
|
+
int value = decodeToInt(blurhash, 4 + iter * 2, 6 + iter * 2);
|
115
|
+
if (value == -1) return -1;
|
116
|
+
decodeAC(value, maxValue * punch, &r, &g, &b);
|
117
|
+
colors[iter][0] = r;
|
118
|
+
colors[iter][1] = g;
|
119
|
+
colors[iter][2] = b;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
int bytesPerRow = width * nChannels;
|
124
|
+
int x = 0, y = 0, i = 0, j = 0;
|
125
|
+
int intR = 0, intG = 0, intB = 0;
|
126
|
+
|
127
|
+
for(y = 0; y < height; y ++) {
|
128
|
+
for(x = 0; x < width; x ++) {
|
129
|
+
|
130
|
+
float r = 0, g = 0, b = 0;
|
131
|
+
|
132
|
+
for(j = 0; j < numY; j ++) {
|
133
|
+
for(i = 0; i < numX; i ++) {
|
134
|
+
float basics = cos((M_PI * x * i) / width) * cos((M_PI * y * j) / height);
|
135
|
+
int idx = i + j * numX;
|
136
|
+
r += colors[idx][0] * basics;
|
137
|
+
g += colors[idx][1] * basics;
|
138
|
+
b += colors[idx][2] * basics;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
intR = linearTosRGB(r);
|
143
|
+
intG = linearTosRGB(g);
|
144
|
+
intB = linearTosRGB(b);
|
145
|
+
|
146
|
+
pixelArray[nChannels * x + 0 + y * bytesPerRow] = clampToUByte(&intR);
|
147
|
+
pixelArray[nChannels * x + 1 + y * bytesPerRow] = clampToUByte(&intG);
|
148
|
+
pixelArray[nChannels * x + 2 + y * bytesPerRow] = clampToUByte(&intB);
|
149
|
+
|
150
|
+
if (nChannels == 4)
|
151
|
+
pixelArray[nChannels * x + 3 + y * bytesPerRow] = 255; // If nChannels=4, treat each pixel as RGBA instead of RGB
|
152
|
+
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
return 0;
|
157
|
+
}
|
158
|
+
|
159
|
+
uint8_t * decode(const char * blurhash, int width, int height, int punch, int nChannels) {
|
160
|
+
int bytesPerRow = width * nChannels;
|
161
|
+
uint8_t * pixelArray = createByteArray(bytesPerRow * height);
|
162
|
+
|
163
|
+
if (decodeToArray(blurhash, width, height, punch, nChannels, pixelArray) == -1)
|
164
|
+
return NULL;
|
165
|
+
return pixelArray;
|
166
|
+
}
|
167
|
+
|
168
|
+
void freePixelArray(uint8_t * pixelArray) {
|
169
|
+
if (pixelArray) {
|
170
|
+
free(pixelArray);
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
VALUE method_decode(VALUE self, VALUE blurhash, VALUE heigh, VALUE widt, VALUE punc) {
|
175
|
+
const char * hash = StringValuePtr(blurhash);
|
176
|
+
int height = NUM2INT(heigh);
|
177
|
+
int width = NUM2INT(widt);
|
178
|
+
int punch = NUM2INT(punc);
|
179
|
+
|
180
|
+
const char * output_file = "tmp/out.png";
|
181
|
+
|
182
|
+
const int nChannels = 4;
|
183
|
+
|
184
|
+
uint8_t * bytes = decode(hash, width, height, punch, nChannels);
|
185
|
+
if (!bytes) {
|
186
|
+
// fprintf(stderr, "%s is not a valid blurhash, decoding failed.\n", hash);
|
187
|
+
return 1;
|
188
|
+
}
|
189
|
+
|
190
|
+
if (stbi_write_png(output_file, width, height, nChannels, bytes, nChannels * width) == 0) {
|
191
|
+
// fprintf(stderr, "Failed to write PNG file %s\n", output_file);
|
192
|
+
return 1;
|
193
|
+
}
|
194
|
+
freePixelArray(bytes);
|
195
|
+
// fprintf(stdout, "Decoded blurhash successfully, wrote PNG file %s\n", output_file);
|
196
|
+
return 0;
|
197
|
+
// return rb_float_new(5); /* Convert C double to Ruby float */
|
198
|
+
}
|
199
|
+
|
200
|
+
void
|
201
|
+
Init_blurhash_decoder(void) {
|
202
|
+
DECODER = rb_define_module("DECODER");
|
203
|
+
rb_define_method(DECODER, "decode", method_decode, 4);
|
204
|
+
}
|
205
|
+
|