Browse Source

PNG/SVG: flipping is now supported.

Fixes #11
dev
Andy Meneely 10 years ago
parent
commit
d99e840ffd
  1. 1
      CHANGELOG.md
  2. 4
      lib/squib/api/image.rb
  3. 4
      lib/squib/args/transform.rb
  4. 12
      lib/squib/graphics/cairo_context_wrapper.rb
  5. 18
      lib/squib/graphics/image.rb
  6. 5
      samples/load_images.rb
  7. 6
      spec/data/samples/basic.rb.txt
  8. 3
      spec/data/samples/custom_config.rb.txt
  9. 58
      spec/data/samples/load_images.rb.txt
  10. 3
      spec/data/samples/ranges.rb.txt
  11. 4
      spec/data/samples/showcase.rb.txt
  12. 5
      spec/data/samples/tgc_proofs.rb.txt
  13. 9
      spec/graphics/cairo_context_wrapper_spec.rb
  14. BIN
      spec/samples/expected/load_images_00.png

1
CHANGELOG.md

@ -5,6 +5,7 @@ Squib follows [semantic versioning](http://semver.org).
Features:
* Crop your PNGs and SVGs! This means you can work from spritesheets if you want. New options to `png` and `svg` are documented in the API docs and demonstrated in the `load_images.rb` sample. (#11)
* Flip your PNGs and SVGs! Set `flip_horizontal: true` or `flip_vertical: true` (or both!) to flip the image about it's center. (#11)
Chores:
* Ripped out a lot of old constants used from the old way we handled arguments. Yay negative churn!

4
lib/squib/api/image.rb

@ -32,6 +32,8 @@ module Squib
# @option opts crop_corner_y_radius [Integer] (0): y radius for rounded corners of cropped image. Supports Unit Conversion, see {file:README.md#Units Units}.
# @option opts crop_width [Integer] (:native): Width of the cropped image. Supports Unit Conversion, see {file:README.md#Units Units}.
# @option opts crop_height [Integer] (:native): Height of the cropped image. Supports Unit Conversion, see {file:README.md#Units Units}.
# @option opts flip_horiztonal [Boolean] (false): Flip this image about its center horizontally (i.e. left becomes right and vice versa).
# @option opts flip_vertical [Boolean] (false): Flip this image about its center verticall (i.e. top becomes bottom and vice versa).
# @return [nil] Returns nil
# @api public
def png(opts = {})
@ -79,6 +81,8 @@ module Squib
# @option opts crop_corner_y_radius [Integer] (0): y radius for rounded corners of cropped image. Supports Unit Conversion, see {file:README.md#Units Units}.
# @option opts crop_width [Integer] (:native): Width of the cropped image. Supports Unit Conversion, see {file:README.md#Units Units}.
# @option opts crop_height [Integer] (:native): Height of the cropped image. Supports Unit Conversion, see {file:README.md#Units Units}.
# @option opts flip_horiztonal [Boolean] (false): Flip this image about its center horizontally (i.e. left becomes right and vice versa).
# @option opts flip_vertical [Boolean] (false): Flip this image about its center verticall (i.e. top becomes bottom and vice versa).
# @return [nil] Returns nil
# @api public
def svg(opts = {})

4
lib/squib/args/transform.rb

@ -20,6 +20,8 @@ module Squib
crop_corner_radius: nil,
crop_corner_x_radius: 0,
crop_corner_y_radius: 0,
flip_vertical: false,
flip_horizontal: false,
}
end
@ -28,7 +30,7 @@ module Squib
end
def self.params_with_units
parameters.keys # all of them
parameters.keys - [:flip_vertical, :flip_horizontal]
end
def validate_crop_width(arg, _i)

12
lib/squib/graphics/cairo_context_wrapper.rb

@ -94,6 +94,18 @@ module Squib
translate(-x, -y)
end
# Flip either vertical or horizontal depending
# From the cairo website: http://cairographics.org/matrix_transform/
# cairo.Matrix(fx, 0, 0,
# fy, cx*(1-fx), cy*(fy-1))
# fx/fy = 1 means 'no flip', fx/fy = -1 are used for horizontal/vertical flip
def flip(vertical, horizontal, x, y)
v = vertical ? -1.0 : 1.0
h = horizontal ? -1.0 : 1.0
transform Cairo::Matrix.new(v, 0.0, 0.0,
h, x*(1-v), y*(1-h))
end
end
end
end

18
lib/squib/graphics/image.rb

@ -20,15 +20,15 @@ module Squib
png = Squib.cache_load_image(file)
use_cairo do |cc|
cc.translate(box.x, box.y)
if box.width != :native || box.height != :native
box.width = png.width.to_f if box.width == :native
box.height = png.height.to_f if box.height == :native
box.width = png.width.to_f * box.height.to_f / png.height.to_f if box.width == :scale
box.height = png.height.to_f * box.width.to_f / png.width.to_f if box.height == :scale
Squib.logger.warn "PNG scaling results in aliasing."
cc.scale(box.width.to_f / png.width.to_f, box.height.to_f / png.height.to_f)
end
Squib.logger.warn "PNG scaling results in aliasing." if box.width != :native || box.height != :native
box.width = png.width.to_f if box.width == :native
box.height = png.height.to_f if box.height == :native
box.width = png.width.to_f * box.height.to_f / png.height.to_f if box.width == :scale
box.height = png.height.to_f * box.width.to_f / png.width.to_f if box.height == :scale
cc.scale(box.width.to_f / png.width.to_f, box.height.to_f / png.height.to_f)
cc.rotate(trans.angle)
cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
cc.translate(-box.x, -box.y)
trans.crop_width = png.width.to_f if trans.crop_width == :native
@ -37,6 +37,7 @@ module Squib
cc.clip
cc.translate(-trans.crop_x, -trans.crop_y)
cc.set_source(png, box.x, box.y)
cc.operator = paint.blend unless paint.blend == :none
if paint.mask.empty?
@ -64,6 +65,7 @@ module Squib
scale_height = box.height.to_f / svg.height.to_f
use_cairo do |cc|
cc.translate(box.x, box.y)
cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
cc.rotate(trans.angle)
cc.scale(scale_width, scale_height)

5
samples/load_images.rb

@ -34,6 +34,11 @@ Squib::Deck.new(width: 825, height: 1125, cards: 1) do
svg file: 'spanner.svg', x: 300, y: 500, width: 64, height: 64,
crop_x: 32, crop_y: 32, crop_width: 32, crop_height:32
# We can flip our images too
png file: 'sprites.png', x: 300, y: 535, flip_vertical: true, flip_horizontal: true
svg file: 'spanner.svg', x: 300, y: 615, width: 64, height: 64,
flip_vertical: true, flip_horizontal: true
# We can also limit our rendering to a single object, if the SVG ID is set
svg file: 'spanner.svg', id: '#backdrop', x: 50, y: 350, width: 75, height: 75
# Squib prepends a #-sign if one is not specified

6
spec/data/samples/basic.rb.txt

@ -180,7 +180,9 @@ pango: ellipsized?([])
cairo: restore([])
cairo: save([])
cairo: translate([620, 75])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-620, -75])
cairo: rounded_rectangle([620, 75, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -190,7 +192,9 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([620, 75])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-620, -75])
cairo: rounded_rectangle([620, 75, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -200,6 +204,7 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([620, 218])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([1.0, 1.0])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -209,6 +214,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([620, 218])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([1.0, 1.0])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])

