1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
#--
# $Id: stretchable.rb,v 1.7 2009/02/28 23:52:28 rmagick Exp $
# Copyright (C) 2009 Timothy P. Hunter
#++
module Magick
class RVG
module PreserveAspectRatio
#--
# Included in Stretchable module and Image class
#++
# Specifies how the image within a viewport should be scaled.
# [+align+] a combination of 'xMin', 'xMid', or 'xMax', followed by
# 'YMin', 'YMid', or 'YMax'
# [+meet_or_slice+] one of 'meet' or 'slice'
def preserve_aspect_ratio(align, meet_or_slice = 'meet')
@align = align.to_s
if @align != 'none'
m = /\A(xMin|xMid|xMax)(YMin|YMid|YMax)\z/.match(@align)
raise(ArgumentError, "unknown alignment specifier: #{@align}") unless m
end
if meet_or_slice
meet_or_slice = meet_or_slice.to_s.downcase
raise(ArgumentError, "specifier must be `meet' or `slice' (got #{meet_or_slice})") unless %w[meet slice].include?(meet_or_slice)
@meet_or_slice = meet_or_slice
end
yield(self) if block_given?
self
end
end # module PreserveAspectRatio
# The methods in this module describe the user-coordinate space.
# RVG and Pattern objects are stretchable.
module Stretchable
private
# Scale to fit
def set_viewbox_none(width, height)
sx = 1.0
sy = 1.0
sx = width / @vbx_width if @vbx_width
sy = height / @vbx_height if @vbx_height
[sx, sy]
end
# Use align attribute to compute x- and y-offset from viewport's upper-left corner.
def align_to_viewport(width, height, sx, sy)
tx = case @align
when /\AxMin/
0
when NilClass, /\AxMid/
(width - @vbx_width * sx) / 2.0
when /\AxMax/
width - @vbx_width * sx
end
ty = case @align
when /YMin\z/
0
when NilClass, /YMid\z/
(height - @vbx_height * sy) / 2.0
when /YMax\z/
height - @vbx_height * sy
end
[tx, ty]
end
# Scale to smaller viewbox dimension
def set_viewbox_meet(width, height)
sx = sy = [width / @vbx_width, height / @vbx_height].min
[sx, sy]
end
# Scale to larger viewbox dimension
def set_viewbox_slice(width, height)
sx = sy = [width / @vbx_width, height / @vbx_height].max
[sx, sy]
end
# Establish the viewbox as necessary
def add_viewbox_primitives(width, height, gc)
@vbx_width ||= width
@vbx_height ||= height
@vbx_x ||= 0.0
@vbx_y ||= 0.0
if @align == 'none'
sx, sy = set_viewbox_none(width, height)
tx = 0
ty = 0
elsif @meet_or_slice == 'meet'
sx, sy = set_viewbox_meet(width, height)
tx, ty = align_to_viewport(width, height, sx, sy)
else
sx, sy = set_viewbox_slice(width, height)
tx, ty = align_to_viewport(width, height, sx, sy)
end
# Establish clipping path around the current viewport
name = __id__.to_s
gc.define_clip_path(name) do
gc.path("M0,0 l#{width},0 l0,#{height} l-#{width},0 l0,-#{height}z")
end
gc.clip_path(name)
# Add a non-scaled translation if meet or slice
gc.translate(tx, ty) if tx.abs > 1.0e-10 || ty.abs > 1.0e-10
# Scale viewbox as necessary
gc.scale(sx, sy) if sx != 1.0 || sy != 1.0
# Add a scaled translation if non-0 origin
gc.translate(-@vbx_x, -@vbx_y) if @vbx_x.abs != 0.0 || @vbx_y.abs != 0
end
def initialize(*_args)
super()
@vbx_x, @vbx_y, @vbx_width, @vbx_height = nil
@meet_or_slice = 'meet'
@align = nil
end
public
include PreserveAspectRatio
# Describe a user coordinate system to be imposed on the viewbox.
# The arguments must be numbers and the +width+ and +height+
# arguments must be positive.
def viewbox(x, y, width, height)
begin
@vbx_x = Float(x)
@vbx_y = Float(y)
@vbx_width = Float(width)
@vbx_height = Float(height)
rescue ArgumentError
raise ArgumentError, "arguments must be convertable to float (got #{x.class}, #{y.class}, #{width.class}, #{height.class})"
end
raise(ArgumentError, "viewbox width must be > 0 (#{@vbx_width} given)") unless @vbx_width >= 0
raise(ArgumentError, "viewbox height must be > 0 (#{@vbx_height} given)") unless @vbx_height >= 0
# return the user-coordinate space attributes if defined
class << self
unless defined? @redefined
@redefined = true
define_method(:x) { @vbx_x }
define_method(:y) { @vbx_y }
define_method(:width) { @vbx_width }
define_method(:height) { @vbx_height }
end
end
yield(self) if block_given?
self
end
end # module Stretchable
end # class RVG
end # module Magick
|