squib 0.6.0 → 0.7.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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +162 -133
  3. data/Gemfile +4 -4
  4. data/README.md +630 -550
  5. data/RELEASE TODO.md +18 -18
  6. data/Rakefile +99 -99
  7. data/lib/squib.rb +32 -32
  8. data/lib/squib/api/background.rb +20 -19
  9. data/lib/squib/api/data.rb +100 -99
  10. data/lib/squib/api/image.rb +90 -76
  11. data/lib/squib/api/save.rb +149 -103
  12. data/lib/squib/api/settings.rb +35 -37
  13. data/lib/squib/api/shapes.rb +230 -228
  14. data/lib/squib/api/text.rb +65 -66
  15. data/lib/squib/api/text_embed.rb +96 -66
  16. data/lib/squib/args/arg_loader.rb +138 -0
  17. data/lib/squib/args/box.rb +55 -0
  18. data/lib/squib/args/card_range.rb +32 -0
  19. data/lib/squib/args/color_validator.rb +12 -0
  20. data/lib/squib/args/coords.rb +33 -0
  21. data/lib/squib/args/dir_validator.rb +16 -0
  22. data/lib/squib/args/draw.rb +92 -0
  23. data/lib/squib/args/embed_adjust.rb +25 -0
  24. data/lib/squib/args/embed_key.rb +17 -0
  25. data/lib/squib/args/hand_special.rb +37 -0
  26. data/lib/squib/args/input_file.rb +37 -0
  27. data/lib/squib/args/paint.rb +44 -0
  28. data/lib/squib/args/paragraph.rb +115 -0
  29. data/lib/squib/args/save_batch.rb +60 -0
  30. data/lib/squib/args/scale_box.rb +53 -0
  31. data/lib/squib/args/sheet.rb +72 -0
  32. data/lib/squib/args/showcase_special.rb +38 -0
  33. data/lib/squib/args/svg_special.rb +37 -0
  34. data/lib/squib/args/transform.rb +25 -0
  35. data/lib/squib/args/typographer.rb +117 -117
  36. data/lib/squib/card.rb +67 -67
  37. data/lib/squib/conf.rb +117 -111
  38. data/lib/squib/constants.rb +178 -178
  39. data/lib/squib/deck.rb +113 -111
  40. data/lib/squib/graphics/cairo_context_wrapper.rb +99 -53
  41. data/lib/squib/graphics/gradient_regex.rb +46 -46
  42. data/lib/squib/graphics/hand.rb +42 -43
  43. data/lib/squib/graphics/image.rb +76 -73
  44. data/lib/squib/graphics/save_doc.rb +103 -137
  45. data/lib/squib/graphics/save_images.rb +33 -33
  46. data/lib/squib/graphics/shapes.rb +119 -152
  47. data/lib/squib/graphics/showcase.rb +85 -88
  48. data/lib/squib/graphics/text.rb +176 -216
  49. data/lib/squib/layout_parser.rb +91 -89
  50. data/lib/squib/layouts/economy.yml +85 -0
  51. data/lib/squib/layouts/fantasy.yml +101 -0
  52. data/lib/squib/layouts/hand.yml +62 -46
  53. data/lib/squib/layouts/playing-card.yml +35 -18
  54. data/lib/squib/project_template/config.yml +45 -40
  55. data/lib/squib/version.rb +10 -10
  56. data/samples/color_shortcuts.rb +6 -0
  57. data/samples/csv_import.rb +18 -18
  58. data/samples/custom-config.yml +5 -5
  59. data/samples/custom_config.rb +18 -18
  60. data/samples/draw_shapes.rb +45 -35
  61. data/samples/embed_text.rb +88 -90
  62. data/samples/hand.rb +24 -24
  63. data/samples/layouts.rb +62 -61
  64. data/samples/layouts_builtin.rb +51 -0
  65. data/samples/load_images.rb +78 -64
  66. data/samples/ranges.rb +64 -53
  67. data/samples/sample.csv +2 -2
  68. data/samples/text_options.rb +102 -94
  69. data/spec/api/api_data_spec.rb +57 -50
  70. data/spec/api/api_settings_spec.rb +37 -17
  71. data/spec/args/box_spec.rb +127 -0
  72. data/spec/args/draw_spec.rb +95 -0
  73. data/spec/args/embed_key_spec.rb +13 -0
  74. data/spec/args/input_file_spec.rb +21 -0
  75. data/spec/args/paint_spec.rb +22 -0
  76. data/spec/args/paragraph_spec.rb +153 -0
  77. data/spec/args/range_spec.rb +36 -0
  78. data/spec/args/save_batch_spec.rb +51 -0
  79. data/spec/args/scale_box_spec.rb +71 -0
  80. data/spec/args/sheet_spec.rb +58 -0
  81. data/spec/args/showcase_special_spec.rb +15 -0
  82. data/spec/data/samples/autoscale_font.rb.txt +84 -87
  83. data/spec/data/samples/basic.rb.txt +209 -203
  84. data/spec/data/samples/cairo_access.rb.txt +2 -2
  85. data/spec/data/samples/config_text_markup.rb.txt +72 -75
  86. data/spec/data/samples/csv_import.rb.txt +76 -80
  87. data/spec/data/samples/custom_config.rb.txt +48 -49
  88. data/spec/data/samples/draw_shapes.rb.txt +100 -42
  89. data/spec/data/samples/embed_text.rb.txt +283 -295
  90. data/spec/data/samples/excel.rb.txt +162 -171
  91. data/spec/data/samples/gradients.rb.txt +79 -67
  92. data/spec/data/samples/hand.rb.txt +538 -514
  93. data/spec/data/samples/hello_world.rb.txt +36 -38
  94. data/spec/data/samples/load_images.rb.txt +41 -5
  95. data/spec/data/samples/portrait-landscape.rb.txt +49 -51
  96. data/spec/data/samples/ranges.rb.txt +460 -429
  97. data/spec/data/samples/saves.rb.txt +801 -785
  98. data/spec/data/samples/showcase.rb.txt +5910 -5906
  99. data/spec/data/samples/text_options.rb.txt +1125 -981
  100. data/spec/data/samples/tgc_proofs.rb.txt +81 -79
  101. data/spec/data/samples/units.rb.txt +18 -12
  102. data/spec/data/xlsx/with_macros.xlsm +0 -0
  103. data/spec/graphics/cairo_context_wrapper_spec.rb +84 -75
  104. data/spec/graphics/graphics_images_spec.rb +94 -85
  105. data/spec/graphics/graphics_save_doc_spec.rb +67 -65
  106. data/spec/samples/expected/hand.png +0 -0
  107. data/spec/samples/expected/hand_pretty.png +0 -0
  108. data/spec/samples/expected/layout_00.png +0 -0
  109. data/spec/samples/expected/load_images_00.png +0 -0
  110. data/spec/samples/expected/ranges_00.png +0 -0
  111. data/spec/samples/expected/shape_00.png +0 -0
  112. data/spec/samples/expected/showcase.png +0 -0
  113. data/spec/samples/expected/showcase2.png +0 -0
  114. data/spec/samples/expected/showcase_individual_00.png +0 -0
  115. data/spec/samples/expected/showcase_individual_01.png +0 -0
  116. data/spec/samples/expected/showcase_individual_02.png +0 -0
  117. data/spec/samples/expected/showcase_individual_03.png +0 -0
  118. data/spec/samples/expected/text_00.png +0 -0
  119. data/spec/samples/expected/text_01.png +0 -0
  120. data/spec/samples/expected/text_02.png +0 -0
  121. data/spec/samples/samples_regression_spec.rb +82 -82
  122. data/spec/spec_helper.rb +3 -2
  123. data/squib.gemspec +48 -48
  124. data/squib.sublime-project +42 -36
  125. metadata +61 -33
  126. data/lib/squib/input_helpers.rb +0 -238
  127. data/spec/api/api_image_spec.rb +0 -38
  128. data/spec/api/api_text_spec.rb +0 -37
  129. data/spec/graphics/graphics_shapes_spec.rb +0 -85
  130. data/spec/graphics/graphics_text_spec.rb +0 -164
  131. data/spec/input_helpers_spec.rb +0 -238
  132. data/spec/samples/expected/embed_multi_00.png +0 -0
  133. data/spec/samples/expected/embed_multi_01.png +0 -0
  134. data/spec/samples/expected/embed_multi_02.png +0 -0
  135. data/spec/samples/expected/ranges_01.png +0 -0
  136. data/spec/samples/expected/ranges_02.png +0 -0
