make_screenshots.rb (7634B)
1 #!/usr/bin/env ruby 2 3 # To reggenerate screenshots: 4 # 1. Extract module screenshots from Rack: ./scripts/make_module_screenshots.rb 5 # 2. Make composite screenshots for README: ./scripts/make_screenshots.rb 6 7 screens = [ 8 { 9 file: 'modules1.png', 10 crop: false, 11 rows: [ 12 ['VCO', 'LVCO', 'Sine', 'Pulse', 'XCO', 'Additator', 'FMOp', 'Chirp'], 13 ['LFO', 'LLFO', 'FourFO', 'EightFO'] 14 ] 15 }, 16 { 17 file: 'modules2.png', 18 crop: false, 19 rows: [ 20 ['VCF', 'LVCF', 'FFB', 'EQ', 'EQS', '-', 'LPG', 'LLPG', 'MegaGate'], 21 ['PEQ', 'PEQ6', 'PEQ6XF', 'PEQ14', 'PEQ14XF'], 22 ['DADSRH', 'DADSRHPlus', 'Shaper', 'ShaperPlus', 'AD', 'ASR', 'ADSR', 'Vish', 'DGate', 'RGate', 'Edge', 'Follow'] 23 ] 24 }, 25 { 26 file: 'modules3.png', 27 crop: false, 28 rows: [ 29 ['Mix8', 'Mix8x', 'Mute8'], 30 ['Mix4', 'Mix4x', 'Mix1', 'Mix2', 'UMix', 'Mumix', 'VCM', 'Pan', 'XFade'], 31 ['Matrix81', 'Matrix18', 'Matrix44', 'Matrix44Cvm'], 32 ['Matrix88', 'Matrix88Cv', 'Matrix88M'], 33 ['Switch81', 'Switch18', 'Switch44', 'Switch88', 'Switch1616'] 34 ] 35 }, 36 { 37 file: 'modules4.png', 38 crop: false, 39 rows: [ 40 ['VCA', 'VCAmp', 'Velo', '-', 'AMRM', 'Pressor', 'Clpr', 'Lmtr', 'Nsgt', 'CmpDist'], 41 ['Noise', 'SampleHold', 'Walk2', 'Walk', '-', 'EightOne', 'OneEight', 'AddrSeq', 'AddrSeqX', 'PGMR', 'PGMRX'], 42 ['VU', 'Analyzer', 'AnalyzerXL'], 43 ['Ranalyzer'] 44 ] 45 }, 46 { 47 file: 'modules5.png', 48 crop: false, 49 rows: [ 50 ['Mono', 'Arp', 'Assign', 'Unison', 'PolyCon', 'PolyCon8', 'PolyOff16', 'PolyOff8', 'PolyMult', '-', 'Detune', 'Stack', 'Reftone'], 51 ['Bool', 'Cmp', 'CVD', 'FlipFlop', 'Inv', 'Manual', 'FourMan', 'Mult', 'Offset', 'Slew', 'Sums', 'Switch', 'Lgsw', '-', 'Blank3', 'Blank6'] 52 ] 53 }, 54 55 { 56 file: 'skin-dark.png', 57 crop: false, 58 rows: [ 59 ['VCO', 'XCO', 'FMOp', 'Walk2', 'VCF', 'DADSRH', 'FFB', 'SampleHold', 'Pan'], 60 ['Mix4', 'AddrSeq', 'Pgmr', 'Switch44', 'Analyzer', 'Pressor', 'Offset', 'Blank6'] 61 ], 62 skin: 'dark', 63 nopad: true 64 }, 65 { 66 file: 'skin-lowcontrast.png', 67 crop: false, 68 rows: [ 69 ['VCO', 'XCO', 'FMOp', 'Walk2', 'VCF', 'DADSRH', 'FFB', 'SampleHold', 'Pan'], 70 ['Mix4', 'AddrSeq', 'Pgmr', 'Switch44', 'Analyzer', 'Pressor', 'Offset', 'Blank6'] 71 ], 72 skin: 'lowcontrast', 73 nopad: true 74 }, 75 76 { 77 file: 'oscillators1.png', 78 crop: true, 79 rows: [ 80 ['VCO', 'LVCO', 'Sine', 'Pulse', 'XCO'] 81 ] 82 }, 83 { 84 file: 'oscillators2.png', 85 crop: true, 86 rows: [ 87 ['Additator', 'FMOp', 'Chirp'] 88 ] 89 }, 90 { 91 file: 'lfos.png', 92 crop: true, 93 rows: [ 94 ['LFO', 'LLFO', 'FourFO', 'EightFO'] 95 ] 96 }, 97 { 98 file: 'filters.png', 99 crop: true, 100 rows: [ 101 ['VCF', 'LVCF', 'FFB', 'EQ', 'EQS'] 102 ] 103 }, 104 { 105 file: 'lpgs.png', 106 crop: true, 107 rows: [ 108 ['LPG', 'LLPG', 'MegaGAte'] 109 ] 110 }, 111 { 112 file: 'parametric_eqs1.png', 113 crop: true, 114 rows: [ 115 ['PEQ', 'PEQ6', 'PEQ6XF'] 116 ] 117 }, 118 { 119 file: 'parametric_eqs2.png', 120 crop: true, 121 rows: [ 122 ['PEQ14', 'PEQ14XF'] 123 ] 124 }, 125 { 126 file: 'envelopes1.png', 127 crop: true, 128 rows: [ 129 ['DADSRH', 'DADSRHPlus'] 130 ] 131 }, 132 { 133 file: 'envelopes2.png', 134 crop: true, 135 rows: [ 136 ['Shaper', 'ShaperPlus', 'AD', 'ASR', 'ADSR', 'Vish'] 137 ] 138 }, 139 { 140 file: 'envelopes3.png', 141 crop: true, 142 rows: [ 143 ['DGate', 'RGate', 'Edge', 'Follow'] 144 ] 145 }, 146 { 147 file: 'noise.png', 148 crop: true, 149 rows: [ 150 ['Noise', 'SampleHold', 'Walk2', 'Walk'] 151 ] 152 }, 153 { 154 file: 'mixers1.png', 155 crop: true, 156 rows: [ 157 ['Mix8', 'Mix8x'] 158 ] 159 }, 160 { 161 file: 'mixers2.png', 162 crop: true, 163 rows: [ 164 ['Mix4', 'Mix4x'] 165 ] 166 }, 167 { 168 file: 'mixers3.png', 169 crop: true, 170 rows: [ 171 ['Mix1', 'Mix2', 'UMix', 'Mumix', 'Mute8', 'VCM', 'Pan', 'XFade'] 172 ] 173 }, 174 { 175 file: 'mixers4.png', 176 crop: true, 177 rows: [ 178 ['Matrix81', 'Matrix18', 'Matrix44', 'Matrix44Cvm'] 179 ] 180 }, 181 { 182 file: 'mixers5.png', 183 crop: true, 184 rows: [ 185 ['Matrix88', 'Matrix88Cv', 'Matrix88M'] 186 ] 187 }, 188 { 189 file: 'mixers6.png', 190 crop: true, 191 rows: [ 192 ['Switch81', 'Switch18', 'Switch44', 'Switch88', 'Switch1616'] 193 ] 194 }, 195 { 196 file: 'vcas.png', 197 crop: true, 198 rows: [ 199 ['VCA', 'VCAmp', 'Velo'] 200 ] 201 }, 202 { 203 file: 'dynamics.png', 204 crop: true, 205 rows: [ 206 ['AMRM', 'Pressor', 'Clpr', 'Lmtr', 'Nsgt', 'CmpDist'] 207 ] 208 }, 209 { 210 file: 'sequencers1.png', 211 crop: true, 212 rows: [ 213 ['EightOne', 'OneEight', 'AddrSeq', 'AddrSeqX'] 214 ] 215 }, 216 { 217 file: 'sequencers2.png', 218 crop: true, 219 rows: [ 220 ['PGMR', 'PGMRX'] 221 ] 222 }, 223 { 224 file: 'visualizers.png', 225 crop: true, 226 rows: [ 227 ['VU', 'Analyzer'] 228 ] 229 }, 230 { 231 file: 'visualizers2.png', 232 crop: true, 233 rows: [ 234 ['AnalyzerXL'] 235 ] 236 }, 237 { 238 file: 'visualizers3.png', 239 crop: true, 240 rows: [ 241 ['Ranalyzer'] 242 ] 243 }, 244 { 245 file: 'poly1.png', 246 crop: true, 247 rows: [ 248 ['Mono', 'Arp', 'Assign', 'Unison'] 249 ] 250 }, 251 { 252 file: 'poly2.png', 253 crop: true, 254 rows: [ 255 ['PolyCon', 'Polycon8', 'PolyOff16', 'PolyOff8', 'PolyMult'] 256 ] 257 }, 258 { 259 file: 'pitch.png', 260 crop: true, 261 rows: [ 262 ['Detune', 'Stack', 'Reftone'] 263 ] 264 }, 265 { 266 file: 'utilities.png', 267 crop: true, 268 rows: [ 269 ['Bool', 'Cmp', 'CVD', 'FlipFlop', 'Inv', 'Manual', 'FourMan', 'Mult', 'Offset', 'Slew', 'Sums', 'Switch', 'Lgsw'] 270 ] 271 }, 272 { 273 file: 'misc.png', 274 crop: true, 275 rows: [ 276 ['Blank3', 'Blank6'] 277 ] 278 } 279 ] 280 281 HP = 15 282 PAD_EDGE = 1 * HP 283 PAD_MODULE = 1 * HP 284 PAD_GROUP = 3 * HP 285 BACKGROUND_FILE = './doc/rack_background.png' 286 287 # require 'chunky_png' 288 require 'oily_png' 289 require 'pp' 290 291 base_dir = File.absolute_path(File.join(File.dirname($0), '..')) 292 screens_dir = File.join(base_dir, 'doc', 'module_screenshots') 293 unless Dir.exist?(screens_dir) 294 STDERR.puts "No screenshots directory: #{screens_dir}\nMake it with ./scripts/make_module_screenshots.rb" 295 exit 1 296 end 297 298 out_dir = File.join(base_dir, 'doc', 'www') 299 unless Dir.exist?(screens_dir) 300 STDERR.puts "No output directory: #{out_dir}" 301 exit 1 302 end 303 304 $row_background = nil 305 def background_for_rows(n) 306 unless $row_background 307 background = ChunkyPNG::Image.from_file(BACKGROUND_FILE) 308 $row_background = background.crop(0, 31, 15 * ((background.dimension.width - 10) / 15), 380) 309 end 310 311 out = ChunkyPNG::Image.new($row_background.dimension.width, n * $row_background.dimension.height, ChunkyPNG::Color::TRANSPARENT) 312 (0...n).each do |i| 313 out.compose!($row_background, 0, i * $row_background.dimension.height) 314 end 315 out 316 end 317 318 screens.each do |screen| 319 next unless screen 320 321 unless screen.key?(:skin) 322 screen[:skin] = 'default' 323 end 324 325 rows = screen[:rows] 326 out = background_for_rows(rows.size) 327 x = 0 328 rows.each_with_index do |row, i| 329 x = PAD_EDGE 330 row.each_with_index do |item, j| 331 if item == '-' 332 unless screen[:nopad] 333 x += PAD_GROUP 334 x -= PAD_MODULE if j > 0 335 end 336 else 337 image = ChunkyPNG::Image.from_file(File.join(screens_dir, screen[:skin], "#{item}.png")) 338 out.compose!(image, x, i * $row_background.dimension.height) 339 x += image.dimension.width 340 x += PAD_MODULE unless screen[:nopad] 341 end 342 end 343 x -= PAD_MODULE if rows.last.size > 0 && !screen[:nopad] 344 x += PAD_EDGE 345 end 346 347 out.crop!(0, 0, x, out.dimension.height) if screen[:crop] 348 349 file = File.join(out_dir, screen[:file]) 350 out.save(file) 351 puts "wrote #{file}" 352 # system("open #{file}") 353 end