History

Merge pull request #870 from jmerle/sprites

Automatically generate spritesheets

Jasper van Merle committed 7 years ago

Commit af36b512 parents 0f9ff40 8a71af918 files · +258 −181Browse files

Unified · Split

18 files
  1. Gemfile +2 −0
  2. Gemfile.lock +5 −0
  3. assets/images/.gitignore +1 −0
  4. assets/images/docs-1.png +0 −0
  5. assets/images/docs-1@2x.png +0 −0
  6. assets/images/docs-2.png +0 −0
  7. assets/images/docs-2@2x.png +0 −0
  8. assets/stylesheets/application.css.scss +3 −4
  9. assets/stylesheets/global/_icons.scss +0 −182
  10. assets/stylesheets/global/_icons.scss.erb +43 −0
  11. lib/app.rb +7 −4
  12. lib/tasks/assets.thor +1 −0
  13. lib/tasks/sprites.thor +205 −0
  14. public/icons/docs-1.pxm +0 −0
  15. public/icons/docs-1@2x.pxm +0 −0
  16. public/icons/docs-2.pxm +0 −0
  17. public/icons/docs-2@2x.pxm +0 −0
  18. public/icons/docs/bluebird/16@2x.png +0 −0
Gemfile+2−0
@@ -22,6 +22,8 @@ group :app do · 21 unmodified lines
22 gem 'browser'22 gem 'browser'
23 gem 'sass'23 gem 'sass'
24 gem 'coffee-script'24 gem 'coffee-script'
25 gem 'chunky_png'
26 gem 'sprockets-sass'
25end27end
26 28
27group :production do29group :production do
Gemfile.lock+5−0
@@ -12,6 +12,7 @@ GEM · 11 unmodified lines
12 erubi (>= 1.0.0)12 erubi (>= 1.0.0)
13 rack (>= 0.9.0)13 rack (>= 0.9.0)
14 browser (2.5.3)14 browser (2.5.3)
15 chunky_png (1.3.10)
15 coderay (1.1.2)16 coderay (1.1.2)
16 coffee-script (2.4.1)17 coffee-script (2.4.1)
17 coffee-script-source18 coffee-script-source
@@ -96,6 +97,8 @@ GEM · 78 unmodified lines
96 rack (> 1, < 3)97 rack (> 1, < 3)
97 sprockets-helpers (1.2.1)98 sprockets-helpers (1.2.1)
98 sprockets (>= 2.2)99 sprockets (>= 2.2)
100 sprockets-sass (2.0.0.beta2)
101 sprockets (>= 2.0, < 4.0)
99 strings (0.1.4)102 strings (0.1.4)
100 strings-ansi (~> 0.1.0)103 strings-ansi (~> 0.1.0)
101 unicode-display_width (~> 1.4.0)104 unicode-display_width (~> 1.4.0)
@@ -132,6 +135,7 @@ DEPENDENCIES · 30 unmodified lines
132 activesupport (~> 5.2)135 activesupport (~> 5.2)
133 better_errors136 better_errors
134 browser137 browser
138 chunky_png
135 coffee-script139 coffee-script
136 erubi140 erubi
137 html-pipeline141 html-pipeline
@@ -153,6 +157,7 @@ DEPENDENCIES · 15 unmodified lines
153 sinatra-contrib157 sinatra-contrib
154 sprockets158 sprockets
155 sprockets-helpers159 sprockets-helpers
160 sprockets-sass
156 thin161 thin
157 thor162 thor
158 tty-pager163 tty-pager
assets/images/.gitignore+1−0
@@ -0,0 +1 @@
1sprites/**/*
assets/images/docs-1.png+0−0

Binary file not shown.

assets/images/docs-1@2x.png+0−0

Binary file not shown.

assets/images/docs-2.png+0−0

Binary file not shown.

assets/images/docs-2@2x.png+0−0

Binary file not shown.

