fast_thumbhash 0.6.0 → 0.7.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3dd0e9e710dfa53775b6c718fd674fdc26fb0a17932058ca18e495b229d43e30
4
- data.tar.gz: dc6e6c4203596d64bb3fb7e84fa63b72dda92cd5b7612a97371cb4ae418ae2af
3
+ metadata.gz: '029bcedcfcff9aeabbd3d052759f165228798ba5a4599747866b80c0e7cb2267'
4
+ data.tar.gz: c0284c796f2541a68ba0ce5d5f6d39362abc487d443ea92fe34f1cae84e49da2
5
5
  SHA512:
6
- metadata.gz: 29c037a813de79ec9a791484823fc42291e2bbc6dd5c8c6a8ecc4d5e17b9de52dfcc64cd554372604bd8068cc5239e898b982f875e0f1efd62cac53967dabf05
7
- data.tar.gz: 2d0f21baac23f3c28e024d6755a45cdae2c9f4b1dd78243ccd98e6fa6e9b01520f1ff28f9c113d4ab0a9c2ecc0988a0fa801c141310d760337c8f1ec37cc002b
6
+ metadata.gz: 8305727fad3e3d55f31265f05f3bad5931f6a6cb90787e896a8aacde19d3fa5aa75c122a3d6a7a977b0f0703c95323556527401816641148f69c25001b5b30e9
7
+ data.tar.gz: 77e126d32164ec0531b0ee450a950e59e7f872ca18d2fbb46e5544171f35f44066835817fb0f6cccce2625e806e5af9a280c238e7bd104de2125065e6b6e3cb8
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.7.8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fast_thumbhash (0.6.0)
4
+ fast_thumbhash (0.7.1)
5
5
  ffi
6
6
 
7
7
  GEM
@@ -52,6 +52,7 @@ GEM
52
52
 
53
53
  PLATFORMS
54
54
  arm64-darwin-21
55
+ arm64-darwin-22
55
56
  x86_64-darwin-18
56
57
  x86_64-darwin-20
57
58
  x86_64-darwin-21
@@ -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
- void write_varying_factors(double *ac, int ac_size, uint8_t *thumbhash, uint8_t *ac_index) {
71
- for (int i = 0; i < ac_size; i++) {
72
- uint8_t index = (*ac_index) >> 1;
73
- thumbhash[index] |= (uint8_t) roundf(15.0 * ac[i]) << (((*ac_index)++ & 1) << 2);
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
- void rgba_to_thumbhash(uint8_t w, uint8_t h, uint8_t *rgba, uint8_t *thumbhash)
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);
@@ -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
- thumbhash[5] = (uint8_t) roundf(15.0 * ea.dc) | ((uint8_t) roundf(15.0 * ea.scale) << 4);
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
- write_varying_factors(el.ac, el.ac_size, thumbhash + ac_start, &ac_index);
154
- write_varying_factors(ep.ac, ep.ac_size, thumbhash + ac_start, &ac_index);
155
- write_varying_factors(eq.ac, eq.ac_size, thumbhash + ac_start, &ac_index);
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
- write_varying_factors(ea.ac, ea.ac_size, thumbhash + ac_start, &ac_index);
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,15 +343,14 @@ 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
- uint8_t *hash,
325
- uint8_t w,
326
- uint8_t h,
327
- enum FillMode fill_mode,
328
- uint8_t *fill_color,
329
- double *homogeneous_transform,
330
- int saturation,
331
- uint8_t *rgba
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
356
  u_int32_t header24 = hash[0] | (hash[1] << 8) | (hash[2] << 16);
@@ -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 : 7);
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
 