@@ -1,33 +1,33 @@
1
- module Squib
2
- class Card
3
-
4
- # :nodoc:
5
- # @api private
6
- def save_png(i, dir, prefix, count_format, do_rotate, angle)
7
- if [true, :clockwise, :counterclockwise].include?(do_rotate)
8
- surface = rotated_image(angle)
9
- else
10
- surface = @cairo_surface
11
- end
12
- write_png(surface, i, dir, prefix, count_format)
13
- end
14
-
15
- # :nodoc:
16
- # @api private
17
- def rotated_image(angle)
18
- rotated_cc = Cairo::Context.new(Cairo::ImageSurface.new(@height, @width) )
19
- rotated_cc.translate(@height * 0.5, @width * 0.5)
20
- rotated_cc.rotate(angle)
21
- rotated_cc.translate(@width * -0.5, @height * -0.5)
22
- rotated_cc.set_source(@cairo_surface)
23
- rotated_cc.paint
24
- rotated_cc.target
25
- end
26
- # :nodoc:
27
- # @api private
28
- def write_png(surface, i, dir, prefix, count_format)
29
- surface.write_to_png("#{dir}/#{prefix}#{count_format % i}.png")
30
- end
31
-
32
- end
33
- end
1
+ module Squib
2
+ class Card
3
+
4
+ # :nodoc:
5
+ # @api private
6
+ def save_png(batch)
7
+ surface = if batch.rotate
8
+ rotated_image(batch.angle)
9
+ else
10
+ surface = @cairo_surface
11
+ end
12
+ write_png(surface, index, batch.dir, batch.prefix, batch.count_format)
13
+ end
14
+
15
+ # :nodoc:
16
+ # @api private
17
+ def rotated_image(angle)
18
+ rotated_cc = Cairo::Context.new(Cairo::ImageSurface.new(@height, @width) )
19
+ rotated_cc.translate(@height * 0.5, @width * 0.5)
20
+ rotated_cc.rotate(angle)
21
+ rotated_cc.translate(@width * -0.5, @height * -0.5)
22
+ rotated_cc.set_source(@cairo_surface)
23
+ rotated_cc.paint
24
+ rotated_cc.target
25
+ end
26
+ # :nodoc:
27
+ # @api private
28
+ def write_png(surface, i, dir, prefix, count_format)
29
+ surface.write_to_png("#{dir}/#{prefix}#{count_format % i}.png")
30
+ end
31
+
32
+ end
33
+ end
@@ -1,152 +1,119 @@
1
- module Squib
2
- # @api private
3
- class Card
4
-
5
- # :nodoc:
6
- # @api private
7
- def rect(x, y, width, height, x_radius, y_radius, fill_color, stroke_color, stroke_width)
8
- width = @width if width == :native
9
- height = @height if height == :native
10
- use_cairo do |cc|
11
- cc.rounded_rectangle(x, y, width, height, x_radius, y_radius)
12
- cc.set_source_squibcolor(stroke_color)
13
- cc.set_line_width(stroke_width)
14
- cc.stroke
15
- cc.rounded_rectangle(x, y, width, height, x_radius, y_radius)
16
- cc.set_source_squibcolor(fill_color)
17
- cc.fill
18
- end
19
- end
20
-
21
- # :nodoc:
22
- # @api private
23
- def circle(x, y, radius, fill_color, stroke_color, stroke_width)
24
- use_cairo do |cc|
25
- cc.move_to(x + radius, y)
26
- cc.circle(x, y, radius)
27
- cc.set_source_squibcolor(stroke_color)
28
- cc.set_line_width(stroke_width)
29
- cc.stroke
30
- cc.circle(x, y, radius)
31
- cc.set_source_squibcolor(fill_color)
32
- cc.fill
33
- end
34
- end
35
-
36
- # Ellipse drawing taken from looking at the control points in Inkscape
37
- # Think of it like a rectangle. Curves go from mid-points of the sides
38
- # of the rectangle. Control points are at 1/4 and 3/4 of the side.
39
- # :nodoc:
40
- # @api private
41
- def ellipse(x, y, w, h, fill_color, stroke_color, stroke_width)
42
- use_cairo do |cc|
43
- cc.move_to(x, y + 0.5*h) # start west
44
- cc.curve_to(x, y + 0.25*h, # west to north
45
- x + 0.25*w, y,
46
- x + 0.5*w, y)
47
- cc.curve_to(x + 0.75*w, y, # north to east
48
- x + w, y + 0.25*h,
49
- x + w, y + 0.5*h)
50
- cc.curve_to(x + w, y + 0.75*h, # east to south
51
- x + 0.75*w, y + h,
52
- x + 0.5*w, y + h)
53
- cc.curve_to(x + 0.25*w, y + h, # south to west
54
- x, y + 0.75*h,
55
- x, y + 0.5*h)
56
- cc.set_source_squibcolor(stroke_color)
57
- cc.set_line_width(stroke_width)
58
- cc.stroke_preserve
59
- cc.set_source_squibcolor(fill_color)
60
- cc.fill
61
- end
62
- end
63
-
64
- # :nodoc:
65
- # @api private
66
- def triangle(x1, y1, x2, y2, x3, y3, fill_color, stroke_color, stroke_width)
67
- use_cairo do |cc|
68
- cc.triangle(x1, y1, x2, y2, x3, y3)
69
- cc.set_source_squibcolor(stroke_color)
70
- cc.set_line_width(stroke_width)
71
- cc.stroke
72
- cc.triangle(x1, y1, x2, y2, x3, y3)
73
- cc.set_source_squibcolor(fill_color)
74
- cc.fill
75
- end
76
- end
77
-
78
- # :nodoc:
79
- # @api private
80
- def line(x1, y1, x2, y2, stroke_color, stroke_width)
81
- use_cairo do |cc|
82
- cc.move_to(x1, y1)
83
- cc.line_to(x2, y2)
84
- cc.set_source_squibcolor(stroke_color)
85
- cc.set_line_width(stroke_width)
86
- cc.stroke
87
- end
88
- end
89
-
90
- # :nodoc:
91
- # @api private
92
- def curve(x1, y1, cx1, cy1, x2, y2, cx2, cy2, fill_color, stroke_color, stroke_width)
93
- use_cairo do |cc|
94
- cc.move_to(x1, y1)
95
- cc.curve_to(cx1, cy1, cx2, cy2, x2, y2)
96
- cc.set_line_width(stroke_width)
97
- cc.set_source_squibcolor(stroke_color)
98
- cc.stroke
99
- cc.move_to(x1, y1)
100
- cc.curve_to(cx1, cy1, cx2, cy2, x2, y2)
101
- cc.set_source_squibcolor(fill_color)
102
- cc.fill
103
- end
104
- end
105
-
106
- # :nodoc:
107
- # @api private
108
- def star(x, y, n, angle, inner_radius, outer_radius, fill_color, stroke_color, stroke_width)
109
- use_cairo do |cc|
110
- cc.translate(x, y)
111
- cc.rotate(angle)
112
- cc.translate(-x, -y)
113
- cc.move_to(x + outer_radius, y) #i = 0, so cos(0)=1 and sin(0)=0
114
- theta = Math::PI / n.to_f # i.e. (2*pi) / (2*n)
115
- 0.upto(2 * n) do |i|
116
- radius = i.even? ? outer_radius : inner_radius
117
- cc.line_to(x + radius * Math::cos(i * theta),
118
- y + radius * Math::sin(i * theta))
119
- end
120
- cc.close_path
121
- cc.set_source_squibcolor(stroke_color)
122
- cc.set_line_width(stroke_width)
123
- cc.fill_preserve
124
- cc.set_source_squibcolor(fill_color)
125
- cc.stroke
126
- end
127
- end
128
-
129
- # :nodoc:
130
- # @api private
131
- def polygon(x, y, n, angle, radius, fill_color, stroke_color, stroke_width)
132
- use_cairo do |cc|
133
- cc.translate(x, y)
134
- cc.rotate(angle)
135
- cc.translate(-x, -y)
136
- cc.move_to(x + radius, y) # i = 0, so cos(0)=1 and sin(0)=0
137
- theta = (2 * Math::PI) / n.to_f
138
- 0.upto(n) do |i|
139
- cc.line_to(x + radius * Math::cos(i * theta),
140
- y + radius * Math::sin(i * theta))
141
- end
142
- cc.close_path
143
- cc.set_source_squibcolor(stroke_color)
144
- cc.set_line_width(stroke_width)
145
- cc.fill_preserve
146
- cc.set_source_squibcolor(fill_color)
147
- cc.stroke
148
- end
149
- end
150
-
151
- end
152
- end
1
+ module Squib
2
+ # @api private
3
+ class Card
4
+
5
+ # :nodoc:
6
+ # @api private
7
+ def rect(box, draw)
8
+ use_cairo do |cc|
9
+ cc.rounded_rectangle(box.x, box.y, box.width, box.height, box.x_radius, box.y_radius)
10
+ cc.fill_n_stroke(draw)
11
+ end
12
+ end
13
+
14
+ # :nodoc:
15
+ # @api private
16
+ def circle(box, draw)
17
+ x, y, r = box.x, box.y, box.radius
18
+ use_cairo do |cc|
19
+ cc.move_to(x + r, y)
20
+ cc.circle(x, y, r)
21
+ cc.fill_n_stroke(draw)
22
+ end
23
+ end
24
+
25
+ # Ellipse drawing taken from looking at the control points in Inkscape
26
+ # Think of it like a rectangle. Curves go from mid-points of the sides
27
+ # of the rectangle. Control points are at 1/4 and 3/4 of the side.
28
+ # :nodoc:
29
+ # @api private
30
+ def ellipse(box, draw)
31
+ x, y, w, h = box.x, box.y, box.width, box.height
32
+ use_cairo do |cc|
33
+ cc.move_to(x, y + 0.5*h) # start west
34
+ cc.curve_to(x, y + 0.25*h, # west to north
35
+ x + 0.25*w, y,
36
+ x + 0.5*w, y)
37
+ cc.curve_to(x + 0.75*w, y, # north to east
38
+ x + w, y + 0.25*h,
39
+ x + w, y + 0.5*h)
40
+ cc.curve_to(x + w, y + 0.75*h, # east to south
41
+ x + 0.75*w, y + h,
42
+ x + 0.5*w, y + h)
43
+ cc.curve_to(x + 0.25*w, y + h, # south to west
44
+ x, y + 0.75*h,
45
+ x, y + 0.5*h)
46
+ cc.fill_n_stroke(draw)
47
+ end
48
+ end
49
+
50
+ # :nodoc:
51
+ # @api private
52
+ def triangle(tri, draw)
53
+ use_cairo do |cc|
54
+ cc.triangle(tri.x1, tri.y1, tri.x2, tri.y2, tri.x3, tri.y3)
55
+ cc.fill_n_stroke(draw)
56
+ end
57
+ end
58
+
59
+ # :nodoc:
60
+ # @api private
61
+ def line(coord, draw)
62
+ use_cairo do |cc|
63
+ cc.move_to(coord.x1, coord.y1)
64
+ cc.line_to(coord.x2, coord.y2)
65
+ cc.fill_n_stroke(draw)
66
+ end
67
+ end
68
+
69
+ # :nodoc:
70
+ # @api private
71
+ def curve(bez, draw)
72
+ x1, y1, cx1, cy1 = bez.x1, bez.y1, bez.cx1, bez.cy1
73
+ cx2, cy2, x2, y2 = bez.cx2, bez.cy2, bez.x2, bez.y2
74
+ use_cairo do |cc|
75
+ cc.move_to(x1, y1)
76
+ cc.curve_to(cx1, cy1, cx2, cy2, x2, y2)
77
+ cc.fill_n_stroke(draw)
78
+ end
79
+ end
80
+
81
+ # :nodoc:
82
+ # @api private
83
+ def star(poly, trans, draw)
84
+ x, y, n = poly.x, poly.y, poly.n
85
+ inner_radius, outer_radius = poly.inner_radius, poly.outer_radius
86
+ use_cairo do |cc|
87
+ cc.rotate_about(x, y, trans.angle)
88
+ cc.move_to(x + outer_radius, y) #i = 0, so cos(0)=1 and sin(0)=0
89
+ theta = Math::PI / n.to_f # i.e. (2*pi) / (2*n)
90
+ 0.upto(2 * n) do |i|
91
+ radius = i.even? ? outer_radius : inner_radius
92
+ cc.line_to(x + radius * Math::cos(i * theta),
93
+ y + radius * Math::sin(i * theta))
94
+ end
95
+ cc.close_path
96
+ cc.fill_n_stroke(draw)
97
+ end
98
+ end
99
+
100
+ # :nodoc:
101
+ # @api private
102
+ def polygon(poly, trans, draw)
103
+ x, y, n, radius = poly.x, poly.y, poly.n, poly.radius
104
+ use_cairo do |cc|
105
+ cc.rotate_about(x, y, trans.angle)
106
+ cc.move_to(x + radius, y) # i = 0, so cos(0)=1 and sin(0)=0
107
+ theta = (2 * Math::PI) / n.to_f
108
+ 0.upto(n) do |i|
109
+ cc.line_to(x + radius * Math::cos(i * theta),
110
+ y + radius * Math::sin(i * theta))
111
+ end
112
+ cc.close_path
113
+ cc.fill_n_stroke(draw)
114
+ end
115
+ end
116
+
117
+ end
118
+ end
119
+
@@ -1,88 +1,85 @@
1
- require 'squib/graphics/cairo_context_wrapper'
2
-
3
- module Squib
4
- class Deck
5
-
6
- # So the Cairo people have said over and over again that they won't support the 3x3 matrices that would handle perspective transforms.
7
- # Since our perspective transform needs are a bit simpler, we can use a "striping method" that does the job for us.
8
- # It's a little bit involved, but it works well enough for limited ranges of our parameters.
9
- # These were also helpful:
10
- # http://kapo-cpp.blogspot.com/2008/01/perspective-effect-using-cairo.html
11
- # http://zetcode.com/gui/pygtk/drawingII/
12
- # :nodoc:
13
- # @api private
14
- def render_showcase(range,
15
- trim, trim_radius, scale, offset, fill_color,
16
- reflect_offset, reflect_percent, reflect_strength, margin, face_right,
17
- dir, file_to_save)
18
- out_width = range.size * ((@width - 2*trim) * scale * offset) + 2*margin
19
- out_height = reflect_offset + (1.0 + reflect_percent) * (@height - 2*trim) + 2*margin
20
- out_cc = Cairo::Context.new(Cairo::ImageSurface.new(out_width, out_height))
21
- wrapper = Squib::Graphics::CairoContextWrapper.new(out_cc)
22
- wrapper.set_source_squibcolor(fill_color)
23
- wrapper.paint
24
-
25
- cards = range.collect { |i| @cards[i] }
26
- cards.each_with_index do |card, i|
27
- trimmed = trim_rounded(card.cairo_surface, trim, trim_radius)
28
- reflected = reflect(trimmed, reflect_offset, reflect_percent, reflect_strength)
29
- perspectived = perspective(reflected, scale, face_right)
30
- out_cc.set_source(perspectived, margin + i * perspectived.width * offset, margin)
31
- out_cc.paint
32
- end
33
- out_cc.target.write_to_png("#{dir}/#{file_to_save}")
34
- end
35
-
36
- # :nodoc:
37
- # @api private
38
- def trim_rounded(src, trim, radius)
39
- trim_cc = Cairo::Context.new(Cairo::ImageSurface.new(src.width-2.0*trim, src.height-2.0*trim))
40
- trim_cc.rounded_rectangle(0, 0, trim_cc.target.width, trim_cc.target.height, radius, radius)
41
- trim_cc.set_source(src, -1 * trim, -1 * trim)
42
- trim_cc.clip
43
- trim_cc.paint
44
- return trim_cc.target
45
- end
46
-
47
- # :nodoc:
48
- # @api private
49
- def reflect(src, roffset, rpercent, rstrength)
50
- tmp_cc = Cairo::Context.new(Cairo::ImageSurface.new(src.width, src.height * (1.0 + rpercent) + roffset))
51
- tmp_cc.set_source(src, 0, 0)
52
- tmp_cc.paint
53
- # Flip affine magic from: http://cairographics.org/matrix_transform/
54
- matrix = Cairo::Matrix.new(1, 0, 0, -1, 0, 2 * src.height + roffset)
55
- tmp_cc.transform(matrix) # flips the coordinate system
56
- top_y = src.height # top of the reflection
57
- bottom_y = src.height * (1.0 - rpercent) + roffset # bottom of the reflection
58
- gradient = Cairo::LinearPattern.new(0,top_y, 0,bottom_y)
59
- gradient.add_color_stop_rgba(0.0, 0,0,0, rstrength) # start a little reflected
60
- gradient.add_color_stop_rgba(1.0, 0,0,0, 0.0) # fade to nothing
61
- tmp_cc.set_source(src, 0, 0)
62
- tmp_cc.mask(gradient)
63
- return tmp_cc.target
64
- end
65
-
66
- # :nodoc:
67
- # @api private
68
- def perspective(src, scale, face_right)
69
- dest_cxt = Cairo::Context.new(Cairo::ImageSurface.new(src.width * scale, src.height))
70
- in_thickness = 1 # Take strip 1 pixel-width at a time
71
- out_thickness = 3 # Scale it to 3 pixels wider to cover any gaps
72
- (0..src.width).step(in_thickness) do |i|
73
- percentage = i / src.width.to_f
74
- i = src.width - i if face_right
75
- factor = scale + (percentage * (1.0 - scale)) #linear interpolation
76
- dest_cxt.save
77
- dest_cxt.translate 0, src.height / 2.0 * (1.0 - factor)
78
- dest_cxt.scale factor * scale, factor
79
- dest_cxt.set_source src, 0, 0
80
- dest_cxt.rounded_rectangle i, 0, out_thickness, src.height, 0,0
81
- dest_cxt.fill
82
- dest_cxt.restore
83
- end
84
- return dest_cxt.target
85
- end
86
-
87
- end
88
- end
1
+ require 'squib/graphics/cairo_context_wrapper'
2
+
3
+ module Squib
4
+ class Deck
5
+
6
+ # So the Cairo people have said over and over again that they won't support the 3x3 matrices that would handle perspective transforms.
7
+ # Since our perspective transform needs are a bit simpler, we can use a "striping method" that does the job for us.
8
+ # It's a little bit involved, but it works well enough for limited ranges of our parameters.
9
+ # These were also helpful:
10
+ # http://kapo-cpp.blogspot.com/2008/01/perspective-effect-using-cairo.html
11
+ # http://zetcode.com/gui/pygtk/drawingII/
12
+ # :nodoc:
13
+ # @api private
14
+ def render_showcase(range, sheet, showcase)
15
+ out_width = range.size * ((@width - 2*sheet.trim) * showcase.scale * showcase.offset) + 2*sheet.margin
16
+ out_height = showcase.reflect_offset + (1.0 + showcase.reflect_percent) * (@height - 2*sheet.trim) + 2*sheet.margin
17
+ out_cc = Cairo::Context.new(Cairo::ImageSurface.new(out_width, out_height))
18
+ wrapper = Squib::Graphics::CairoContextWrapper.new(out_cc)
19
+ wrapper.set_source_squibcolor(sheet.fill_color)
20
+ wrapper.paint
21
+
22
+ cards = range.collect { |i| @cards[i] }
23
+ cards.each_with_index do |card, i|
24
+ trimmed = trim_rounded(card.cairo_surface, sheet.trim, sheet.trim_radius)
25
+ reflected = reflect(trimmed, showcase.reflect_offset, showcase.reflect_percent, showcase.reflect_strength)
26
+ perspectived = perspective(reflected, showcase.scale, showcase.face_right?)
27
+ out_cc.set_source(perspectived, sheet.margin + i * perspectived.width * showcase.offset, sheet.margin)
28
+ out_cc.paint
29
+ end
30
+ out_cc.target.write_to_png("#{sheet.dir}/#{sheet.file}")
31
+ end
32
+
33
+ # :nodoc:
34
+ # @api private
35
+ def trim_rounded(src, trim, radius)
36
+ trim_cc = Cairo::Context.new(Cairo::ImageSurface.new(src.width-2.0*trim, src.height-2.0*trim))
37
+ trim_cc.rounded_rectangle(0, 0, trim_cc.target.width, trim_cc.target.height, radius, radius)
38
+ trim_cc.set_source(src, -1 * trim, -1 * trim)
39
+ trim_cc.clip
40
+ trim_cc.paint
41
+ return trim_cc.target
42
+ end
43
+
44
+ # :nodoc:
45
+ # @api private
46
+ def reflect(src, roffset, rpercent, rstrength)
47
+ tmp_cc = Cairo::Context.new(Cairo::ImageSurface.new(src.width, src.height * (1.0 + rpercent) + roffset))
48
+ tmp_cc.set_source(src, 0, 0)
49
+ tmp_cc.paint
50
+ # Flip affine magic from: http://cairographics.org/matrix_transform/
51
+ matrix = Cairo::Matrix.new(1, 0, 0, -1, 0, 2 * src.height + roffset)
52
+ tmp_cc.transform(matrix) # flips the coordinate system
53
+ top_y = src.height # top of the reflection
54
+ bottom_y = src.height * (1.0 - rpercent) + roffset # bottom of the reflection
55
+ gradient = Cairo::LinearPattern.new(0,top_y, 0,bottom_y)
56
+ gradient.add_color_stop_rgba(0.0, 0,0,0, rstrength) # start a little reflected
57
+ gradient.add_color_stop_rgba(1.0, 0,0,0, 0.0) # fade to nothing
58
+ tmp_cc.set_source(src, 0, 0)
59
+ tmp_cc.mask(gradient)
60
+ return tmp_cc.target
61
+ end
62
+
63
+ # :nodoc:
64
+ # @api private
65
+ def perspective(src, scale, face_right)
66
+ dest_cxt = Cairo::Context.new(Cairo::ImageSurface.new(src.width * scale, src.height))
67
+ in_thickness = 1 # Take strip 1 pixel-width at a time
68
+ out_thickness = 3 # Scale it to 3 pixels wider to cover any gaps
69
+ (0..src.width).step(in_thickness) do |i|
70
+ percentage = i / src.width.to_f
71
+ i = src.width - i if face_right
72
+ factor = scale + (percentage * (1.0 - scale)) #linear interpolation
73
+ dest_cxt.save
74
+ dest_cxt.translate 0, src.height / 2.0 * (1.0 - factor)
75
+ dest_cxt.scale factor * scale, factor
76
+ dest_cxt.set_source src, 0, 0
77
+ dest_cxt.rounded_rectangle i, 0, out_thickness, src.height, 0,0
78
+ dest_cxt.fill
79
+ dest_cxt.restore
80
+ end
81
+ return dest_cxt.target
82
+ end
83
+
84
+ end
85
+ end