foggy-mirror 1.0.1 → 1.2.0

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: fd9dc0797613dd9564889a8ee92f972656d40a631bc8218f21b92ddda1d9745c
4
- data.tar.gz: 7209f57bce3182e42a806c5049f2a78903e184ba72e076440275a2ff96ab95b9
3
+ metadata.gz: bb318a75bf83a7eaa2e49ce6bad117aa28b994525ad3f2fd5917d132d9fdf9c4
4
+ data.tar.gz: 4a426b98c5eb87c053493eeea40bd138908c609681aefbab48c2b784c668231e
5
5
  SHA512:
6
- metadata.gz: ff5ff9df6a022ba4eef370d28b6316422328cd67c160104fe479d65a5087dea6b6c46341216b1e67bbd8c6e46c3e5d7bc6251fe1d26d703b1c735d0f9077b9b3
7
- data.tar.gz: 5db7ce87395d0846861a63b46c2ebb8639922b7cc6ea7808f0fc2dbb5fe37308f7fed281f513bf721a5702e5f1cb8613e29c89c69be55819b37c62f2099587bc
6
+ metadata.gz: 5980f61abdffaff14d23b7496aa1604f8735c389df2a995e39fdefb5f3646956d8190d25d4d99e740f2894a901ec9822042ee00d53667bb2fe64dbaa662b1b02
7
+ data.tar.gz: 492eb518d3102e18cdb7d43053a40a077a5bf89395ee17bccbe45afd0fe973974d1d04ca4a43ab8819aa6eadff200d9d8aaab9b5e83c0fbab612f8b8b6bca4f3
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ ## [1.2.0] - 2023-03-13
2
+
3
+ - Added embedded image through data-URI strategy to SVG exporter
4
+
5
+ ## [1.0.1] - 2022-03-11
6
+
7
+ - Initial release
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- foggy-mirror (1.0.1)
4
+ foggy-mirror (1.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -11,31 +11,31 @@ GEM
11
11
  diff-lcs (1.5.0)
12
12
  ffi (1.15.5)
13
13
  method_source (1.0.0)
14
- nokogiri (1.13.3-x86_64-darwin)
14
+ nokogiri (1.14.2-x86_64-darwin)
15
15
  racc (~> 1.4)
16
- nokogiri (1.13.3-x86_64-linux)
16
+ nokogiri (1.14.2-x86_64-linux)
17
17
  racc (~> 1.4)
18
- pry (0.13.1)
18
+ pry (0.14.2)
19
19
  coderay (~> 1.1)
20
20
  method_source (~> 1.0)
21
- pry-byebug (3.9.0)
21
+ pry-byebug (3.10.1)
22
22
  byebug (~> 11.0)
23
- pry (~> 0.13.0)
24
- racc (1.6.0)
23
+ pry (>= 0.13, < 0.15)
24
+ racc (1.6.2)
25
25
  rake (13.0.6)
26
- rspec (3.11.0)
27
- rspec-core (~> 3.11.0)
28
- rspec-expectations (~> 3.11.0)
29
- rspec-mocks (~> 3.11.0)
30
- rspec-core (3.11.0)
31
- rspec-support (~> 3.11.0)
32
- rspec-expectations (3.11.0)
26
+ rspec (3.12.0)
27
+ rspec-core (~> 3.12.0)
28
+ rspec-expectations (~> 3.12.0)
29
+ rspec-mocks (~> 3.12.0)
30
+ rspec-core (3.12.1)
31
+ rspec-support (~> 3.12.0)
32
+ rspec-expectations (3.12.2)
33
33
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.11.0)
35
- rspec-mocks (3.11.0)
34
+ rspec-support (~> 3.12.0)
35
+ rspec-mocks (3.12.4)
36
36
  diff-lcs (>= 1.2.0, < 2.0)
37
- rspec-support (~> 3.11.0)
38
- rspec-support (3.11.0)
37
+ rspec-support (~> 3.12.0)
38
+ rspec-support (3.12.0)
39
39
  ruby-vips (2.1.4)
40
40
  ffi (~> 1.12)