@@ -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
- } else {
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
- fmax(0, 255 * fmin(1, r)),
467
- fmax(0, 255 * fmin(1, g)),
468
- fmax(0, 255 * fmin(1, b)),
469
- fmax(0, 255 * fmin(1, a))
470
- };
471
-
472
- if (fill_color && fill_color[3] > 0) {
473
- double top_a = (double) top[3] / 255.0;
474
- double fill_color_a = (double) fill_color[3] / 255.0;
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] = roundf(((double) top[0] * top_a + (double) fill_color[0] * fill_color_a * inverse_top_a) / sum_a);
480
- rgba[i+1] = roundf(((double) top[1] * top_a + (double) fill_color[1] * fill_color_a * inverse_top_a) / sum_a);
481
- rgba[i+2] = roundf(((double) top[2] * top_a + (double) fill_color[2] * fill_color_a * inverse_top_a) / sum_a);
482
- rgba[i+3] = roundf(sum_a * 255.0);
483
- } else {
484
- rgba[i] = top[0];
485
- rgba[i+1] = top[1];
486
- rgba[i+2] = top[2];
487
- rgba[i+3] = top[3];
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) saturation + 100.0f) / 200.0f * 1.4f;
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
- void rgba_to_thumbhash(
13
- uint8_t w,
14
- uint8_t h,
15
- uint8_t *rgba,
16
- uint8_t *thumbhash
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
- uint8_t *hash,
21
- uint8_t max_size,
22
- uint8_t *size
23
- );
20
+ uint8_t *hash,
21
+ uint8_t max_size,
22
+ uint8_t *size);
24
23
 
25
24
  void thumbhash_to_rgba(
26
- uint8_t *hash,
27
- uint8_t w,
28
- uint8_t h,
29
- enum FillMode fill_mode,
30
- uint8_t *fill_color,
31
- double *homogeneous_transform,
32
- int saturation,
33
- uint8_t *rgba
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FastThumbhash
4
- VERSION = "0.6.0"
4
+ VERSION = "0.7.1"
5
5
  end
@@ -33,29 +33,66 @@ module FastThumbhash
33
33
  homogeneous_transform: nil,
34
34
  saturation: 0
35
35
  )
36
+ %i[solid blur clamp].include?(fill_mode) or
37
+ raise ArgumentError, "Invalid `fill_mode` option"
38
+
39
+ if fill_color
40
+ fill_color.length == 4 or
41
+ raise ArgumentError, "You need to pass [r, g, b, a] to the `fill_color` option"
42
+ end
43
+
44
+ raise ArgumentError, "Option `fill_color` is required for :solid fill_mode" if fill_mode == :solid && fill_color.nil?
45
+
46
+ if homogeneous_transform
47
+ (homogeneous_transform.size == 3 && homogeneous_transform.all? { |row| row.size == 3 }) or
48
+ raise ArgumentError, "`homogeneous_transform` option must be a 3x3 matrix"
49
+ end
50
+
51
+ if size
52
+ size.length == 2 or
53
+ raise ArgumentError, "You need to pass [width, height] to the `size` option"
54
+
55
+ size.all? { |dimension| dimension < 100 } or
56
+ raise ArgumentError, "Cannot generate images bigger then 100 pixels"
57
+ end
58
+
59
+ if max_size
60
+ max_size <= 100 or
61
+ raise ArgumentError, "Cannot generate images bigger then 100 pixels"
62
+ end
63
+
36
64
  !max_size.nil? ^ !size.nil? or
37
65
  raise ArgumentError, "Pass either the `max_size` option, or an explicit `size`"
38
66
 
39
- %i[solid blur clamp].include?(fill_mode) or
40
- raise ArgumentError, "Invalid `fill_mode` option"
67
+ binary_thumbhash_to_rgba!(
68
+ binary_thumbhash,
69
+ max_size: max_size,
70
+ size: size,
71
+ fill_mode: fill_mode,
72
+ fill_color: fill_color,
73
+ homogeneous_transform: homogeneous_transform,
74
+ saturation: saturation
75
+ )
76
+ end
41
77
 
