foggy-mirror 1.0.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: []