This repository has been archived by the owner on Jul 13, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
geometry.rb
158 lines (137 loc) · 4.49 KB
/
geometry.rb
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
module Paperclip
# Defines the geometry of an image.
class Geometry
attr_accessor :height, :width, :modifier
EXIF_ROTATED_ORIENTATION_VALUES = [5, 6, 7, 8]
# Gives a Geometry representing the given height and width
def initialize(width = nil, height = nil, modifier = nil)
if width.is_a?(Hash)
options = width
@height = options[:height].to_f
@width = options[:width].to_f
@modifier = options[:modifier]
@orientation = options[:orientation].to_i
else
@height = height.to_f
@width = width.to_f
@modifier = modifier
end
end
# Extracts the Geometry from a file (or path to a file)
def self.from_file(file)
GeometryDetector.new(file).make
end
# Extracts the Geometry from a "WxH,O" string
# Where W is the width, H is the height,
# and O is the EXIF orientation
def self.parse(string)
GeometryParser.new(string).make
end
# Swaps the height and width if necessary
def auto_orient
if EXIF_ROTATED_ORIENTATION_VALUES.include?(@orientation)
@height, @width = @width, @height
@orientation -= 4
end
end
# True if the dimensions represent a square
def square?
height == width
end
# True if the dimensions represent a horizontal rectangle
def horizontal?
height < width
end
# True if the dimensions represent a vertical rectangle
def vertical?
height > width
end
# The aspect ratio of the dimensions.
def aspect
width / height
end
# Returns the larger of the two dimensions
def larger
[height, width].max
end
# Returns the smaller of the two dimensions
def smaller
[height, width].min
end
# Returns the width and height in a format suitable to be passed to Geometry.parse
def to_s
s = ""
s << width.to_i.to_s if width > 0
s << "x#{height.to_i}" if height > 0
s << modifier.to_s
s
end
# Same as to_s
def inspect
to_s
end
# Returns the scaling and cropping geometries (in string-based ImageMagick format)
# neccessary to transform this Geometry into the Geometry given. If crop is true,
# then it is assumed the destination Geometry will be the exact final resolution.
# In this case, the source Geometry is scaled so that an image containing the
# destination Geometry would be completely filled by the source image, and any
# overhanging image would be cropped. Useful for square thumbnail images. The cropping
# is weighted at the center of the Geometry.
def transformation_to dst, crop = false
if crop
ratio = Geometry.new( dst.width / self.width, dst.height / self.height )
scale_geometry, scale = scaling(dst, ratio)
crop_geometry = cropping(dst, ratio, scale)
else
scale_geometry = dst.to_s
end
[ scale_geometry, crop_geometry ]
end
# resize to a new geometry
# @param geometry [String] the Paperclip geometry definition to resize to
# @example
# Paperclip::Geometry.new(150, 150).resize_to('50x50!')
# #=> Paperclip::Geometry(50, 50)
def resize_to(geometry)
new_geometry = Paperclip::Geometry.parse geometry
case new_geometry.modifier
when '!', '#'
new_geometry
when '>'
if new_geometry.width >= self.width && new_geometry.height >= self.height
self
else
scale_to new_geometry
end
when '<'
if new_geometry.width <= self.width || new_geometry.height <= self.height
self
else
scale_to new_geometry
end
else
scale_to new_geometry
end
end
private
def scaling dst, ratio
if ratio.horizontal? || ratio.square?
[ "%dx" % dst.width, ratio.width ]
else
[ "x%d" % dst.height, ratio.height ]
end
end
def cropping dst, ratio, scale
if ratio.horizontal? || ratio.square?
"%dx%d+%d+%d" % [ dst.width, dst.height, 0, (self.height * scale - dst.height) / 2 ]
else
"%dx%d+%d+%d" % [ dst.width, dst.height, (self.width * scale - dst.width) / 2, 0 ]
end
end
# scale to the requested geometry and preserve the aspect ratio
def scale_to(new_geometry)
scale = [new_geometry.width.to_f / self.width.to_f , new_geometry.height.to_f / self.height.to_f].min
Paperclip::Geometry.new((self.width * scale).round, (self.height * scale).round)
end
end
end