78
+ def self.binary_thumbhash_to_rgba!(
79
+ binary_thumbhash,
80
+ max_size: nil,
81
+ size: nil,
82
+ fill_mode: :solid,
83
+ fill_color: [255, 255, 255, 0],
84
+ homogeneous_transform: nil,
85
+ saturation: 0
86
+ )
42
87
  fill_color_pointer =
43
88
  if fill_color
44
- fill_color.length == 4 or
45
- raise ArgumentError, "You need to pass [r, g, b, a] to the `fill_color` option"
46
-
47
89
  FFI::MemoryPointer.new(:uint8, 4).tap do |p|
48
90
  p.write_array_of_uint8(fill_color)
49
91
  end
50
92
  end
51
93
 
52
- raise ArgumentError, "Option `fill_color` is required for :solid fill_mode" if fill_mode == :solid && fill_color.nil?
53
-
54
94
  transform_pointer =
55
95
  if homogeneous_transform
56
- (homogeneous_transform.size == 3 && homogeneous_transform.all? { |row| row.size == 3 }) or
57
- raise ArgumentError, "`homogeneous_transform` option must be a 3x3 matrix"
58
-
59
96
  FFI::MemoryPointer.new(:double, 6).tap do |p|
60
97
  p.write_array_of_double(
61
98
  [
@@ -75,17 +112,8 @@ module FastThumbhash
75
112
 
76
113
  width, height =
77
114
  if size
78
- size.length == 2 or
79
- raise ArgumentError, "You need to pass [width, height] to the `size` option"
80
-
81
- size.all? { |dimension| dimension < 100 } or
82
- raise ArgumentError, "Cannot generate images bigger then 100 pixels"
83
-
84
115
  size
85
116
  else
86
- max_size <= 100 or
87
- raise ArgumentError, "Cannot generate images bigger then 100 pixels"
88
-
89
117
  thumb_size_pointer = FFI::MemoryPointer.new(:uint8, 2)
90
118
  Library.thumb_size(thumbhash_pointer, max_size, thumb_size_pointer)
91
119
  thumb_size_pointer.read_array_of_uint8(2)
@@ -93,6 +121,7 @@ module FastThumbhash
93
121
 
94
122
  rgba_size = width * height * 4
95
123
  rgba_pointer = FFI::MemoryPointer.new(:uint8, rgba_size)
124
+
96
125
  Library.thumbhash_to_rgba(
97
126
  thumbhash_pointer,
98
127
  width,
@@ -100,7 +129,7 @@ module FastThumbhash
100
129
  fill_mode.to_sym,
101
130
  fill_color_pointer,
102
131
  transform_pointer,
103
- saturation,
132
+ saturation.clamp(-100, 100),
104
133
  rgba_pointer
105
134
  )
106
135
 
@@ -126,10 +155,9 @@ module FastThumbhash
126
155
 
127
156
  thumbhash_pointer = FFI::MemoryPointer.new(:uint8, 25)
128
157
 
129
- Library.rgba_to_thumbhash(width, height, rgba_pointer, thumbhash_pointer)
158
+ length = Library.rgba_to_thumbhash(width, height, rgba_pointer, thumbhash_pointer)
130
159
 
131
- result = thumbhash_pointer.read_array_of_uint8(25)
132
- result.pop while result.last.zero?
160
+ result = thumbhash_pointer.read_array_of_uint8(length)
133
161
 
134
162
  result.pack("C*")
135
163
  ensure
@@ -149,6 +177,6 @@ module FastThumbhash
149
177
 
150
178
  attach_function :thumb_size, %i[pointer uint8 pointer], :size_t
151
179
  attach_function :thumbhash_to_rgba, %i[pointer uint8 uint8 fill_mode pointer pointer int pointer], :void
152
- attach_function :rgba_to_thumbhash, %i[uint8 uint8 pointer pointer], :void
180
+ attach_function :rgba_to_thumbhash, %i[uint8 uint8 pointer pointer], :uint8
153
181
  end
154
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.6.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefano Verna
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-04-07 00:00:00.000000000 Z
11
+ date: 2023-11-17 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