fast_thumbhash 0.7.0 → 0.7.2
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/.ruby-version +1 -0
- data/Gemfile.lock +3 -1
- data/README.md +58 -0
- data/ext/fast_thumbhash/fast_thumbhash.c +78 -50
- data/ext/fast_thumbhash/fast_thumbhash.h +18 -20
- data/lib/fast_thumbhash/version.rb +1 -1
- data/lib/fast_thumbhash.rb +3 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e30e34927a692280eac28e7a1237d9964ad6899a29ab7268a321df8ea2b9d979
|
4
|
+
data.tar.gz: 39cd14b05a0d0a598ea3f48646c169935cbe17e17a4a2000450833347e4465b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee54e878ad7002544e568618281784baf2a565aa3f3ea44b0f0918c81b13ea56b849e12ea5c3a7baa7d36b5e94229b015bf256c0c4c23492789e0182bfc6457a
|
7
|
+
data.tar.gz: 9bd9096c25bf0a64999f6a484becae2e075018f3e9bc4a215dd94c4156e4c0319b7ad61ae7ff4d99d9c54cf5551c5e68b08d1d3f7000a1d1977f56816c043cac
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.8
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
<!--datocms-autoinclude-header start-->
|
2
|
+
|
3
|
+
<a href="https://www.datocms.com/"><img src="https://www.datocms.com/images/full_logo.svg" height="60"></a>
|
4
|
+
|
5
|
+
👉 [Visit the DatoCMS homepage](https://www.datocms.com) or see [What is DatoCMS?](#what-is-datocms)
|
6
|
+
|
7
|
+
---
|
8
|
+
|
9
|
+
<!--datocms-autoinclude-header end-->
|
10
|
+
|
1
11
|
# FastThumbhash
|
2
12
|
|
3
13
|
[](https://github.com/datocms/fast_thumbhash/actions/workflows/main.yml)
|
@@ -124,3 +134,51 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
124
134
|
## Code of Conduct
|
125
135
|
|
126
136
|
Everyone interacting in the FastThumbhash project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/datocms/fast_thumbhash/blob/master/CODE_OF_CONDUCT.md).
|
137
|
+
|
138
|
+
<!--datocms-autoinclude-footer start-->
|
139
|
+
|
140
|
+
---
|
141
|
+
|
142
|
+
# What is DatoCMS?
|
143
|
+
|
144
|
+
<a href="https://www.datocms.com/"><img src="https://www.datocms.com/images/full_logo.svg" height="60" alt="DatoCMS - The Headless CMS for the Modern Web"></a>
|
145
|
+
|
146
|
+
[DatoCMS](https://www.datocms.com/) is the REST & GraphQL Headless CMS for the modern web.
|
147
|
+
|
148
|
+
Trusted by over 25,000 enterprise businesses, agencies, and individuals across the world, DatoCMS users create online content at scale from a central hub and distribute it via API. We ❤️ our [developers](https://www.datocms.com/team/best-cms-for-developers), [content editors](https://www.datocms.com/team/content-creators) and [marketers](https://www.datocms.com/team/cms-digital-marketing)!
|
149
|
+
|
150
|
+
**Why DatoCMS?**
|
151
|
+
|
152
|
+
- **API-First Architecture**: Built for both REST and GraphQL, enabling flexible content delivery
|
153
|
+
- **Just Enough Features**: We believe in keeping things simple, and giving you [the right feature-set tools](https://www.datocms.com/features) to get the job done
|
154
|
+
- **Developer Experience**: First-class TypeScript support with powerful developer tools
|
155
|
+
|
156
|
+
**Getting Started:**
|
157
|
+
|
158
|
+
- ⚡️ [Create Free Account](https://dashboard.datocms.com/signup) - Get started with DatoCMS in minutes
|
159
|
+
- 🔖 [Documentation](https://www.datocms.com/docs) - Comprehensive guides and API references
|
160
|
+
- ⚙️ [Community Support](https://community.datocms.com/) - Get help from our team and community
|
161
|
+
- 🆕 [Changelog](https://www.datocms.com/product-updates) - Latest features and improvements
|
162
|
+
|
163
|
+
**Official Libraries:**
|
164
|
+
|
165
|
+
- [**Content Delivery Client**](https://github.com/datocms/cda-client) - TypeScript GraphQL client for content fetching
|
166
|
+
- [**REST API Clients**](https://github.com/datocms/js-rest-api-clients) - Node.js/Browser clients for content management
|
167
|
+
- [**CLI Tools**](https://github.com/datocms/cli) - Command-line utilities for schema migrations (includes [Contentful](https://github.com/datocms/cli/tree/main/packages/cli-plugin-contentful) and [WordPress](https://github.com/datocms/cli/tree/main/packages/cli-plugin-wordpress) importers)
|
168
|
+
|
169
|
+
**Official Framework Integrations**
|
170
|
+
|
171
|
+
Helpers to manage SEO, images, video and Structured Text coming from your DatoCMS projects:
|
172
|
+
|
173
|
+
- [**React Components**](https://github.com/datocms/react-datocms)
|
174
|
+
- [**Vue Components**](https://github.com/datocms/vue-datocms)
|
175
|
+
- [**Svelte Components**](https://github.com/datocms/datocms-svelte)
|
176
|
+
- [**Astro Components**](https://github.com/datocms/astro-datocms)
|
177
|
+
|
178
|
+
**Additional Resources:**
|
179
|
+
|
180
|
+
- [**Plugin Examples**](https://github.com/datocms/plugins) - Example plugins we've made that extend the editor/admin dashboard
|
181
|
+
- [**Starter Projects**](https://www.datocms.com/marketplace/starters) - Example website implementations for popular frameworks
|
182
|
+
- [**All Public Repositories**](https://github.com/orgs/datocms/repositories?q=&type=public&language=&sort=stargazers)
|
183
|
+
|
184
|
+
<!--datocms-autoinclude-footer end-->
|
@@ -67,14 +67,20 @@ encoded_channel encode_channel(double *channel, uint8_t nx, uint8_t ny, uint8_t
|
|
67
67
|
return (encoded_channel){dc, ac, ac_length, scale};
|
68
68
|
}
|
69
69
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
70
|
+
uint8_t write_varying_factors(double *ac, int ac_size, uint8_t *thumbhash, uint8_t *ac_index)
|
71
|
+
{
|
72
|
+
uint8_t index;
|
73
|
+
|
74
|
+
for (int i = 0; i < ac_size; i++)
|
75
|
+
{
|
76
|
+
index = (*ac_index) >> 1;
|
77
|
+
thumbhash[index] |= (uint8_t)roundf(15.0 * ac[i]) << (((*ac_index)++ & 1) << 2);
|
74
78
|
}
|
79
|
+
|
80
|
+
return index;
|
75
81
|
}
|
76
82
|
|
77
|
-
|
83
|
+
uint8_t rgba_to_thumbhash(uint8_t w, uint8_t h, uint8_t *rgba, uint8_t *thumbhash)
|
78
84
|
{
|
79
85
|
assert(w <= 100);
|
80
86
|
assert(h <= 100);
|
@@ -134,8 +140,8 @@ void rgba_to_thumbhash(uint8_t w, uint8_t h, uint8_t *rgba, uint8_t *thumbhash)
|
|
134
140
|
|
135
141
|
// Write the constants
|
136
142
|
bool is_landscape = w > h;
|
137
|
-
|
138
|
-
|
143
|
+
uint32_t header24 = (uint32_t)roundf(63.0 * el.dc) | ((uint32_t)roundf(31.5 + 31.5 * ep.dc) << 6) | ((uint32_t)roundf(31.5 + 31.5 * eq.dc) << 12) | ((uint32_t)roundf(31.0 * el.scale) << 18) | (has_alpha << 23);
|
144
|
+
uint32_t header16 = (is_landscape ? ly : lx) | ((uint16_t)roundf(63.0 * ep.scale) << 3) | ((uint16_t)roundf(63.0 * eq.scale) << 9) | (is_landscape << 15);
|
139
145
|
|
140
146
|
thumbhash[0] = (uint8_t)(header24 & 0xff);
|
141
147
|
thumbhash[1] = (uint8_t)((header24 >> 8) & 0xff);
|
@@ -143,28 +149,44 @@ void rgba_to_thumbhash(uint8_t w, uint8_t h, uint8_t *rgba, uint8_t *thumbhash)
|
|
143
149
|
thumbhash[3] = (uint8_t)(header16 & 0xff);
|
144
150
|
thumbhash[4] = (uint8_t)(header16 >> 8);
|
145
151
|
|
146
|
-
if (has_alpha)
|
147
|
-
|
152
|
+
if (has_alpha)
|
153
|
+
{
|
154
|
+
thumbhash[5] = (uint8_t)roundf(15.0 * ea.dc) | ((uint8_t)roundf(15.0 * ea.scale) << 4);
|
148
155
|
}
|
149
156
|
|
150
157
|
uint8_t ac_start = has_alpha ? 6 : 5;
|
151
158
|
uint8_t ac_index = 0;
|
152
159
|
|
153
|
-
|
154
|
-
|
155
|
-
write_varying_factors(
|
160
|
+
uint8_t max_written_indexes[] = {0, 0, 0, 0};
|
161
|
+
|
162
|
+
max_written_indexes[0] = write_varying_factors(el.ac, el.ac_size, thumbhash + ac_start, &ac_index);
|
163
|
+
max_written_indexes[1] = write_varying_factors(ep.ac, ep.ac_size, thumbhash + ac_start, &ac_index);
|
164
|
+
max_written_indexes[2] = write_varying_factors(eq.ac, eq.ac_size, thumbhash + ac_start, &ac_index);
|
156
165
|
|
157
|
-
if (has_alpha)
|
158
|
-
|
166
|
+
if (has_alpha)
|
167
|
+
{
|
168
|
+
max_written_indexes[3] = write_varying_factors(ea.ac, ea.ac_size, thumbhash + ac_start, &ac_index);
|
159
169
|
}
|
160
170
|
|
161
171
|
free(el.ac);
|
162
172
|
free(ep.ac);
|
163
173
|
free(eq.ac);
|
164
174
|
|
165
|
-
if (has_alpha)
|
175
|
+
if (has_alpha)
|
176
|
+
{
|
166
177
|
free(ea.ac);
|
167
178
|
}
|
179
|
+
|
180
|
+
// store the largest number at max_written_indexes[0]
|
181
|
+
for (int i = 1; i < 4; ++i)
|
182
|
+
{
|
183
|
+
if (max_written_indexes[0] < max_written_indexes[i])
|
184
|
+
{
|
185
|
+
max_written_indexes[0] = max_written_indexes[i];
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
return max_written_indexes[0] + ac_start + 1;
|
168
190
|
}
|
169
191
|
|
170
192
|
double *decode_channel(uint8_t nx, uint8_t ny, double scale, uint8_t *hash, uint8_t ac_start, uint8_t *ac_index)
|
@@ -321,19 +343,18 @@ void hsv2rgb(float *hsv, uint8_t *rgb)
|
|
321
343
|
* Decodes a ThumbHash to an RGBA image. RGB is not be premultiplied by A.
|
322
344
|
*/
|
323
345
|
void thumbhash_to_rgba(
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
)
|
346
|
+
uint8_t *hash,
|
347
|
+
uint8_t w,
|
348
|
+
uint8_t h,
|
349
|
+
enum FillMode fill_mode,
|
350
|
+
uint8_t *fill_color,
|
351
|
+
double *homogeneous_transform,
|
352
|
+
int saturation,
|
353
|
+
uint8_t *rgba)
|
333
354
|
{
|
334
355
|
// Read the constants
|
335
|
-
|
336
|
-
|
356
|
+
uint32_t header24 = hash[0] | (hash[1] << 8) | (hash[2] << 16);
|
357
|
+
uint32_t header16 = hash[3] | (hash[4] << 8);
|
337
358
|
double l_dc = (double)(header24 & 63) / 63;
|
338
359
|
double p_dc = (double)((header24 >> 6) & 63) / 31.5 - 1;
|
339
360
|
double q_dc = (double)((header24 >> 12) & 63) / 31.5 - 1;
|
@@ -343,7 +364,8 @@ void thumbhash_to_rgba(
|
|
343
364
|
double q_scale = (double)((header16 >> 9) & 63) / 63;
|
344
365
|
bool is_landscape = (header16 >> 15) != 0;
|
345
366
|
uint8_t lx = fmax(3, is_landscape ? has_alpha ? 5 : 7 : header16 & 7);
|
346
|
-
uint8_t ly = fmax(3, is_landscape ? header16 & 7 : has_alpha ? 5
|
367
|
+
uint8_t ly = fmax(3, is_landscape ? header16 & 7 : has_alpha ? 5
|
368
|
+
: 7);
|
347
369
|
double a_dc = (double)has_alpha ? (hash[5] & 15) / 15 : 1;
|
348
370
|
double a_scale = (double)(hash[5] >> 4) / 15;
|
349
371
|
|
@@ -359,7 +381,7 @@ void thumbhash_to_rgba(
|
|
359
381
|
// Decode using the DCT into RGB
|
360
382
|
double fx[7];
|
361
383
|
double fy[7];
|
362
|
-
|
384
|
+
uint32_t i = 0;
|
363
385
|
|
364
386
|
for (uint8_t ry = 0; ry < h; ry++)
|
365
387
|
{
|
@@ -380,7 +402,8 @@ void thumbhash_to_rgba(
|
|
380
402
|
|
381
403
|
double r, g, b, a;
|
382
404
|
|
383
|
-
if (fill_mode == CLAMP || fill_mode == BLUR)
|
405
|
+
if (fill_mode == CLAMP || fill_mode == BLUR)
|
406
|
+
{
|
384
407
|
if (x < 0)
|
385
408
|
{
|
386
409
|
x = 0;
|
@@ -401,7 +424,8 @@ void thumbhash_to_rgba(
|
|
401
424
|
|
402
425
|
bool inside_image = x >= 0 && x <= 1.0 && y >= 0 && y <= 1.0;
|
403
426
|
|
404
|
-
if (inside_image)
|
427
|
+
if (inside_image)
|
428
|
+
{
|
405
429
|
double l = l_dc, p = p_dc, q = q_dc;
|
406
430
|
a = a_dc;
|
407
431
|
|
@@ -455,7 +479,9 @@ void thumbhash_to_rgba(
|
|
455
479
|
b = l - 2.0 / 3.0 * p;
|
456
480
|
r = (3.0 * l - b + q) / 2.0;
|
457
481
|
g = r - q;
|
458
|
-
}
|
482
|
+
}
|
483
|
+
else
|
484
|
+
{
|
459
485
|
r = 255;
|
460
486
|
g = 255;
|
461
487
|
b = 255;
|
@@ -463,35 +489,37 @@ void thumbhash_to_rgba(
|
|
463
489
|
}
|
464
490
|
|
465
491
|
uint_fast8_t top[4] = {
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
double top_a = (double)
|
474
|
-
double fill_color_a = (double)
|
492
|
+
fmax(0, 255 * fmin(1, r)),
|
493
|
+
fmax(0, 255 * fmin(1, g)),
|
494
|
+
fmax(0, 255 * fmin(1, b)),
|
495
|
+
fmax(0, 255 * fmin(1, a))};
|
496
|
+
|
497
|
+
if (fill_color && fill_color[3] > 0)
|
498
|
+
{
|
499
|
+
double top_a = (double)top[3] / 255.0;
|
500
|
+
double fill_color_a = (double)fill_color[3] / 255.0;
|
475
501
|
double inverse_top_a = 1.0 - top_a;
|
476
502
|
double sum_a = top_a + fill_color_a * inverse_top_a;
|
477
503
|
|
478
504
|
// Alpha compositing (top over fill_color)
|
479
|
-
rgba[i]
|
480
|
-
rgba[i+1] = roundf(((double)
|
481
|
-
rgba[i+2] = roundf(((double)
|
482
|
-
rgba[i+3] = roundf(sum_a * 255.0);
|
483
|
-
}
|
484
|
-
|
485
|
-
|
486
|
-
rgba[i
|
487
|
-
rgba[i+
|
505
|
+
rgba[i] = roundf(((double)top[0] * top_a + (double)fill_color[0] * fill_color_a * inverse_top_a) / sum_a);
|
506
|
+
rgba[i + 1] = roundf(((double)top[1] * top_a + (double)fill_color[1] * fill_color_a * inverse_top_a) / sum_a);
|
507
|
+
rgba[i + 2] = roundf(((double)top[2] * top_a + (double)fill_color[2] * fill_color_a * inverse_top_a) / sum_a);
|
508
|
+
rgba[i + 3] = roundf(sum_a * 255.0);
|
509
|
+
}
|
510
|
+
else
|
511
|
+
{
|
512
|
+
rgba[i] = top[0];
|
513
|
+
rgba[i + 1] = top[1];
|
514
|
+
rgba[i + 2] = top[2];
|
515
|
+
rgba[i + 3] = top[3];
|
488
516
|
}
|
489
517
|
|
490
518
|
if (saturation)
|
491
519
|
{
|
492
520
|
float hsv[3] = {0};
|
493
521
|
rgb2hsv(rgba + i, hsv);
|
494
|
-
float mult = ((float)
|
522
|
+
float mult = ((float)saturation + 100.0f) / 200.0f * 1.4f;
|
495
523
|
hsv[1] = fminf(fmaxf(hsv[1] * mult, 0), 1.0f);
|
496
524
|
hsv2rgb(hsv, rgba + i);
|
497
525
|
}
|
@@ -3,34 +3,32 @@
|
|
3
3
|
|
4
4
|
#include <stdint.h>
|
5
5
|
|
6
|
-
enum FillMode
|
6
|
+
enum FillMode
|
7
|
+
{
|
7
8
|
SOLID = 0,
|
8
9
|
BLUR = 1,
|
9
10
|
CLAMP = 2,
|
10
11
|
};
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
);
|
13
|
+
uint8_t rgba_to_thumbhash(
|
14
|
+
uint8_t w,
|
15
|
+
uint8_t h,
|
16
|
+
uint8_t *rgba,
|
17
|
+
uint8_t *thumbhash);
|
18
18
|
|
19
19
|
void thumb_size(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
);
|
20
|
+
uint8_t *hash,
|
21
|
+
uint8_t max_size,
|
22
|
+
uint8_t *size);
|
24
23
|
|
25
24
|
void thumbhash_to_rgba(
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
);
|
25
|
+
uint8_t *hash,
|
26
|
+
uint8_t w,
|
27
|
+
uint8_t h,
|
28
|
+
enum FillMode fill_mode,
|
29
|
+
uint8_t *fill_color,
|
30
|
+
double *homogeneous_transform,
|
31
|
+
int saturation,
|
32
|
+
uint8_t *rgba);
|
35
33
|
|
36
34
|
#endif
|
data/lib/fast_thumbhash.rb
CHANGED
@@ -155,10 +155,9 @@ module FastThumbhash
|
|
155
155
|
|
156
156
|
thumbhash_pointer = FFI::MemoryPointer.new(:uint8, 25)
|
157
157
|
|
158
|
-
Library.rgba_to_thumbhash(width, height, rgba_pointer, thumbhash_pointer)
|
158
|
+
length = Library.rgba_to_thumbhash(width, height, rgba_pointer, thumbhash_pointer)
|
159
159
|
|
160
|
-
result = thumbhash_pointer.read_array_of_uint8(
|
161
|
-
result.pop while result.last.zero?
|
160
|
+
result = thumbhash_pointer.read_array_of_uint8(length)
|
162
161
|
|
163
162
|
result.pack("C*")
|
164
163
|
ensure
|
@@ -178,6 +177,6 @@ module FastThumbhash
|
|
178
177
|
|
179
178
|
attach_function :thumb_size, %i[pointer uint8 pointer], :size_t
|
180
179
|
attach_function :thumbhash_to_rgba, %i[pointer uint8 uint8 fill_mode pointer pointer int pointer], :void
|
181
|
-
attach_function :rgba_to_thumbhash, %i[uint8 uint8 pointer pointer], :
|
180
|
+
attach_function :rgba_to_thumbhash, %i[uint8 uint8 pointer pointer], :uint8
|
182
181
|
end
|
183
182
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_thumbhash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stefano Verna
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -35,6 +35,7 @@ extra_rdoc_files: []
|
|
35
35
|
files:
|
36
36
|
- ".rspec"
|
37
37
|
- ".rubocop.yml"
|
38
|
+
- ".ruby-version"
|
38
39
|
- CODE_OF_CONDUCT.md
|
39
40
|
- Gemfile
|
40
41
|
- Gemfile.lock
|