3
spec/data/samples/custom_config.rb.txt

@ -26,7 +26,9 @@ pango: ellipsized?([])
cairo: restore([])
cairo: save([])
cairo: translate([620, 75])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-620, -75])
cairo: rounded_rectangle([620, 75, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -36,6 +38,7 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([620, 218])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([1.0, 1.0])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])

58
spec/data/samples/load_images.rb.txt

@ -16,7 +16,9 @@ cairo: stroke([])
cairo: restore([])
cairo: save([])
cairo: translate([620, 75])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-620, -75])
cairo: rounded_rectangle([620, 75, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -26,6 +28,7 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([620, 218])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([1.0, 1.0])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -35,6 +38,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([50, 50])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([1.953125, 1.953125])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -46,6 +50,7 @@ cairo: save([])
cairo: translate([305, 50])
cairo: scale([1.953125, 1.953125])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-305, -50])
cairo: rounded_rectangle([305, 50, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -55,6 +60,7 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([200, 350])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.2734375, 0.2734375])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -64,6 +70,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([200, 390])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.2734375, 0.2734375])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -75,6 +82,7 @@ cairo: save([])
cairo: translate([240, 350])
cairo: scale([0.2734375, 0.2734375])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-240, -350])
cairo: rounded_rectangle([240, 350, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -86,6 +94,7 @@ cairo: save([])
cairo: translate([240, 390])
cairo: scale([0.2734375, 0.2734375])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-240, -390])
cairo: rounded_rectangle([240, 390, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -95,7 +104,9 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([300, 350])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-300, -350])
cairo: rounded_rectangle([300, 350, 128.0, 64.0, 0, 0])
cairo: clip([])
@ -105,7 +116,9 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([300, 425])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-300, -425])
cairo: rounded_rectangle([300, 425, 64, 64, 10, 10])
cairo: clip([])
@ -115,7 +128,9 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([400, 425])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-400, -425])
cairo: rounded_rectangle([400, 425, 64, 64, 25, 25])
cairo: clip([])
@ -125,7 +140,9 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([500, 415])
cairo: scale([1.0, 1.0])
cairo: rotate([0.5235987755982988])
cairo: transform([Matrix])
cairo: translate([-500, -415])
cairo: rounded_rectangle([500, 415, 64, 64, 25, 25])
cairo: clip([])
@ -135,6 +152,7 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([300, 500])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.5, 0.5])
cairo: rounded_rectangle([0, 0, 64.0, 64.0, 0, 0])
@ -143,7 +161,30 @@ cairo: translate([-32, -32])
cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([300, 535])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-300, -535])
cairo: rounded_rectangle([300, 535, 128.0, 64.0, 0, 0])
cairo: clip([])
cairo: translate([0, 0])
cairo: set_source([ImageSurface, 300, 535])
cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([300, 615])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.5, 0.5])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
cairo: clip([])
cairo: translate([0, 0])
cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([50, 350])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.5859375, 0.5859375])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -153,6 +194,7 @@ cairo: render_rsvg_handle([RSVG::Handle, "#backdrop"])
cairo: restore([])
cairo: save([])
cairo: translate([50, 450])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.9765625, 0.9765625])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -162,6 +204,7 @@ cairo: render_rsvg_handle([RSVG::Handle, "#backdrop"])
cairo: restore([])
cairo: save([])
cairo: translate([50, 600])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.5859375, 0.5859375])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -171,6 +214,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([0, 0])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.8021390374331551, 0.8032128514056225])
cairo: rounded_rectangle([0, 0, 748.0, 747.0, 0, 0])
@ -180,7 +224,9 @@ cairo: render_rsvg_handle([RSVG::Handle, "#thing"])
cairo: restore([])
cairo: save([])
cairo: translate([50, 700])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-50, -700])
cairo: rounded_rectangle([50, 700, 100.0, 100.0, 0, 0])
cairo: clip([])
@ -190,7 +236,9 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([70, 750])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-70, -750])
cairo: rounded_rectangle([70, 750, 100.0, 100.0, 0, 0])
cairo: clip([])
@ -201,7 +249,9 @@ cairo: paint([0.75])
cairo: restore([])
cairo: save([])
cairo: translate([300, 700])
cairo: scale([1.0, 1.0])
cairo: rotate([0.0])
cairo: transform([Matrix])
cairo: translate([-300, -700])
cairo: rounded_rectangle([300, 700, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -211,7 +261,9 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([300, 800])
cairo: scale([1.0, 1.0])
cairo: rotate([0.7853981633974483])
cairo: transform([Matrix])
cairo: translate([-300, -800])
cairo: rounded_rectangle([300, 800, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -221,6 +273,7 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([300, 900])
cairo: transform([Matrix])
cairo: rotate([1.4707963267948965])
cairo: scale([1.0, 1.0])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -230,6 +283,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([500, 600])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.390625, 0.390625])
cairo: rounded_rectangle([0, 0, 512.0, 512.0, 0, 0])
@ -241,6 +295,7 @@ cairo: mask([MockDouble, 0, 0])
cairo: restore([])
cairo: save([])
cairo: translate([500, 800])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.390625, 0.390625])
cairo: rounded_rectangle([0, 0, 512.0, 512.0, 0, 0])
@ -252,7 +307,9 @@ cairo: mask([MockDouble, 0, 0])
cairo: restore([])
cairo: save([])
cairo: translate([650, 950])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-650, -950])
cairo: rounded_rectangle([650, 950, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -263,6 +320,7 @@ cairo: mask([ImageSurface, 650, 950])
cairo: restore([])
cairo: save([])
cairo: translate([0, 0])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([6.4453125, 8.7890625])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])