assets/stylesheets/application.css.scss+3−4
@@ -1,7 +1,6 @@
1//= depend_on docs-1.png1//= depend_on sprites/docs.png
2//= depend_on docs-1@2x.png2//= depend_on sprites/docs@2x.png
3//= depend_on docs-2.png3//= depend_on sprites/docs.json
4//= depend_on docs-2@2x.png
5 4
6/*!5/*!
7 * Copyright 2013-2019 Thibaut Courouble and other contributors6 * Copyright 2013-2019 Thibaut Courouble and other contributors
assets/stylesheets/global/_icons.scss+0−182
@@ -1,182 +0,0 @@
1%svg-icon {
2 display: inline-block;
3 vertical-align: top;
4 width: 1rem;
5 height: 1rem;
6 pointer-events: none;
7 fill: currentColor;
8}
9
10%doc-icon {
11 content: '';
12 display: block;
13 width: 1rem;
14 height: 1rem;
15 background-image: image-url('docs-1.png');
16 background-size: 10rem 10rem;
17}
18
19%doc-icon-2 { background-image: image-url('docs-2.png') !important; }
20
21@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
22 %doc-icon { background-image: image-url('docs-1@2x.png'); }
23 %doc-icon-2 { background-image: image-url('docs-2@2x.png') !important; }
24}
25
26html._theme-dark {
27 %darkIconFix {
28 filter: invert(100%) grayscale(100%);
29 -webkit-filter: invert(100%) grayscale(100%);
30 }
31}
32
33._icon-jest:before { background-position: 0 0; }
34._icon-liquid:before { background-position: -1rem 0; }
35._icon-openjdk:before { background-position: -2rem 0; }
36._icon-codeceptjs:before { background-position: -3rem 0; }
37._icon-codeception:before { background-position: -4rem 0; }
38._icon-sqlite:before { background-position: -5rem 0; @extend %darkIconFix !optional; }
39._icon-async:before { background-position: -6rem 0; @extend %darkIconFix !optional; }
40._icon-http:before { background-position: -7rem 0; @extend %darkIconFix !optional; }
41._icon-jquery:before { background-position: -8rem 0; @extend %darkIconFix !optional; }
42._icon-underscore:before { background-position: -9rem 0; @extend %darkIconFix !optional; }
43._icon-html:before { background-position: 0 -1rem; }
44._icon-css:before { background-position: -1rem -1rem; }
45._icon-dom:before { background-position: -2rem -1rem; }
46._icon-dom_events:before { background-position: -3rem -1rem; }
47._icon-javascript:before { background-position: -4rem -1rem; }
48._icon-backbone:before { background-position: -5rem -1rem; @extend %darkIconFix !optional; }
49._icon-node:before,
50._icon-node_lts:before { background-position: -6rem -1rem; }
51._icon-sass:before { background-position: -7rem -1rem; }
52._icon-less:before { background-position: -8rem -1rem; }
53._icon-angularjs:before { background-position: -9rem -1rem; }
54._icon-coffeescript:before { background-position: 0 -2rem; @extend %darkIconFix !optional; }
55._icon-ember:before { background-position: -1rem -2rem; }
56._icon-yarn:before { background-position: -2rem -2rem; }
57._icon-immutable:before { background-position: -3rem -2rem; @extend %darkIconFix !optional; }
58._icon-jqueryui:before { background-position: -4rem -2rem; }
59._icon-jquerymobile:before { background-position: -5rem -2rem; }
60._icon-lodash:before { background-position: -6rem -2rem; }
61._icon-php:before { background-position: -7rem -2rem; }
62._icon-ruby:before,
63._icon-minitest:before { background-position: -8rem -2rem; }
64._icon-rails:before { background-position: -9rem -2rem; }
65._icon-python:before,
66._icon-python2:before { background-position: 0 -3rem; }
67._icon-git:before { background-position: -1rem -3rem; }
68._icon-redis:before { background-position: -2rem -3rem; }
69._icon-postgresql:before { background-position: -3rem -3rem; }
70._icon-d3:before { background-position: -4rem -3rem; }
71._icon-knockout:before { background-position: -5rem -3rem; }
72._icon-moment:before { background-position: -6rem -3rem; @extend %darkIconFix !optional; }
73._icon-c:before { background-position: -7rem -3rem; }
74._icon-statsmodels:before { background-position: -8rem -3rem; }
75._icon-yii:before,
76._icon-yii1:before { background-position: -9rem -3rem; }
77._icon-cpp:before { background-position: 0 -4rem; }
78._icon-go:before { background-position: -1rem -4rem; }
79._icon-express:before { background-position: -2rem -4rem; }
80._icon-grunt:before { background-position: -3rem -4rem; }
81._icon-rust:before { background-position: -4rem -4rem; @extend %darkIconFix !optional; }
82._icon-laravel:before { background-position: -5rem -4rem; }
83._icon-haskell:before { background-position: -6rem -4rem; }
84._icon-requirejs:before { background-position: -7rem -4rem; }
85._icon-chai:before { background-position: -8rem -4rem; }
86._icon-sinon:before { background-position: -9rem -4rem; }
87._icon-cordova:before { background-position: 0 -5rem; }
88._icon-markdown:before { background-position: -1rem -5rem; @extend %darkIconFix !optional; }
89._icon-django:before { background-position: -2rem -5rem; }
90._icon-xslt_xpath:before { background-position: -3rem -5rem; }
91._icon-nginx:before,
92._icon-nginx_lua_module:before { background-position: -4rem -5rem; }
93._icon-svg:before { background-position: -5rem -5rem; }
94._icon-marionette:before { background-position: -6rem -5rem; }
95._icon-jsdoc:before,
96._icon-koa:before,
97._icon-graphite:before,
98._icon-mongoose:before { background-position: -7rem -5rem; }
99._icon-phpunit:before { background-position: -8rem -5rem; }
100._icon-nokogiri:before { background-position: -9rem -5rem; @extend %darkIconFix !optional; }
101._icon-rethinkdb:before { background-position: 0 -6rem; }
102._icon-react:before { background-position: -1rem -6rem; }
103._icon-socketio:before { background-position: -2rem -6rem; }
104._icon-modernizr:before { background-position: -3rem -6rem; }
105._icon-bower:before { background-position: -4rem -6rem; }
106._icon-fish:before { background-position: -5rem -6rem; @extend %darkIconFix !optional; }
107._icon-scikit_image:before { background-position: -6rem -6rem; }
108._icon-twig:before { background-position: -7rem -6rem; }
109._icon-pandas:before { background-position: -8rem -6rem; }
110._icon-scikit_learn:before { background-position: -9rem -6rem; }
111._icon-bottle:before { background-position: 0 -7rem; }
112._icon-docker:before { background-position: -1rem -7rem; }
113._icon-cakephp:before { background-position: -2rem -7rem; }
114._icon-lua:before { background-position: -3rem -7rem; @extend %darkIconFix !optional; }
115._icon-clojure:before { background-position: -4rem -7rem; }
116._icon-symfony:before { background-position: -5rem -7rem; }
117._icon-mocha:before { background-position: -6rem -7rem; }
118._icon-meteor:before { background-position: -7rem -7rem; @extend %darkIconFix !optional; }
119._icon-npm:before { background-position: -8rem -7rem; }
120._icon-apache_http_server:before { background-position: -9rem -7rem; }
121._icon-drupal:before { background-position: 0 -8rem; }
122._icon-webpack:before { background-position: -1rem -8rem; }
123._icon-phaser:before { background-position: -2rem -8rem; }
124._icon-vue:before { background-position: -3rem -8rem; }
125._icon-opentsdb:before { background-position: -4rem -8rem; }
126._icon-q:before { background-position: -5rem -8rem; }
127._icon-crystal:before { background-position: -6rem -8rem; @extend %darkIconFix !optional; }
128._icon-julia:before { background-position: -7rem -8rem; @extend %darkIconFix !optional; }
129._icon-redux:before { background-position: -8rem -8rem; @extend %darkIconFix !optional; }
130._icon-bootstrap:before { background-position: -9rem -8rem; }
131._icon-react_native:before { background-position: 0 -9rem; }
132._icon-phalcon:before { background-position: -1rem -9rem; }
133._icon-matplotlib:before { background-position: -2rem -9rem; }
134._icon-cmake:before { background-position: -3rem -9rem; }
135._icon-elixir:before { background-position: -4rem -9rem; @extend %darkIconFix !optional; }
136._icon-vagrant:before { background-position: -5rem -9rem; }
137._icon-dojo:before { background-position: -6rem -9rem; }
138._icon-flow:before { background-position: -7rem -9rem; }
139._icon-relay:before { background-position: -8rem -9rem; }
140._icon-phoenix:before { background-position: -9rem -9rem; }
141
142._icon-tcl_tk:before { background-position: 0 0; @extend %doc-icon-2; }
143._icon-erlang:before { background-position: -1rem 0; @extend %doc-icon-2; }
144._icon-chef:before { background-position: -2rem 0; @extend %doc-icon-2; }
145._icon-ramda:before { background-position: -3rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; }
146._icon-codeigniter:before { background-position: -4rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; }
147._icon-influxdata:before { background-position: -5rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; }
148._icon-tensorflow:before { background-position: -6rem 0; @extend %doc-icon-2; }
149._icon-haxe:before { background-position: -7rem 0; @extend %doc-icon-2; }
150._icon-ansible:before { background-position: -8rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; }
151._icon-typescript:before { background-position: -9rem 0; @extend %doc-icon-2; }
152._icon-browser_support_tables:before { background-position: 0rem -1rem; @extend %doc-icon-2; }
153._icon-gnu_fortran:before { background-position: -1rem -1rem; @extend %doc-icon-2; }
154._icon-gcc:before { background-position: -2rem -1rem; @extend %doc-icon-2; }
155._icon-perl:before { background-position: -3rem -1rem; @extend %doc-icon-2; }
156._icon-apache_pig:before { background-position: -4rem -1rem; @extend %doc-icon-2; }
157._icon-numpy:before { background-position: -5rem -1rem; @extend %doc-icon-2; }
158._icon-kotlin:before { background-position: -6rem -1rem; @extend %doc-icon-2; }
159._icon-padrino:before { background-position: -7rem -1rem; @extend %doc-icon-2; }
160._icon-angular:before { background-position: -8rem -1rem; @extend %doc-icon-2; }
161._icon-love:before { background-position: -9rem -1rem; @extend %doc-icon-2; }
162._icon-jasmine:before { background-position: 0 -2rem; @extend %doc-icon-2; }
163._icon-pug:before { background-position: -1rem -2rem; @extend %doc-icon-2; }
164._icon-electron:before { background-position: -2rem -2rem; @extend %doc-icon-2; }
165._icon-falcon:before { background-position: -3rem -2rem; @extend %doc-icon-2; }
166._icon-godot:before { background-position: -4rem -2rem; @extend %doc-icon-2; }
167._icon-nim:before { background-position: -5rem -2rem; @extend %doc-icon-2; @extend %darkIconFix !optional; }
168._icon-vulkan:before { background-position: -6rem -2rem; @extend %doc-icon-2; @extend %darkIconFix !optional; }
169._icon-d:before { background-position: -7rem -2rem; @extend %doc-icon-2; }
170._icon-bluebird:before { background-position: -8rem -2rem; @extend %doc-icon-2; }
171._icon-eslint:before { background-position: -9rem -2rem; @extend %doc-icon-2; }
172._icon-homebrew:before { background-position: 0 -3rem; @extend %doc-icon-2; }
173._icon-jekyll:before { background-position: -1rem -3rem; @extend %doc-icon-2; }
174._icon-babel:before { background-position: -2rem -3rem; @extend %doc-icon-2; }
175._icon-leaflet:before { background-position: -3rem -3rem; @extend %doc-icon-2; }
176._icon-terraform:before { background-position: -4rem -3rem; @extend %doc-icon-2; }
177._icon-pygame:before { background-position: -5rem -3rem; @extend %doc-icon-2; }
178._icon-bash:before { background-position: -6rem -3rem; @extend %doc-icon-2; }
179._icon-dart:before { background-position: -7rem -3rem; @extend %doc-icon-2; }
180._icon-qt:before { background-position: -8rem -3rem; @extend %doc-icon-2; }
181._icon-puppeteer:before { background-position: -9rem -3rem; @extend %doc-icon-2; }
182._icon-handlebars:before { background-position: 0 -4rem; @extend %doc-icon-2; @extend %darkIconFix !optional; }
assets/stylesheets/global/_icons.scss.erb+43−0
@@ -0,0 +1,43 @@
1<% manifest = JSON.parse(File.read('assets/images/sprites/docs.json')) %>
2
3%svg-icon {
4 display: inline-block;
5 vertical-align: top;
6 width: 1rem;
7 height: 1rem;
8 pointer-events: none;
9 fill: currentColor;
10}
11
12%doc-icon {
13 content: '';
14 display: block;
15 width: 1rem;
16 height: 1rem;
17 background-image: image-url('sprites/docs.png');
18 background-size: <%= manifest['icons_per_row'] %>rem <%= manifest['icons_per_row'] %>rem;
19}
20
21@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
22 %doc-icon { background-image: image-url('sprites/docs@2x.png'); }
23}
24
25html._theme-dark {
26 %darkIconFix {
27 filter: invert(100%) grayscale(100%);
28 -webkit-filter: invert(100%) grayscale(100%);
29 }
30}
31
32<%=
33 items = []
34
35 manifest['items'].each do |item|
36 rules = []
37 rules << "background-position: -#{item['col']}rem -#{item['row']}rem;"
38 rules << "@extend %darkIconFix !optional;" if item['dark_icon_fix']
39 items << "._icon-#{item['type']}:before { #{rules.join(' ')} }"
40 end
41
42 items.join('')
43 %>
lib/app.rb+7−4
@@ -53,6 +53,11 @@ class App < Sinatra::Application · 52 unmodified lines
53 end53 end
54 54
55 configure :test, :development do55 configure :test, :development do
56 require 'thor'
57 load 'tasks/sprites.thor'
58
59 SpritesCLI.new.invoke(:generate)
60
56 require 'active_support/per_thread_registry'61 require 'active_support/per_thread_registry'
57 require 'active_support/cache'62 require 'active_support/cache'
58 sprockets.cache = ActiveSupport::Cache.lookup_store :file_store, root.join('tmp', 'cache', 'assets', environment.to_s)63 sprockets.cache = ActiveSupport::Cache.lookup_store :file_store, root.join('tmp', 'cache', 'assets', environment.to_s)
@@ -199,10 +204,8 @@ class App < Sinatra::Application · 140 unmodified lines
199 @@service_worker_asset_urls ||= [204 @@service_worker_asset_urls ||= [
200 javascript_path('application', asset_host: false),205 javascript_path('application', asset_host: false),
201 stylesheet_path('application'),206 stylesheet_path('application'),
202 image_path('docs-1.png'),207 image_path('sprites/docs.png'),
203 image_path('docs-1@2x.png'),208 image_path('sprites/docs@2x.png'),
204 image_path('docs-2.png'),
205 image_path('docs-2@2x.png'),
206 asset_path('docs.js'),209 asset_path('docs.js'),
207 App.production? ? nil : javascript_path('debug'),210 App.production? ? nil : javascript_path('debug'),
208 ].compact211 ].compact
lib/tasks/assets.thor+1−0
@@ -14,6 +14,7 @@ class AssetsCLI < Thor · 13 unmodified lines
14 option :keep, type: :numeric, default: 0, desc: 'Number of old assets to keep'14 option :keep, type: :numeric, default: 0, desc: 'Number of old assets to keep'
15 option :verbose, type: :boolean15 option :verbose, type: :boolean
16 def compile16 def compile
17 invoke 'sprites:generate', [], :verbose => options[:verbose]
17 manifest.compile App.assets_compile18 manifest.compile App.assets_compile
18 manifest.clean(options[:keep]) if options[:clean]19 manifest.clean(options[:keep]) if options[:clean]
19 end20 end
lib/tasks/sprites.thor+205−0
@@ -0,0 +1,205 @@
1class SpritesCLI < Thor
2 def self.to_s
3 'Sprites'
4 end
5
6 def initialize(*args)
7 require 'docs'
8 require 'chunky_png'
9 require 'fileutils'
10 super
11 end
12
13 desc 'generate [--verbose]', 'Generate the documentation icon spritesheets'
14 option :verbose, type: :boolean
15 def generate
16 items = get_items
17 items_with_icons = items.select {|item| item[:has_icons]}
18 items_without_icons = items.select {|item| !item[:has_icons]}
19 icons_per_row = Math.sqrt(items_with_icons.length).ceil
20
21 bg_color = get_sidebar_background
22
23 items_with_icons.each_with_index do |item, index|
24 item[:row] = (index / icons_per_row).floor
25 item[:col] = index - item[:row] * icons_per_row
26
27 item[:icon_16] = get_icon(item[:path_16], 16)
28 item[:icon_32] = get_icon(item[:path_32], 32)
29
30 item[:dark_icon_fix] = needs_dark_icon_fix(item[:icon_32], bg_color)
31 end
32
33 log_details(items_with_icons, icons_per_row)
34
35 generate_spritesheet(16, items_with_icons, 'assets/images/sprites/docs.png') {|item| item[:icon_16]}
36 generate_spritesheet(32, items_with_icons, 'assets/images/sprites/docs@2x.png') {|item| item[:icon_32]}
37
38 # Add Mongoose's icon details to docs without custom icons
39 default_item = items_with_icons.find {|item| item[:type] == 'mongoose'}
40 items_without_icons.each do |item|
41 item[:row] = default_item[:row]
42 item[:col] = default_item[:col]
43 item[:dark_icon_fix] = default_item[:dark_icon_fix]
44 end
45
46 save_manifest(items, icons_per_row, 'assets/images/sprites/docs.json')
47 end
48
49 private
50
51 def get_items
52 items = Docs.all.map do |doc|
53 base_path = "public/icons/docs/#{doc.slug}"
54 {
55 :type => doc.slug,
56 :path_16 => "#{base_path}/16.png",
57 :path_32 => "#{base_path}/16@2x.png"
58 }
59 end
60
61 # Checking paths against an array of possible paths is faster than 200+ File.exist? calls
62 files = Dir.glob('public/icons/docs/**/*.png')
63
64 items.each do |item|
65 item[:has_icons] = files.include?(item[:path_16]) && files.include?(item[:path_32])
66 end
67 end
68
69 def get_icon(path, max_size)
70 icon = ChunkyPNG::Image.from_file(path)
71
72 # Check if the icon is too big
73 # If it is, resize the image without changing the aspect ratio
74 if icon.width > max_size || icon.height > max_size
75 ratio = icon.width.to_f / icon.height
76 new_width = (icon.width >= icon.height ? max_size : max_size * ratio).floor
77 new_height = (icon.width >= icon.height ? max_size / ratio : max_size).floor
78
79 logger.warn("Icon #{path} is too big: max size is #{max_size} x #{max_size}, icon is #{icon.width} x #{icon.height}, resizing to #{new_width} x #{new_height}")
80
81 icon.resample_nearest_neighbor!(new_width, new_height)
82 end
83
84 icon
85 end
86
87 def get_sidebar_background
88 # This is a hacky way to get the background color of the sidebar
89 # Unfortunately, it's not possible to get the value of a SCSS variable from a Thor task
90 # Because hard-coding the value is even worse, we extract it using some regex
91 path = 'assets/stylesheets/global/_variables-dark.scss'
92 regex = /--sidebarBackground:\s+([^;]+);/
93 ChunkyPNG::Color.parse(File.read(path)[regex, 1])
94 end
95
96 def needs_dark_icon_fix(icon, bg_color)
97 # Determine whether the icon needs to be grayscaled if the user has enabled the dark theme
98 # The logic is roughly based on https://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast
99 contrast = icon.pixels.select {|pixel| ChunkyPNG::Color.a(pixel) > 0}.map do |pixel|
100 get_contrast(bg_color, pixel)
101 end
102
103 avg = contrast.reduce(:+) / contrast.size.to_f
104 avg < 3.5
105 end
106
107 def get_contrast(base, other)
108 # Calculating the contrast ratio as described in the WCAG 2.0:
109 # https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
110 l1 = get_luminance(base) + 0.05
111 l2 = get_luminance(other) + 0.05
112 ratio = l1 / l2
113 l2 > l1 ? 1 / ratio : ratio
114 end
115
116 def get_luminance(color)
117 rgb = [
118 ChunkyPNG::Color.r(color).to_f,
119 ChunkyPNG::Color.g(color).to_f,
120 ChunkyPNG::Color.b(color).to_f
121 ]
122
123 # Calculating the relative luminance as described in the WCAG 2.0:
124 # https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
125
126 rgb.map! do |value|
127 value /= 255
128 value < 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4
129 end
130
131 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]
132 end
133
134 def generate_spritesheet(size, items_with_icons, output_path, &item_to_icon)
135 logger.info("Generating spritesheet #{output_path} with icons of size #{size} x #{size}")
136
137 icons_per_row = Math.sqrt(items_with_icons.length).ceil
138 spritesheet = ChunkyPNG::Image.new(size * icons_per_row, size * icons_per_row)
139
140 items_with_icons.each do |item|
141 icon = item_to_icon.call(item)
142
143 # Calculate the base coordinates
144 base_x = item[:col] * size
145 base_y = item[:row] * size
146
147 # Center the icon if it's not a perfect rectangle
148 x = base_x + ((size - icon.width) / 2).floor
149 y = base_y + ((size - icon.height) / 2).floor
150
151 spritesheet.compose!(icon, x, y)
152 end
153
154 FileUtils.mkdir_p(File.dirname(output_path))
155 spritesheet.save(output_path)
156 end
157
158 def save_manifest(items, icons_per_row, path)
159 logger.info("Saving spritesheet details to #{path}")
160
161 FileUtils.mkdir_p(File.dirname(path))
162
163 # Only save the details that the scss file needs
164 manifest_items = items.map do |item|
165 {
166 :type => item[:type],
167 :row => item[:row],
168 :col => item[:col],
169 :dark_icon_fix => item[:dark_icon_fix]
170 }
171 end
172
173 manifest = {:icons_per_row => icons_per_row, :items => manifest_items}
174
175 File.open(path, 'w') do |f|
176 f.write(JSON.generate(manifest))
177 end
178 end
179
180 def log_details(items_with_icons, icons_per_row)
181 logger.debug("Amount of icons: #{items_with_icons.length}")
182 logger.debug("Amount of icons needing the dark icon fix: #{items_with_icons.count {|item| item[:dark_icon_fix]}}")
183 logger.debug("Amount of icons per row: #{icons_per_row}")
184
185 max_type_length = items_with_icons.map {|item| item[:type].length}.max
186 border = "+#{'-' * (max_type_length + 2)}+#{'-' * 5}+#{'-' * 8}+#{'-' * 15}+"
187
188 logger.debug(border)
189 logger.debug("| #{'Type'.ljust(max_type_length)} | Row | Column | Dark icon fix |")
190 logger.debug(border)
191
192 items_with_icons.each do |item|
193 logger.debug("| #{item[:type].ljust(max_type_length)} | #{item[:row].to_s.ljust(3)} | #{item[:col].to_s.ljust(6)} | #{(item[:dark_icon_fix] ? 'Yes' : 'No').ljust(13)} |")
194 end
195
196 logger.debug(border)
197 end
198
199 def logger
200 @logger ||= Logger.new($stdout).tap do |logger|
201 logger.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
202 logger.formatter = proc {|severity, datetime, progname, msg| "#{msg}\n"}
203 end
204 end
205end
public/icons/docs-1.pxm+0−0

Binary file not shown.

public/icons/docs-1@2x.pxm+0−0

Binary file not shown.

public/icons/docs-2.pxm+0−0

Binary file not shown.

public/icons/docs-2@2x.pxm+0−0

Binary file not shown.

public/icons/docs/bluebird/16@2x.png+0−0

Binary file not shown.