skeptick 0.1.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.
Files changed (45) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +584 -0
  5. data/Rakefile +10 -0
  6. data/foo.rb +1 -0
  7. data/lib/skeptick.rb +2 -0
  8. data/lib/skeptick/chain.rb +55 -0
  9. data/lib/skeptick/chain/dsl_context.rb +21 -0
  10. data/lib/skeptick/command.rb +46 -0
  11. data/lib/skeptick/convert.rb +109 -0
  12. data/lib/skeptick/convert/dsl_context.rb +27 -0
  13. data/lib/skeptick/core.rb +46 -0
  14. data/lib/skeptick/error.rb +4 -0
  15. data/lib/skeptick/helper.rb +26 -0
  16. data/lib/skeptick/image.rb +69 -0
  17. data/lib/skeptick/image/dsl_context.rb +29 -0
  18. data/lib/skeptick/railtie.rb +8 -0
  19. data/lib/skeptick/sugar.rb +8 -0
  20. data/lib/skeptick/sugar/composition.rb +55 -0
  21. data/lib/skeptick/sugar/debugging.rb +12 -0
  22. data/lib/skeptick/sugar/drawing.rb +32 -0
  23. data/lib/skeptick/sugar/edges.rb +70 -0
  24. data/lib/skeptick/sugar/formatting.rb +12 -0
  25. data/lib/skeptick/sugar/geometry.rb +38 -0
  26. data/lib/skeptick/sugar/resizing.rb +16 -0
  27. data/lib/skeptick/sugar/sequence_manipulation.rb +43 -0
  28. data/lib/skeptick/version.rb +3 -0
  29. data/logo.png +0 -0
  30. data/logo.rb +45 -0
  31. data/refresh_preview.scpt +2 -0
  32. data/skeptick.gemspec +21 -0
  33. data/test/chain_test.rb +94 -0
  34. data/test/convert_test.rb +177 -0
  35. data/test/image_test.rb +145 -0
  36. data/test/sugar/composition_test.rb +273 -0
  37. data/test/sugar/debugging_test.rb +24 -0
  38. data/test/sugar/drawing_test.rb +86 -0
  39. data/test/sugar/edges_test.rb +99 -0
  40. data/test/sugar/formatting_test.rb +19 -0
  41. data/test/sugar/geometry_test.rb +92 -0
  42. data/test/sugar/resizing_test.rb +25 -0
  43. data/test/sugar/sequence_manipulation_test.rb +98 -0
  44. data/test/test_helper.rb +11 -0
  45. metadata +117 -0
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .rvmrc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in skeptick.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Maxim Chernyak
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,584 @@
1
+ # Skeptick
2
+
3
+ Skeptick is an all-purpose DSL for building and running ImageMagic commands.
4
+ It helps you build any transformations, from trivial resizes to complex mask
5
+ algorithms and free drawing. In a nutshell, Skeptick is nothing more than a
6
+ string manipulator and a process spawner. That's all it's meant to be. However,
7
+ with Skeptick you get quite a few advantages over using plain shell-out or other
8
+ libraries.
9
+
10
+ ## What you get
11
+
12
+ * Clean Ruby syntax to build ImageMagick commands
13
+ * Composable Image objects
14
+ * ImageMagick's `STDERR` output revealed in a Ruby exception
15
+ * Ability to save intermediate images for debugging
16
+ * Minimal memory consumption on shell-outs thanks to
17
+ [posix-spawn](https://github.com/rtomayko/posix-spawn)
18
+ * Emphasis on performing the whole transformation in a single command
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'skeptick'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install skeptick
33
+
34
+ ## Usage
35
+
36
+ To use Skeptick, you simply require it and include the module into your class.
37
+
38
+ ```ruby
39
+ require 'skeptick'
40
+
41
+ class MyClass
42
+ include Skeptick
43
+
44
+ def convert_some_image
45
+ cmd = convert(to: 'result.png') do
46
+ # ...
47
+ end
48
+
49
+ cmd.build
50
+ end
51
+ end
52
+ ```
53
+
54
+ The `cmd` object seen in above example can be inspected to see the exact command
55
+ that Skeptick will run. Simply use `cmd.inspect` or `cmd.to_s`. Skeptick never
56
+ runs anything until you call `build` (except for one very special case), so you
57
+ can inspect commands all you want before executing them.
58
+
59
+ If you don't want to require all of Skeptick, you can just require the core, and
60
+ and select any specific sugar you want.
61
+
62
+ ```ruby
63
+ require 'skeptick/core'
64
+ require 'skeptick/sugar/resizing'
65
+ require 'skeptick/sugar/composition'
66
+ ```
67
+
68
+ See the `lib/skeptick/sugar` dir for all the goodies.
69
+
70
+ In Rails Skeptick will automatically use `Rails.logger` and `Rails.root` as
71
+ `cd_path`. You can also configure your own.
72
+
73
+ ```ruby
74
+ Skeptick.logger = MyLogger.new
75
+ Skeptick.cd_path = '/some/dir'
76
+ ```
77
+
78
+ You can enable `debug_mode` to display every executed command in the log.
79
+
80
+ ```ruby
81
+ Skeptick.debug_mode = true
82
+ ```
83
+
84
+ ## DSL
85
+
86
+ ![Skeptick Logo](https://raw.github.com/maxim/skeptick/master/logo.png)
87
+
88
+ This picture is produced with the following script
89
+
90
+ ```ruby
91
+ include Skeptick
92
+
93
+ image_size = '400x120'
94
+ left, top = 8, 80
95
+
96
+ # Build a picture with built-in tile:granite: texture and using the
97
+ # skeptick-provided sugar method `rounded_corners_image`
98
+ paper = rounded_corners_image(size: image_size, radius: 25) do
99
+ set :size, image_size
100
+ image 'tile:granite:'
101
+ apply '-brightness-contrast', '38x-33'
102
+ apply :blur, '0x0.5'
103
+ end
104
+
105
+ # Build a text image that says "Skeptick" using specified font, add gradient
106
+ text = image do
107
+ canvas :none, size: '395x110'
108
+ font 'Handwriting - Dakota Regular'
109
+ set :pointsize, 90
110
+ set :fill, 'gradient:#37e-#007'
111
+ write 'Skeptick', left: left, top: top
112
+ apply :blur, '0x0.7'
113
+ end
114
+
115
+ bezier = \
116
+ "#{left + 17 }, #{top + 17} #{left + 457}, #{top - 13} " +
117
+ "#{left + 377}, #{top + 27} #{left + 267}, #{top + 27}"
118
+
119
+ # Draw a curve that will appear underneath the text using bezier coordinates
120
+ curve = image do
121
+ canvas :none, size: '395x110'
122
+ set :strokewidth, 2
123
+ set :stroke, 'gradient:#37e-#007'
124
+ draw "fill none bezier #{bezier}"
125
+ end
126
+
127
+ # Combine text and curve using `:over` blending, multiply it with paper using
128
+ # `:multiply` blending, and add a torn effect using Skeptick-provided sugar
129
+ # method `torn_paper_image`
130
+ torn = torn_paper_image(
131
+ paper * (text + curve),
132
+ spread: 50,
133
+ blur: '3x10'
134
+ )
135
+
136
+ # Create a convert command with all of the above and run it
137
+ logo = convert(torn, to: "#{File.dirname(__FILE__)}/logo.png")
138
+ logo.build
139
+
140
+ # This is what the resulting command looks like
141
+ # You can see it by running `logo.to_s`
142
+ #
143
+ # convert (
144
+ # (
145
+ # (
146
+ # -size 400x120 tile:granite:
147
+ # -brightness-contrast 38x-33 -blur 0x0.5
148
+ # (
149
+ # +clone -alpha transparent -background none
150
+ # -draw roundrectangle 1,1 400,120 25,25
151
+ # )
152
+ # -alpha set -compose dstin -composite
153
+ # )
154
+ #
155
+ # (
156
+ # -size 395x110 canvas:none
157
+ # -font Handwriting---Dakota-Regular -pointsize 90
158
+ # -fill gradient:#37e-#007 -draw text 8,80 'Skeptick'
159
+ # -blur 0x0.7 -size 395x110 canvas:none -strokewidth 2
160
+ # -stroke gradient:#37e-#007
161
+ # -draw fill none
162
+ # bezier 25, 97 465, 67 385, 107 275, 107
163
+ # -compose over -composite
164
+ # )
165
+ #
166
+ # -compose multiply -composite
167
+ # )
168
+ #
169
+ # (
170
+ # +clone -alpha extract -virtual-pixel black -spread 50
171
+ # -blur 0x3 -threshold 50% -spread 1 -blur 0x.7
172
+ # )
173
+ #
174
+ # -alpha off -compose copy_opacity -composite
175
+ # ) logo.png
176
+
177
+ ```
178
+
179
+ ## All those little commands
180
+
181
+ A lot of things happened in the above script, no worries, it's just a showcase.
182
+ I bet the first thing you noticed is a shitstorm of little method names like
183
+ `apply`, `canvas`, `font`, `write`, `draw`, etc. Well, they are all sugar. We
184
+ will cover sugar later in teh given parchment.
185
+
186
+ There are actually only three real commands in all of Skeptick: `convert`,
187
+ `set`, and `image`.
188
+
189
+ ### Convert
190
+
191
+ `convert` can be used both outside and inside a transformation block. You could
192
+ say for example this.
193
+
194
+ ```ruby
195
+ command = convert('image1.png', to: 'image2.png') do
196
+ set '-resize', '200x200'
197
+ end
198
+
199
+ # OUTPUT:
200
+ # convert image1.png -resize 200x200 image2.png
201
+ ```
202
+
203
+ Or you could put it inside, and it will become a parenthesized subcommand.
204
+
205
+ ```ruby
206
+ command = convert('image1.png', to: 'image2.png') do
207
+ convert do
208
+ set '+clone' # pull in image1 into parentheses
209
+ set '-resize 100x100' # resize image1's clone in memory
210
+ end
211
+
212
+ set '-compose over'
213
+ set '-composite'
214
+ end
215
+
216
+ # OUTPUT:
217
+ # convert image1.png ( +clone -resize 100x100 )
218
+ # -compose over -composite image2.png
219
+ ```
220
+
221
+ If you love parentheses a lot, you could nest `convert` infinitely. However,
222
+ ImageMagick's `clone`, `delete`, and `swap` are your friends, learn them to
223
+ cure parenthethitis.
224
+
225
+ Oh, speaking of nesting — we can reuse that whole command inside another command
226
+ by passing it to `convert` in place of an image filepath.
227
+
228
+ ```ruby
229
+ new_command = convert(command, to: 'whatever.png') do
230
+ set '-resize 300x300'
231
+ end
232
+
233
+ # OUTPUT:
234
+ # convert
235
+ # ( image1.png ( +clone -resize 100x100 ) -compose over -composite )
236
+ # -resize 300x300 whatever.png
237
+ ```
238
+
239
+ See what I did there? It's composability. If you have a `convert` object in a
240
+ variable, you can use it inside another `convert` object down the line.
241
+
242
+ ### Set
243
+
244
+ `set` appends a string to your command. You can give it any arguments, it
245
+ doesn't care, it will just `to_s` and concatenate them.
246
+
247
+ ```ruby
248
+ # All same thing
249
+ set '-resize 100x100'
250
+ set '-resize', '100x100'
251
+ set :resize, '100x100'
252
+ ```
253
+
254
+ Yeah that last one is special convenience. If an argument to `set` is a symbol,
255
+ it will convert it to `"-#{symbol}"`. If you need `+resize` type of thing you'd
256
+ just have to use a string, or sugar, but later on that.
257
+
258
+ ### Image
259
+
260
+ `image` is very similar to `convert`. However, `convert` is a command object
261
+ that may contain many images, settings, operators, nested converts, etc. Image
262
+ is also a command object that can contain many settings and operators, but it
263
+ can only contain one image reference inside of it. The reference can be a path,
264
+ a nested convert, or a special string representing a built-in imagemagick image,
265
+ but it can be only one.
266
+
267
+ ```ruby
268
+ command = convert(to: '/path/to/result.png') do
269
+ image '/path/to/image.png'
270
+ set :resize, '200x200'
271
+ end
272
+
273
+ # OUTPUT:
274
+ # convert /path/to/image.png -resize 200x200 /path/to/result.png
275
+ ```
276
+
277
+ In this case we declared an image inside a `convert` which references a path.
278
+ Instead we could create an image that references a built-in image.
279
+
280
+ ```ruby
281
+ command = convert(to: '/path/to/result.png') do
282
+ image 'rose:'
283
+ end
284
+
285
+ # OUTPUT:
286
+ # convert rose: /path/to/result.png
287
+ ```
288
+
289
+ You can save image objects in variables, and pass them around, but unlike
290
+ `convert`, you cannot run them standalone.
291
+
292
+ ```ruby
293
+ rose_image = image('rose:')
294
+ command = convert(rose_image, to: '/path/to/result.png')
295
+
296
+ # OUTPUT:
297
+ # convert rose: /path/to/result.png
298
+ ```
299
+ See, we had to wrap it in a `convert` in order to use it. You could also append
300
+ this image at any point inside the convert block.
301
+
302
+ ```ruby
303
+ rose_image = image('rose:')
304
+ command = convert(to: '/path/to/result.png') do
305
+ image rose_image
306
+ end
307
+
308
+ # OUTPUT:
309
+ # convert rose: /path/to/result.png
310
+ ```
311
+
312
+ As mentioned above, an image can come with its own settings and operators.
313
+
314
+ ```ruby
315
+ rose_image = image do
316
+ set :background, 'transparent'
317
+ image 'rose:'
318
+ apply :resize, '200x200'
319
+ end
320
+
321
+ command = convert(to: '/path/to/result.png') do
322
+ image rose_image
323
+ end
324
+ ```
325
+
326
+ You could do all of this inline, the output will be the same.
327
+
328
+ ```ruby
329
+ command = convert(to: '/path/to/result.png') do
330
+ image do
331
+ set :background, 'transparent'
332
+ image 'rose:'
333
+ apply :resize, '200x200'
334
+ end
335
+ end
336
+
337
+ # OUTPUT:
338
+ # convert -background transparent rose: -resize 200x200 /path/to/result.png
339
+ ```
340
+
341
+ If you have a `convert` object you can pass it as an image too.
342
+
343
+ ```ruby
344
+ saved_convert = convert(to: 'foo.png') do
345
+ image 'rose:'
346
+ set :resize, '200x200'
347
+ end
348
+
349
+ another_convert = convert(to: 'bar.png') do
350
+ image saved_convert
351
+ apply :blur, '0x0.5'
352
+ end
353
+
354
+ # OUTPUT
355
+ # convert ( rose: -resize 200x200 ) -blur 0x0.5 bar.png
356
+ ```
357
+
358
+ Nesting possibilities are endless.
359
+
360
+ ## Sugar
361
+
362
+ Skeptick comes with a bunch of sugar. When you require Skeptick, you can simply
363
+ require everything. This includes all the sugar.
364
+
365
+ ```ruby
366
+ require 'skeptick'
367
+ ```
368
+
369
+ However, you can require just the core stuff described above, and select any
370
+ sugar you want.
371
+
372
+ ```ruby
373
+ require 'skeptick/core'
374
+ require 'skeptick/sugar/composition'
375
+ ```
376
+
377
+ ### Composition Sugar
378
+
379
+ Composition is sugar that adds `compose` shortcut to Skeptick's DSL.
380
+
381
+ ```ruby
382
+ command = compose(:multiply, 'a.png', 'b.png', to: 'out.png') do
383
+ with '-resize', '200x200'
384
+ end
385
+
386
+ # OUTPUT:
387
+ # convert a.png b.png -compose multiply -resize 200x200 -composite out.png
388
+ ```
389
+
390
+ It takes the blending type as the first argument, and injects some extra stuff
391
+ into the resulting command, but really it's just a wrapper around `convert` as
392
+ you could easily see in its implementation.
393
+
394
+ ```ruby
395
+ def compose(blending, *args, &blk)
396
+ convert(*args, &blk).tap do |c|
397
+ c.append :compose, blending.to_s
398
+ c.append :composite
399
+ end
400
+ end
401
+ ```
402
+
403
+ As usual, you don't have to list your images as method arguments like that.
404
+ Instead you could declare them inside the block using the `image` method. The
405
+ following command does the same thing.
406
+
407
+ ```ruby
408
+ command = compose(:multiply, to: 'out.png') do
409
+ image 'a.png'
410
+ image 'b.png'
411
+ with '-resize', '200x200'
412
+ end
413
+ ```
414
+
415
+ Since most of Skeptick's power comes from the ability to infinitely nest things,
416
+ here's a an example involving a nested `compose`.
417
+
418
+ ```ruby
419
+ command = convert('image1.png', to: 'result.png') do
420
+ compose(:multiply) do
421
+ image 'image3.png[200x200]'
422
+
423
+ convert 'image4.png' do
424
+ with '-unsharp', '0x5'
425
+ end
426
+
427
+ end
428
+ end
429
+
430
+ # OUTPUT:
431
+ # convert
432
+ # image1.png ( image3.png[200x200] ( image4.png -unsharp 0x5 ) -compose
433
+ # multiply -composite ) result.png"
434
+ ```
435
+
436
+ Notice how we nest `compose` inside of `convert`, and then `convert` inside of
437
+ `compose`. The output of each acts like any declared image. In other words,
438
+ wherever you would write `image "foo.png"` you could also write a nested
439
+ command.
440
+
441
+ ### Composition Operators
442
+
443
+ This is more of a gimmick than a real feature, but you can use math operators
444
+ like `+`, `-`, `*`, `/`, `&`, `|` to compose images. These are all based on
445
+ `compose` method. Here's a multiply example.
446
+
447
+ ```ruby
448
+ image1 = image('foo.png')
449
+ image2 = image('bar.png')
450
+ result = convert(image1 * image2, to: 'baz.png')
451
+
452
+ # OUTPUT:
453
+ # convert ( foo.png bar.png -compose multiply -composite ) baz.png
454
+ ```
455
+
456
+ As you can see, this is equivalent of simply using `compose`.
457
+
458
+ ```ruby
459
+ # Same thing
460
+ result = compose(:multiply, 'foo.png', 'bar.png', to: 'baz.png')
461
+ ```
462
+
463
+ Check out `lib/skeptick/sugar/composition.rb` for what these operators do.
464
+
465
+ ### Sequence Manipulation Sugar
466
+
467
+ Skeptick provides methods `clone`, `delete`, and `swap` to
468
+ manipulate declared images in a sequence, just like in ImageMagick CLI.
469
+
470
+ ```ruby
471
+ command = compose(:over, 'image1.png', to: 'result.png') do
472
+ # You could think of image sequence as a ruby array. Here's what it would
473
+ # look like right now.
474
+ # [ 'image1.png' ]
475
+
476
+ compose(:multiply) do
477
+ image 'mask.png' # loading another image for this operation
478
+ clone(0) # cloning image1.png from outside "into parentheses"
479
+ end
480
+
481
+ # Sequence at this point:
482
+ # [ 'image1.png', 'result of compose(:multiply)' ]
483
+
484
+ delete(0) # deleting image1.png from the sequence and from memory
485
+
486
+ # Sequence at this point:
487
+ # [ 'result of compose(:multiply)' ]
488
+
489
+ # At this point the only image loaded in memory is the one produced by the
490
+ # compose(:multiply) command above. Let's load another one.
491
+
492
+ image 'image2.png'
493
+
494
+ # Sequence at this point:
495
+ # [ 'result of compose(:multiply)', 'image2.png' ]
496
+
497
+ # Now we have two images in the sequence. We can swap them in case we need
498
+ # to change their order.
499
+
500
+ swap
501
+
502
+ # Sequence at this point:
503
+ # [ 'image2.png', 'result of compose(:multiply)' ]
504
+
505
+ # Now image2.png is first in the sequence, and the output of
506
+ # compose(:multiply) is second. Since our outermost command is compose(:over),
507
+ # at this point these 2 images will be composed over each other, and the
508
+ # result written to result.png.
509
+ end
510
+
511
+ # OUTPUT
512
+ # convert
513
+ # image1.png ( mask.png -clone 0 -compose multiply -composite )
514
+ # -delete 0 image2.png +swap -compose over -composite result.png
515
+ ```
516
+
517
+ You can use `clone` and `delete` to refer to multiple images at once by passing
518
+ mutliple indexes as arguments, like `clone(0,1,2)` or `delete(0,1)`. Ranges are
519
+ also accepted. Without any arguments `clone` and `delete` are translated to
520
+ ImageMagick's `+clone` and `+delete`. They then refer to the last image in the
521
+ sequence. Same with `swap` - you can provide two indexes in arguments like
522
+ `swap(1,3)` to swap any 2 images in the sequence, or without arguments it'll
523
+ act as `+swap` - which swaps last two images.
524
+
525
+ ### Debugging Sugar
526
+
527
+ Sometimes you might want to take a look at an intermediate image that's being
528
+ generated inside parentheses, nested somewhere in your command. You can do so
529
+ with the help of `write('/path/to/img.png')`.
530
+
531
+ ```ruby
532
+ command = convert(to: 'result.png') do
533
+ compose(:multiply, 'a.png', 'b.png') do
534
+ write('~/Desktop/debug.png')
535
+ end
536
+
537
+ set '-resize', '200x200'
538
+ end
539
+ ```
540
+
541
+ In this case the result of inner `compose` command will be written to desktop,
542
+ without affecting anything else. Again, this is a feature that already exists
543
+ in ImageMagick, as becomes apparent from the resulting command.
544
+
545
+ convert
546
+ ( a.png b.png -compose multiply -composite -write ~/Desktop/debug.png )
547
+ -resize 200x200 result.png
548
+
549
+ ## Chain
550
+
551
+ This is rarely (if ever) needed, but with Skeptick you could easily create
552
+ piped commands.
553
+
554
+ ```ruby
555
+ command = chain(to: 'result.png') do
556
+ compose(:hardlight, 'a.png', 'b.png') do
557
+ with '-brightness-contrast', '2x4'
558
+ end
559
+
560
+ compose(:atop, 'c.png', :pipe)
561
+ end
562
+
563
+ # OUTPUT:
564
+ convert
565
+ a.png b.png -compose hardlight -brightness-contrast 2x4 -composite miff:- |
566
+ convert
567
+ c.png miff:- -compose atop -composite result.png
568
+ ```
569
+
570
+ Two things to note here. First of all, commands that are declared in the `chain`
571
+ block will become piped together. Second, we use a special `:pipe` symbol in
572
+ the last `compose` command. This symbol indicates where the piped-in image
573
+ should appear in the image sequence. You can see this in the output string. The
574
+ `miff:-` appears after c.png, as expected.
575
+
576
+ Documentation is to be continued...
577
+
578
+ ## Contributing
579
+
580
+ 1. Fork it
581
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
582
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
583
+ 4. Push to the branch (`git push origin my-new-feature`)
584
+ 5. Create new Pull Request