41
41
 
data/README.md CHANGED
@@ -5,17 +5,13 @@
5
5
  [![Powered by Beezwax](https://img.shields.io/badge/Powered%20By-Beezwax-gold?logo=data:image/svg+xml;charset=utf-8;base64,PHN2ZyB3aWR0aD0iMTA5IiBoZWlnaHQ9IjEwOSIgdmlld0JveD0iMCAwIDEwOSAxMDkiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+DQogICA8Zz4NCiAgICAgIDxwYXRoIGQ9Ik01MC44IDEwNi42MjVDNTkuMSA5OS4zMjUgNjEuOSA5MS42MjUgNjEuOSA5MS42MjVDNjMuMiA4Ny43MjUgNjQuNyA4NC41MjUgNjQuNyA2OS4zMjVDNjQuNyA0Ni40MjUgNTYuMiAzOC45MjUgNDcgMzIuMDI1QzQwLjggMjcuMzI1IDI0LjkgMjEuMTI1IDE1LjUgMTcuNjI1TDIuMjk5OTggMjUuMTI1QzEuNDk5OTggMjUuNzI1IDAuOTk5OTc2IDI2LjIyNSAwLjU5OTk3NiAyNi44MjVDMjQuNCAzMi4xMjUgNDEuNSA0OC4wMjUgNDEuNSA2Ni44MjVDNDEuNSA3OC4yMjUgMzUuMSA4OC42MjUgMjQuOSA5Ni4xMjVMNDQuNyAxMDcuNTI1QzQ1LjEgMTA3LjcyNSA0NiAxMDguMTI1IDQ3LjMgMTA4LjEyNUM0Ny45IDEwOC4xMjUgNDkgMTA3LjkyNSA1MC4zIDEwNi44MjVMNTAuOCAxMDYuNjI1WiIgZmlsbD0iI0ZGRDkzOSI+PC9wYXRoPg0KICAgICAgPHBhdGggZD0iTTIyLjkgMTMuMzI0OEw0NCAyMC40MjQ4QzY0LjYgMjcuNzI0OCA3My4yIDM3LjgyNDggNzMuMiAzNy44MjQ4Qzg0LjUgNDkuMTI0OCA4My4yIDYxLjYyNDggODMuMiA3Ni44MjQ4QzgzLjIgODAuNjI0OCA4Mi42IDg0LjkyNDggODEuNyA4OS4wMjQ4TDkzIDgyLjYyNDhDOTUuNiA4MS4xMjQ4IDk1LjYgNzcuOTI0OCA5NS42IDc3LjkyNDhWMjkuMzI0OEM5NS42IDI2LjEyNDggOTMgMjQuNjI0OCA5MyAyNC42MjQ4TDcyLjUgMTMuMjI0OEw1MC42IDAuNjI0ODQ1QzQ4LjEgLTAuNjc1MTU1IDQ1LjUgMC40MjQ4NDUgNDUuMyAwLjYyNDg0NUwyMy4xIDEzLjMyNDhIMjIuOVoiIGZpbGw9IiNGRkQ5MzkiPjwvcGF0aD4NCiAgICAgIDxwYXRoIGQ9Ik0wLjEgMzEuNTI0NFY3OC4yMjQ0QzAuMSA4MC44MjQ0IDIuMiA4Mi4zMjQ0IDIuNyA4Mi43MjQ0TDE0LjggODkuNjI0NEwxNy44IDkxLjMyNDRDMjQuNCA4NC43MjQ0IDI4LjQgNzYuMzI0NCAyOC41IDY3LjEyNDRDMjguNSA1MS41MjQ0IDE2LjggMzguMDI0NCAwIDMxLjUyNDRIMC4xWiIgZmlsbD0iI0ZGRDkzOSI+PC9wYXRoPg0KICAgPC9nPg0KPC9zdmc+)](https://beezwax.net/)
6
6
 
7
7
  foggy-mirror is a small Ruby tool that creates faux blurry versions of raster
8
- images using radial gradients, exported as either SVG or CSS (using
9
- `background-image`).
8
+ images using SVG or CSS, either through radial gradients or SVG blur filters,
9
+ with very minimal file sizes (usually under 1KB).
10
10
 
11
11
  This is useful as a poor man's replacement for CSS's `backdrop-filter: blur()`,
12
12
  as that CSS feature isn't fully supported by browsers, and sometimes you want
13
- an element with a "frosted glass" effect on top of a crispy background.
14
-
15
- Using gradients we can achieve very smooth and infinitely scalable graphics at
16
- very low file size (e.g. the example SVG below is only 814 bytes after gzip).
17
- If you need a less blurry version of the original picture, then a regular blur
18
- effect saved to JPEG/WebP may be a better value proposition.
13
+ an element with a "frosted glass" effect (also known as glassmorphism) on top
14
+ of a crispy background.
19
15
 
20
16
  Need Ruby/Rails consulting? Contact us at [Beezwax.net](https://beezwax.net/)
21
17
 
@@ -25,9 +21,13 @@ Original raster image:
25
21
 
26
22
  ![Photo by Marek Piwnicki (@marekpiwnicki) / Unsplash](/img/unsplash-sq.webp)
27
23
 
28
- SVG:
24
+ SVG with blur filter:
25
+
26
+ <img src="/img/unsplash.gradients.svg" alt="foggy-mirror SVG using gradients" width="500" height="500" />
27
+
28
+ SVG with radial gradients:
29
29
 
30
- <img src="/img/unsplash.svg" alt="foggy-mirror SVG" width="500" height="500" />
30
+ <img src="/img/unsplash.filter.svg" alt="foggy-mirror SVG using blur filter" width="500" height="500" />
31
31
 
32
32
  ## Installation
33
33
 
@@ -93,7 +93,9 @@ For a full list of options use `-h`:
93
93
  ```
94
94
  $ foggy-mirror -h
95
95
  Usage: foggy-mirror [options] [--] image_file ...
96
- --res=RESOLUTION The output resolution (how many radial gradients per dimension)
96
+ --format=FORMAT Which format output to generate, default: svg
97
+ --strategy=STRATEGY Which strategy to use for SVG output, default: embedded_image
98
+ --res=RESOLUTION The output resolution (how many radial gradients per dimension, default: 5)
97
99
  --overlap=OVERLAP How much to overlap radial gradients
98
100
  --dist=DISTRIBUTION Distribution strategy for radial gradients
99
101
  --random-offset=OFFSET Upper limit for how much to randomly offset each radial gradient
data/bin/cli ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "foggy-mirror"
6
+
7
+ FoggyMirror::CLI.new.run
data/foggy-mirror.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Pedro Carbajal"]
9
9
  spec.email = ["pedro_c@beezwax.net"]
10
10
 
11
- spec.summary = "Tool to create gradient-based blurred versions of raster images"
12
- spec.description = "foggy-mirror takes a raster image and outputs a blurred version of it using CSS or SVG radial-gradients"
11
+ spec.summary = "Tool to create math-based tiny blurry versions of raster images"
12
+ spec.description = "foggy-mirror takes a raster image and outputs a blurred version of it using SVG or CSS"
13
13
  spec.homepage = "https://github.com/beezwax/foggy-mirror"
14
14
  spec.required_ruby_version = ">= 2.4.0"
15
15
 
@@ -5,20 +5,41 @@ rescue LoadError
5
5
  raise LoadError, "Couldn't load 'vips' library, do you have ruby-vips in your Gemfile?"
6
6
  end
7
7
 
8
+ require 'base64'
9
+
8
10
  module FoggyMirror
9
11
  class Vips
10
- include Utils
11
-
12
12
  def initialize(path)
13
13
  @path = path
14
14
  end
15
15
 
16
16
  def dominant_color
17
- html_color *color_avg(load_image(DEFAULT_RESOLUTION))
17
+ color_avg(load_image(DEFAULT_RESOLUTION))
18
18
  end
19
19
 
20
20
  def color_samples(res)
21
- square_image(res).to_a.flatten(1).map { |rgb| html_color *rgb }
21
+ square_image(res).to_a.flatten(1)
22
+ end
23
+
24
+ # format and options are passed to Vips' #write_to_buffer.
25
+ #
26
+ # We use .gif as the default format as it's by far the most size-efficient
27
+ # format for very small images. E.g. a 5x5 GIF is around 171 bytes, while
28
+ # an equivalent PNG would be around 2780 bytes, and JPEG would be larger
29
+ # still.
30
+ #
31
+ def data_uri(res, format: '.gif', options: {})
32
+ image_type = if format.downcase.match?(/\.jpe?g/)
33
+ 'jpeg'
34
+ else
35
+ format.downcase.match(/\.(png|gif)/)[1]
36
+ end
37
+
38
+ raise(ArgumentError, 'Unsupported image format') unless image_type
39
+
40
+ base64 = Base64.strict_encode64(square_image(res).write_to_buffer(format, **options)).chomp
41
+
42
+ "data:image/#{image_type};base64,#{base64}"
22
43
  end
23
44
 
24
45
  private
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FoggyMirror
4
+ Blob = Struct.new(:x, :y, :r, :color, keyword_init: true) do
5
+ def initialize(color:, **args)
6
+ super color: Color.new(color), **args
7
+ end
8
+ end
9
+ end
@@ -5,6 +5,10 @@ module FoggyMirror
5
5
  class CLI
6
6
  DEFAULT_EXTENSION = '.foggy.svg'
7
7
 
8
+ DEFAULT_FORMAT = :svg
9
+
10
+ FORMATS = %i[svg css]
11
+
8
12
  def initialize(args = ARGV)
9
13
  @args = args.dup
10
14
  end
@@ -12,6 +16,8 @@ module FoggyMirror
12
16
  def run
13
17
  @options = {}
14
18
  @extension = DEFAULT_EXTENSION
19
+ @format = DEFAULT_FORMAT
20
+ @strategy = SVG::STRATEGIES.first
15
21
  @stdout = false
16
22
  @target_dir = nil
17
23
 
@@ -22,11 +28,19 @@ module FoggyMirror
22
28
  @args.each do |path|
23
29
  p = Processor.new(path, **@options)
24
30
 
25
- unless @stdout
26
- foggy_file = File.join(@target_dir || File.dirname(path), File.basename(path, '.*') + @extension)
27
- IO.write(foggy_file, p.to_svg)
31
+ output = case @format
32
+ when :svg
33
+ p.to_svg(strategy: @strategy)
34
+ when :css
35
+ @stdout = true
36
+ p.to_css
37
+ end
38
+
39
+ if @stdout
40
+ puts output
28
41
  else
29
- puts p.to_svg
42
+ foggy_file = File.join(@target_dir || File.dirname(path), File.basename(path, '.*') + @extension)
43
+ IO.write(foggy_file, output)
30
44
  end
31
45
  end
32
46
  end
@@ -38,6 +52,14 @@ module FoggyMirror
38
52
  OptionParser.new do |opts|
39
53
  opts.banner = 'Usage: foggy-mirror [options] [--] image_file ...'
40
54
 
55
+ opts.on('--format=FORMAT', FORMATS, "Which format output to generate, default: #{DEFAULT_FORMAT}") do |f|
56
+ @format = f
57
+ end
58
+
59
+ opts.on('--strategy=STRATEGY', SVG::STRATEGIES, "Which strategy to use for SVG output, default: #{SVG::STRATEGIES.first}") do |s|
60
+ @strategy = s
61
+ end
62
+
41
63
  opts.on('--res=RESOLUTION', Integer, "The output resolution (how many radial gradients per dimension, default: #{DEFAULT_RESOLUTION})") do |r|
42
64
  @options[:resolution] = r
43
65
  end
@@ -46,7 +68,7 @@ module FoggyMirror
46
68
  @options[:overlap] = o
47
69
  end
48
70
 
49
- opts.on('--dist=DISTRIBUTION', %w[shuffle spiral_in spiral_out scan scan_reverse], 'Distribution strategy for radial gradients') do |d|
71
+ opts.on('--dist=DISTRIBUTION', %w[shuffle spiral_in spiral_out scan scan_reverse brightness brightness_reverse], 'Distribution strategy for radial gradients') do |d|
50
72
  @options[:distribution] = d.to_s
51
73
  end
52
74
 
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FoggyMirror
4
+ class Color
5
+ RGB_WEIGHTS = [0.299, 0.587, 0.114].freeze
6
+
7
+ PADDED_RGB_HEX = '%06x'
8
+
9
+ def initialize(color)
10
+ if color.kind_of?(String)
11
+ color = color[/[A-Z0-9]{6}/i].to_i(16)
12
+ elsif color.kind_of?(Array)
13
+ color = (color[0] << 16) + (color[1] << 8) + color[2]
14
+ end
15
+
16
+ color = color.to_i
17
+
18
+ if color > 0xFFFFFF || color < 0
19
+ raise ArgumentError, "out of range color value (#{color < 0 ? '-' : ''}0x#{color.abs.to_s(16).upcase})"
20
+ end
21
+
22
+ @value = color.to_i
23
+ end
24
+
25
+ def brightness(weights: RGB_WEIGHTS)
26
+ Math.sqrt(
27
+ weights[0] * red ** 2 +
28
+ weights[1] * green ** 2 +
29
+ weights[2] * blue ** 2
30
+ ).to_i
31
+ end
32
+
33
+ def red
34
+ (@value & 0xFF0000) >> 16
35
+ end
36
+
37
+ def green
38
+ (@value & 0x00FF00) >> 8
39
+ end
40
+
41
+ def blue
42
+ @value & 0x0000FF
43
+ end
44
+
45
+ def ==(oth)
46
+ oth.to_i == @value
47
+ end
48
+ alias_method :eql?, :==
49
+
50
+ def hash
51
+ @value.hash
52
+ end
53
+
54
+ def to_s(prefix: '#', shorthand: true)
55
+ if shorthand
56
+ return 'red' if @value == 0xFF0000
57
+
58
+ if (@value & 0xF0F0F0) == (@value & 0x0F0F0F) << 4
59
+ hex = to_s(prefix: '', shorthand: false)
60
+ return prefix.to_s + hex[0] + hex[2] + hex[4]
61
+ end
62
+ end
63
+
64
+ prefix.to_s + (PADDED_RGB_HEX % @value).upcase
65
+ end
66
+
67
+ def to_i
68
+ @value
69
+ end
70
+
71
+ def inspect
72
+ "#<#{self.class.name} #{to_s}>"
73
+ end
74
+ end
75
+ end
@@ -6,14 +6,14 @@ module FoggyMirror
6
6
  class Exporter
7
7
  extend Forwardable
8
8
 
9
- def_delegators :@processor, :base_color, :resolution, :blobs
9
+ def_delegators :@processor, :base_color, :resolution, :blobs, :data_uri
10
10
 
11
11
  def initialize(processor)
12
12
  @processor = processor
13
13
  end
14
14
 
15
15
  def render
16
- raise NotImplementedError
16
+ raise NoMethodError
17
17
  end
18
18
  end
19
19
  end
@@ -2,6 +2,9 @@
2
2
 
3
3
  module FoggyMirror
4
4
  class CSS < Exporter
5
+ # If hash is true it returns a Ruby Hash with the CSS properties instead of
6
+ # a CSS-ready string
7
+ #
5
8
  def render(hash: false)
6
9
  if hash
7
10
  return {
@@ -18,7 +21,7 @@ module FoggyMirror
18
21
  def radial_gradients
19
22
  blobs.map do |b|
20
23
  x, y, r = [b.x, b.y, b.r].map { |c| (c * 100).round(1) }
21
- "radial-gradient(circle at #{x}% #{y}%, #{b.color} 0%, #{b.color}00 #{r}%)"
24
+ "radial-gradient(circle at #{x}% #{y}%, #{b.color} 0%, #{b.color.to_s(shorthand: false)}00 #{r}%)"
22
25
  end
23
26
  end
24
27
  end
@@ -8,12 +8,28 @@ module FoggyMirror
8
8
 
9
9
  VIEWBOX_SIZE = 1000
10
10
 
11
- def render
11
+ STRATEGIES = %i[embedded_image gradients]
12
+
13
+ def render(strategy: STRATEGIES.first)
14
+ strategy = STRATEGIES.first if strategy.nil?
15
+
16
+ unless STRATEGIES.include?(strategy.to_sym)
17
+ raise ArgumentError, "Invalid SVG rendering strategy `#{strategy}'"
18
+ end
19
+
20
+ send("render_#{strategy}")
21
+ end
22
+
23
+ def render_gradients
12
24
  "#{header}<defs>#{radial_gradients.join}</defs>#{circles.join}</svg>"
13
25
  end
14
26
 
27
+ def render_embedded_image
28
+ "#{header}#{blur_filter}#{embedded_image}</svg>"
29
+ end
30
+
15
31
  def header
16
- %{#{XML_HEADER}<svg viewBox="0 0 #{VIEWBOX_SIZE} #{VIEWBOX_SIZE}" xmlns="http://www.w3.org/2000/svg" version="1.1" style="background-color:#{base_color}">}
32
+ %{#{XML_HEADER}<svg width="#{VIEWBOX_SIZE}" height="#{VIEWBOX_SIZE}" preserveAspectRatio="none" viewBox="0 0 #{VIEWBOX_SIZE} #{VIEWBOX_SIZE}" xmlns="http://www.w3.org/2000/svg" version="1.1" style="background-color:#{base_color}">}
17
33
  end
18
34
 
19
35
  def radial_gradients
@@ -37,5 +53,22 @@ module FoggyMirror
37
53
  Hash[blobs.map(&:color).uniq.map { |c| [c, id.dup].tap { id.succ! } }]
38
54
  end
39
55
  end
56
+
57
+ private
58
+
59
+ def blur_filter
60
+ <<~FILTER.lines.map(&:strip).join
61
+ <filter id="b" color-interpolation-filters="sRGB">
62
+ <feGaussianBlur in="SourceGraphic" stdDeviation="30"/>
63
+ <feComponentTransfer>
64
+ <feFuncA type="discrete" tableValues="1 1"/>
65
+ </feComponentTransfer>
66
+ </filter>
67
+ FILTER
68
+ end
69
+
70
+ def embedded_image
71
+ %{<image href="#{data_uri}" width="#{VIEWBOX_SIZE}" height="#{VIEWBOX_SIZE}" filter="url(#b)"/>}
72
+ end
40
73
  end
41
74
  end
@@ -17,18 +17,14 @@ module FoggyMirror
17
17
  end
18
18
 
19
19
  def base_color
20
- @base_color ||= adapter.dominant_color
21
- end
22
-
23
- def color_samples
24
- @color_samples ||= adapter.color_samples(resolution)
20
+ @base_color ||= Color.new(adapter.dominant_color)
25
21
  end
26
22
 
27
23
  def to_css(hash: false)
28
24
  CSS.new(self).render(hash: hash)
29
25
  end
30
26
 
31
- def to_svg
27
+ def to_svg(strategy: nil)
32
28
  SVG.new(self).render
33
29
  end
34
30
 
@@ -57,30 +53,25 @@ module FoggyMirror
57
53
  spiral_in(blobs).reverse
58
54
  when 'scan_reverse'
59
55
  blobs.reverse
56
+ when 'brightness'
57
+ blobs.sort_by { |b| b.color.brightness }
58
+ when 'brightness_reverse'
59
+ blobs.sort_by { |b| 255 - b.color.brightness }
60
60
  else
61
61
  blobs
62
62
  end
63
63
  end
64
64
 
65
- private
66
-
67
- def spiral_in(array)
68
- matrix = array.each_slice(Math.sqrt(array.size).to_i).to_a
69
-
70
- actions = [
71
- -> { matrix.shift }, # top
72
- -> { matrix.map { |f| f.pop } }, # right
73
- -> { matrix.pop.reverse }, # bottom
74
- -> { matrix.map { |f| f.shift }.reverse } # left
75
- ]
76
-
77
- peel = actions.cycle
65
+ def data_uri
66
+ @data_uri ||= adapter.data_uri(resolution)
67
+ end
78
68
 
79
- [].tap do |r|
80
- r.concat(peel.next.call) until matrix.empty?
81
- end
69
+ def color_samples
70
+ @color_samples ||= adapter.color_samples(resolution)
82
71
  end
83
72
 
73
+ private
74
+
84
75
  def default_adapter
85
76
  require 'vips'
86
77
  Vips
@@ -97,5 +88,22 @@ module FoggyMirror
97
88
 
98
89
  raise Error, "`adapter' must be an adapter class or an adapter name (#{ADAPTERS.keys.join(', ')})"
99
90
  end
91
+
92
+ def spiral_in(array)
93
+ matrix = array.each_slice(Math.sqrt(array.size).to_i).to_a
94
+
95
+ actions = [
96
+ -> { matrix.shift }, # top
97
+ -> { matrix.map { |f| f.pop } }, # right
98
+ -> { matrix.pop.reverse }, # bottom
99
+ -> { matrix.map { |f| f.shift }.reverse } # left
100
+ ]
101
+
102
+ peel = actions.cycle
103
+
104
+ [].tap do |r|
105
+ r.concat(peel.next.call) until matrix.empty?
106
+ end
107
+ end
100
108
  end
101
109
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FoggyMirror
4
- VERSION = "1.0.1"
4
+ VERSION = "1.2.0"
5
5
  end
data/lib/foggy-mirror.rb CHANGED
@@ -12,9 +12,9 @@ module FoggyMirror
12
12
  magick: -> { FoggyMirror::ImageMagick }
13
13
  }.freeze
14
14
 
15
- Blob = Struct.new(:x, :y, :r, :color, keyword_init: true)
16
-
17
15
  autoload :Processor, 'foggy-mirror/processor'
16
+ autoload :Color, 'foggy-mirror/color'
17
+ autoload :Blob, 'foggy-mirror/blob'
18
18
  autoload :CLI, 'foggy-mirror/cli'
19
19
  autoload :Utils, 'foggy-mirror/utils'
20
20
  autoload :ImageMagick, 'foggy-mirror/adapters/image_magick'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foggy-mirror
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pedro Carbajal
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-11 00:00:00.000000000 Z
11
+ date: 2023-03-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-vips
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  description: foggy-mirror takes a raster image and outputs a blurred version of it
84
- using CSS or SVG radial-gradients
84
+ using SVG or CSS
85
85
  email:
86
86
  - pedro_c@beezwax.net
87
87
  executables:
@@ -92,11 +92,13 @@ files:
92
92
  - ".github/workflows/main.yml"
93
93
  - ".gitignore"
94
94
  - ".rspec"
95
+ - CHANGELOG.md
95
96
  - Gemfile
96
97
  - Gemfile.lock
97
98
  - LICENSE.txt
98
99
  - README.md
99
100
  - Rakefile
101
+ - bin/cli
100
102
  - bin/console
101
103
  - bin/setup
102
104
  - exe/foggy-mirror
@@ -104,7 +106,9 @@ files:
104
106
  - lib/foggy-mirror.rb
105
107
  - lib/foggy-mirror/adapters/image_magick.rb
106
108
  - lib/foggy-mirror/adapters/vips.rb
109
+ - lib/foggy-mirror/blob.rb
107
110
  - lib/foggy-mirror/cli.rb
111
+ - lib/foggy-mirror/color.rb
108
112
  - lib/foggy-mirror/exporter.rb
109
113
  - lib/foggy-mirror/exporters/css.rb
110
114
  - lib/foggy-mirror/exporters/svg.rb
@@ -134,5 +138,5 @@ requirements: []
134
138
  rubygems_version: 3.3.3
135
139
  signing_key:
136
140
  specification_version: 4
137
- summary: Tool to create gradient-based blurred versions of raster images
141
+ summary: Tool to create math-based tiny blurry versions of raster images
138
142
  test_files: []