shrine-blurhash 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +128 -0
- data/lib/shrine/plugins/blurhash.rb +147 -0
- data/shrine-blurhash.gemspec +24 -0
- metadata +116 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d996837d159a23efc83e220f7f937ad440bf3acfe98093f7483b4e08de221091
|
4
|
+
data.tar.gz: d956efac6992fff592f1b37c46fbbb9bdfe2b09ef5dfb3414feee57eee9adc5b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 32e9956e147d75ab3bb218c58e14dd46eeb4878a9d91812e064d17cb23d7210aebce352280f64b775849231a0d40a032b3f82418dd16d6f83be7c68da71976bc
|
7
|
+
data.tar.gz: 27c0e803e7f930c70629ec1a70a6aa256c05eea6f6fbf65cccc9e348f78d7027555d85d5e72d03d5409c2f23f7826fc3f2fd0891cc1a48f419948a6f4b2171ef
|
data/README.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/shrine-blurhash.svg)](https://badge.fury.io/rb/shrine-blurhash)
|
2
|
+
|
3
|
+
# Shrine::Blurhash
|
4
|
+
|
5
|
+
Provides [Blurhash] computing for [Shrine].
|
6
|
+
|
7
|
+
Main features:
|
8
|
+
- Multiple pixel extractors support. Current implementations:
|
9
|
+
- `ruby-vips`
|
10
|
+
- Resize the image before computing blurhash (to 100 pixels by default) for a faster computation
|
11
|
+
- Supports custom blurhash components parameter
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Simply add the gem to your `Gemfile`:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem "shrine-blurhash"
|
19
|
+
```
|
20
|
+
|
21
|
+
Then run `bundler`
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
You first need to load the plugin in your Uploader;
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
class PictureUploader < Shrine
|
29
|
+
plugin :blurhash
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
Blurhash will be now be computed when you upload a file and stored in a `blurhash` metadata, with a `.blurhash` method for quick access.
|
34
|
+
|
35
|
+
## Options
|
36
|
+
|
37
|
+
You can pass options to the plugin to customize its behavior.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
class PictureUploader < Shrine
|
41
|
+
plugin :blurhash, resize_to: nil, components: [2, 2]
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
### ```components```
|
46
|
+
|
47
|
+
Type: `[components_x, components_y]`
|
48
|
+
Default: `[4, 3]`
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
plugin :blurhash, components: [2, 2]
|
52
|
+
```
|
53
|
+
|
54
|
+
This allows you to customize the number of components on each axis for the Blurhash algorithm. The visual result will look like a grid of `X * Y` blurred colors.
|
55
|
+
|
56
|
+
### `resize_to`
|
57
|
+
|
58
|
+
Type: `Integer` or `nil`
|
59
|
+
Default: `100`
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
plugin :blurhash, resize_to: 100
|
63
|
+
plugin :blurhash, resize_to: nil
|
64
|
+
```
|
65
|
+
|
66
|
+
Before computing the Blurhash for an image, this plugin needs to extract every pixel and store them in an array. This can result in a lot of memory allocations. To avoid using too much memory, this plugin resizes the image before extracting the pixels. This should not affect the visual result.
|
67
|
+
|
68
|
+
You can either specify the target size or `nil` to disable image resizing completely.
|
69
|
+
|
70
|
+
### `extractor`
|
71
|
+
|
72
|
+
Type: `symbol` or `lambda`
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
plugin :blurhash, extractor: :ruby-vips
|
76
|
+
```
|
77
|
+
|
78
|
+
Supported extractors:
|
79
|
+
|
80
|
+
| Name | Description |
|
81
|
+
| :----------- | :----------- |
|
82
|
+
| `:ruby_vips` | Uses the [ruby-vips] gem to extract pixels from File objects. If non-file IO object is given it will be temporarily downloaded to disk. |
|
83
|
+
|
84
|
+
You can also create your own custom pixel extractor, where you can reuse
|
85
|
+
any of the built-in analyzers. The analyzer is a lambda that accepts an IO
|
86
|
+
object and returns a `[width, height, pixels]` array containing image width, image height and all the pixels in the images in an array, or `nil` if pixels could not be extracted.
|
87
|
+
|
88
|
+
### `auto_extraction`
|
89
|
+
|
90
|
+
Type: `boolean`
|
91
|
+
|
92
|
+
- Multiple pixel extractors support, even if only VIPS is implemented right now
|
93
|
+
- Allows to resize the image before computing blurhash (to 100 pixels by default) for a faster computation
|
94
|
+
- Supports custom blurhash components parameter
|
95
|
+
|
96
|
+
## Errors
|
97
|
+
|
98
|
+
By default, any exceptions that the plugin raises while computing blurhash
|
99
|
+
will be caught and a warning will be printed out. This allows you to have the
|
100
|
+
plugin loaded even for files that are not images.
|
101
|
+
|
102
|
+
However, you can choose different strategies for handling these exceptions:
|
103
|
+
|
104
|
+
```rb
|
105
|
+
plugin :blurhash, on_error: :warn # prints a warning (default)
|
106
|
+
plugin :blurhash, on_error: :fail # raises the exception
|
107
|
+
plugin :blurhash, on_error: :ignore # ignores exceptions
|
108
|
+
plugin :blurhash, on_error: -> (error) { # custom handler
|
109
|
+
# report the exception to your exception handler
|
110
|
+
}
|
111
|
+
```
|
112
|
+
|
113
|
+
## Contributing
|
114
|
+
|
115
|
+
### Running tests
|
116
|
+
|
117
|
+
After setting up your bucket, run the tests:
|
118
|
+
|
119
|
+
```sh
|
120
|
+
$ bundle exec rake test
|
121
|
+
```
|
122
|
+
|
123
|
+
## License
|
124
|
+
|
125
|
+
[MIT](http://opensource.org/licenses/MIT)
|
126
|
+
|
127
|
+
[Shrine]: https://github.com/shrinerb/shrine
|
128
|
+
[Blurhash]: https://blurha.sh
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "blurhash"
|
4
|
+
|
5
|
+
class Shrine
|
6
|
+
module Plugins
|
7
|
+
module Blurhash
|
8
|
+
LOG_SUBSCRIBER = lambda do |event|
|
9
|
+
Shrine.logger.info "Blurhash (#{event.duration}ms) – #{{
|
10
|
+
io: event[:io].class,
|
11
|
+
uploader: event[:uploader],
|
12
|
+
}.inspect}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.configure(uploader, log_subscriber: LOG_SUBSCRIBER, **opts)
|
16
|
+
uploader.opts[:blurhash] ||= {
|
17
|
+
extractor: :ruby_vips,
|
18
|
+
on_error: :warn,
|
19
|
+
auto_extraction: true,
|
20
|
+
resize_to: 100,
|
21
|
+
components: [4, 3],
|
22
|
+
}
|
23
|
+
uploader.opts[:blurhash].merge!(opts)
|
24
|
+
|
25
|
+
# resolve error strategy
|
26
|
+
uploader.opts[:blurhash][:on_error] =
|
27
|
+
case uploader.opts[:blurhash][:on_error]
|
28
|
+
when :fail then ->(error) { raise error }
|
29
|
+
when :warn then ->(error) { Shrine.warn "Error occurred when attempting to extract blurhash: #{error.inspect}" }
|
30
|
+
when :ignore then ->(error) {}
|
31
|
+
else
|
32
|
+
uploader.opts[:blurhash][:on_error]
|
33
|
+
end
|
34
|
+
|
35
|
+
uploader.subscribe(:blurhash, &log_subscriber) if uploader.respond_to?(:subscribe)
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
def compute_blurhash(io)
|
40
|
+
extractor = opts[:blurhash][:extractor]
|
41
|
+
extractor = pixels_extractor(extractor) if extractor.is_a?(Symbol)
|
42
|
+
|
43
|
+
resize_to = opts[:blurhash][:resize_to]
|
44
|
+
args = [io, resize_to, pixels_extractors].take(extractor.arity.abs)
|
45
|
+
|
46
|
+
blurhash = instrument_blurhash(io) do
|
47
|
+
pixels = extractor.call(*args)
|
48
|
+
|
49
|
+
components = opts[:blurhash][:components]
|
50
|
+
::Blurhash.encode(pixels[:width], pixels[:height], pixels[:pixels], x_comp: components[0], y_comp: components[1])
|
51
|
+
end
|
52
|
+
|
53
|
+
io.rewind
|
54
|
+
|
55
|
+
blurhash
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns a hash of built-in pixels extractors, where keys are
|
59
|
+
# extractors names and values are `#call`-able objects which accepts the
|
60
|
+
# IO object.
|
61
|
+
def pixels_extractors
|
62
|
+
@pixels_extractors ||= PixelsExtractor::SUPPORTED_EXTRACTORS.inject({}) do |hash, tool|
|
63
|
+
hash.merge!(tool => pixels_extractor(tool))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns callable pixels extractor object.
|
68
|
+
def pixels_extractor(name)
|
69
|
+
on_error = opts[:blurhash][:on_error]
|
70
|
+
|
71
|
+
PixelsExtractor.new(name, on_error: on_error).method(:call)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Sends a `blurhash.shrine` event for instrumentation plugin.
|
77
|
+
def instrument_blurhash(io, &block)
|
78
|
+
return yield unless respond_to?(:instrument)
|
79
|
+
|
80
|
+
instrument(:blurhash, io: io, &block)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
module InstanceMethods
|
85
|
+
def extract_metadata(io, **options)
|
86
|
+
return super unless self.class.opts[:blurhash][:auto_extraction]
|
87
|
+
|
88
|
+
blurhash = self.class.compute_blurhash(io)
|
89
|
+
super.merge!("blurhash" => blurhash)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module FileMethods
|
94
|
+
def blurhash
|
95
|
+
metadata["blurhash"]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class PixelsExtractor
|
100
|
+
SUPPORTED_EXTRACTORS = [:ruby_vips].freeze
|
101
|
+
|
102
|
+
def initialize(tool, on_error: method(:fail))
|
103
|
+
unless SUPPORTED_EXTRACTORS.include?(tool)
|
104
|
+
raise Error, "unknown pixel extractor #{tool.inspect}, supported extractors are: #{SUPPORTED_EXTRACTORS.join(',')}"
|
105
|
+
end
|
106
|
+
|
107
|
+
@tool = tool
|
108
|
+
@on_error = on_error
|
109
|
+
end
|
110
|
+
|
111
|
+
def call(io, resize_to)
|
112
|
+
dimensions = send(:"extract_with_#{@tool}", io, resize_to)
|
113
|
+
io.rewind
|
114
|
+
dimensions
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def extract_with_ruby_vips(io, resize_to)
|
120
|
+
require "vips"
|
121
|
+
|
122
|
+
begin
|
123
|
+
Shrine.with_file(io) do |file|
|
124
|
+
image = Vips::Image.new_from_file(file.path, access: :sequential)
|
125
|
+
image = image.resize(resize_to.fdiv(image.width), vscale: resize_to.fdiv(image.height)) if resize_to
|
126
|
+
|
127
|
+
{
|
128
|
+
width: image.width,
|
129
|
+
height: image.height,
|
130
|
+
pixels: image.to_a.flatten,
|
131
|
+
}
|
132
|
+
end
|
133
|
+
rescue Vips::Error => e
|
134
|
+
on_error(e)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def on_error(error)
|
139
|
+
@on_error.call(error)
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
register_plugin(:blurhash, Blurhash)
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "shrine-blurhash"
|
5
|
+
gem.version = "0.0.1"
|
6
|
+
|
7
|
+
gem.required_ruby_version = ">= 2.5"
|
8
|
+
|
9
|
+
gem.summary = "Shrine plugin to compute Blurhash on image attachments"
|
10
|
+
gem.homepage = "https://github.com/renchap/shrine-blurhash"
|
11
|
+
gem.authors = ["Renaud Chaput"]
|
12
|
+
gem.email = ["renchap@gmail.com"]
|
13
|
+
gem.license = "MIT"
|
14
|
+
|
15
|
+
gem.files = Dir["README.md", "LICENSE.txt", "lib/**/*.rb", "shrine-blurhash.gemspec"]
|
16
|
+
gem.require_path = "lib"
|
17
|
+
|
18
|
+
gem.add_dependency "blurhash", "~> 0.1.4"
|
19
|
+
gem.add_dependency "shrine", "~> 3.0"
|
20
|
+
|
21
|
+
gem.add_development_dependency "minitest"
|
22
|
+
gem.add_development_dependency "rake"
|
23
|
+
gem.add_development_dependency "ruby-vips"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shrine-blurhash
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Renaud Chaput
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-05-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: blurhash
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: shrine
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ruby-vips
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- renchap@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- README.md
|
91
|
+
- lib/shrine/plugins/blurhash.rb
|
92
|
+
- shrine-blurhash.gemspec
|
93
|
+
homepage: https://github.com/renchap/shrine-blurhash
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
metadata: {}
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '2.5'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubygems_version: 3.0.3
|
113
|
+
signing_key:
|
114
|
+
specification_version: 4
|
115
|
+
summary: Shrine plugin to compute Blurhash on image attachments
|
116
|
+
test_files: []
|