3
spec/data/samples/ranges.rb.txt

@ -402,6 +402,7 @@ pango: ellipsized?([])
cairo: restore([])
cairo: save([])
cairo: translate([150, 55])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.078125, 0.078125])
cairo: rounded_rectangle([0, 0, 512.0, 512.0, 0, 0])
@ -411,6 +412,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([150, 55])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.078125, 0.078125])
cairo: rounded_rectangle([0, 0, 512.0, 512.0, 0, 0])
@ -420,6 +422,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([150, 97])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([0.078125, 0.078125])
cairo: rounded_rectangle([0, 0, 512.0, 512.0, 0, 0])

4
spec/data/samples/showcase.rb.txt

@ -132,6 +132,7 @@ pango: ellipsized?([])
cairo: restore([])
cairo: save([])
cairo: translate([162, 500])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([3.90625, 3.90625])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -141,6 +142,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([162, 500])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([3.90625, 3.90625])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -150,6 +152,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([162, 500])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([3.90625, 3.90625])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -159,6 +162,7 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([162, 500])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([3.90625, 3.90625])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])

5
spec/data/samples/tgc_proofs.rb.txt

@ -60,7 +60,9 @@ pango: ellipsized?([])
cairo: restore([])
cairo: save([])
cairo: translate([620, 75])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([-620, -75])
cairo: rounded_rectangle([620, 75, 128.0, 128.0, 0, 0])
cairo: clip([])
@ -70,6 +72,7 @@ cairo: paint([1.0])
cairo: restore([])
cairo: save([])
cairo: translate([620, 218])
cairo: transform([Matrix])
cairo: rotate([0])
cairo: scale([1.0, 1.0])
cairo: rounded_rectangle([0, 0, 128.0, 128.0, 0, 0])
@ -79,7 +82,9 @@ cairo: render_rsvg_handle([RSVG::Handle, nil])
cairo: restore([])
cairo: save([])
cairo: translate([0, 0])
cairo: scale([1.0, 1.0])
cairo: rotate([0])
cairo: transform([Matrix])
cairo: translate([0, 0])
cairo: rounded_rectangle([0, 0, 825.0, 1125.0, 0, 0])
cairo: clip([])

9
spec/graphics/cairo_context_wrapper_spec.rb

@ -81,4 +81,13 @@ describe Squib::Graphics::CairoContextWrapper do
end
end
context 'flips' do
it 'in the basic case' do
dbl = double(Cairo::Matrix)
expect(Cairo::Matrix).to receive(:new).with(-1.0, 0.0, 0.0, -1.0, 6.0, 8.0).and_return(dbl)
expect(cairo).to receive(:transform).with(dbl)
subject.flip(true, true, 3.0, 4.0)
end
end
end

BIN
spec/samples/expected/load_images_00.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Loading…
Cancel
Save