Class: Blufin::Image

Inherits:
Object
  • Object
show all
Defined in:
lib/core/image.rb

Constant Summary collapse

VALID_SIDES =
%w(width height)

Class Method Summary collapse

Class Method Details

.add_text(src, target, text, x = 35, y = 60, bg = 'black', fg = 'white', point_size = 22, stroke_width = 1) ⇒ Object

Adds text/caption to image.

Returns:

  • void

Raises:

  • (RuntimeError)
[View source]

85
86
87
88
89
90
91
92
# File 'lib/core/image.rb', line 85

def self.add_text(src, target, text, x = 35, y = 60, bg = 'black', fg = 'white', point_size = 22, stroke_width = 1)
    raise RuntimeError, "File not found: #{src}" unless Blufin::Files::file_exists(src)
    raise RuntimeError, "Expected integer (for x), instead got: #{x}" unless x.to_s =~ /^\d+$/
    raise RuntimeError, "Expected integer (for y), instead got: #{y}" unless y.to_s =~ /^\d+$/
    raise RuntimeError, "Expected integer (for point_size), instead got: #{point_size}" unless point_size.to_s =~ /^\d+$/
    raise RuntimeError, "Expected integer (for stroke_width), instead got: #{stroke_width}" unless stroke_width.to_s =~ /^\d+$/
    Blufin::Terminal::execute("convert #{src} -undercolor #{bg} -stroke #{fg} -pointsize #{point_size} -strokewidth #{stroke_width} -draw \"text #{x},#{y} '#{text}'\" #{target}")
end

.calc_aspect_ratio(width, height) ⇒ Object

Calculates the aspect ration (IE: 16:9) from a set of given dimensions.

Returns:

  • string

[View source]

96
97
98
99
# File 'lib/core/image.rb', line 96

def self.calc_aspect_ratio(width, height)
    gcd = gcd(width, height)
    "#{(width / gcd).to_i}:#{(height / gcd).to_i}"
end

.calc_file_name(prefix, ext, side, length, ratio) ⇒ Object

Standardizes the way output files are named.

Returns:

  • string

Raises:

  • (RuntimeError)
[View source]

133
134
135
136
137
138
139
140
# File 'lib/core/image.rb', line 133

def self.calc_file_name(prefix, ext, side, length, ratio)
    raise RuntimeError, "Expected integer, instead got: #{length}" unless length.to_s =~ /^\d+$/
    raise RuntimeError, "Expected one of: #{VALID_SIDES.inspect}, instead got: #{side}" unless VALID_SIDES.include?(side)
    raise RuntimeError, "Unexpected ratio #{Blufin::Terminal::format_highlight(ratio)}, expected something like: 16:9" unless ratio =~ /\d{1,2}:\d{1,2}/
    x = side == 'width' ? length : calc_width_from_ratio(ratio, length)
    y = side == 'width' ? calc_height_from_ratio(ratio, length) : length
    "#{prefix}-#{x}x#{y}.#{ext.downcase}"
end

.calc_height_from_ratio(ratio, width) ⇒ Object

Calculates height from aspect ratio and width: IE: 16:9 and 1920 returns -> 1080.

Returns:

  • int

Raises:

  • (RuntimeError)
[View source]

114
115
116
117
118
119
# File 'lib/core/image.rb', line 114

def self.calc_height_from_ratio(ratio, width)
    raise RuntimeError, "Unexpected ratio #{Blufin::Terminal::format_highlight(ratio)}, expected something like: 16:9" unless ratio =~ /\d{1,2}:\d{1,2}/
    raise RuntimeError, "Expected integer, instead got: #{width}" unless width.to_s =~ /^\d+$/
    rs = ratio.split(':')
    ((width.to_i / rs[0].to_i) * rs[1].to_i).to_i
end

.calc_width_from_ratio(ratio, height) ⇒ Object

Calculates width from aspect ratio and height: IE: 16:9 and 1080 returns -> 1920.

Returns:

  • int

Raises:

  • (RuntimeError)
[View source]

104
105
106
107
108
109
# File 'lib/core/image.rb', line 104

def self.calc_width_from_ratio(ratio, height)
    raise RuntimeError, "Unexpected ratio #{Blufin::Terminal::format_highlight(ratio)}, expected something like: 16:9" unless ratio =~ /\d{1,2}:\d{1,2}/
    raise RuntimeError, "Expected integer, instead got: #{height}" unless height.to_s =~ /^\d+$/
    rs = ratio.split(':')
    ((height.to_i / rs[1].to_i) * rs[0].to_i).to_i
end

.crop_to_length_and_ratio(src, target, side, length, ratio, quality = 100) ⇒ Object

This command crops an image based on a give size and a ration (using ImageMagick). If dimensions don’t match ratio, adjusts accordingly and crops from center. Must specify if you want @side to be width or height, where your chosen side will match @length and the other gets altered as needed. side - the side for which you want to apply the length to (other side will get adjusted accordingly) length - the length that you want applied ratio - the ratio you want (16:9 and 9:16 will produce different results) quality - the higher the quality, the bigger the image.

Returns:

  • void

Raises:

  • (RuntimeError)
[View source]

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
# File 'lib/core/image.rb', line 40

def self.crop_to_length_and_ratio(src, target, side, length, ratio, quality = 100)
    d1 = length
    side.downcase!
    raise RuntimeError, "Expected integer, instead got: #{d1}" unless d1.to_s =~ /^\d+$/
    raise RuntimeError, "Expected one of: #{VALID_SIDES.inspect}, instead got: #{side}" unless VALID_SIDES.include?(side)
    raise RuntimeError, "Unexpected ratio #{Blufin::Terminal::format_highlight(ratio)}, expected something like: 16:9" unless ratio =~ /\d{1,2}:\d{1,2}/
    raise RuntimeError, "Expected integer between 0 - 100, instead got: #{quality}" unless quality.to_s =~ /^\d+$/ && quality.to_i >= 0 && quality.to_i <= 100
    raise RuntimeError, "File not found: #{src}" unless Blufin::Files::file_exists(src)
    # Convert the initial image to match the width.
    c1 = side == 'width' ? d1 : "x#{d1}"
    Blufin::Terminal::execute("magick #{src} -resize #{c1} -quality #{quality} #{target}")
    ac = info(target)
    d2 = side == 'width' ? calc_height_from_ratio(ratio, d1) : calc_width_from_ratio(ratio, d1)
    # If the other side is less that what's desired, convert the image using the other orientation.
    c3 = side == 'width' ? ac.height : ac.width
    if c3 < d2
        c2 = side == 'width' ? "x#{d2}" : d2
        Blufin::Terminal::execute("magick #{src} -resize #{c2} -quality #{quality} #{target}")
        ac = info(target)
    end
    ow = ac.width
    oh = ac.height
    if side == 'width'
        x  = ow > d1 ? ((ow - d1) / 2).to_i : 0
        y  = oh > d2 ? ((oh - d2) / 2).to_i : 0
        c4 = d1
        c5 = calc_height_from_ratio(ratio, d1)
    else
        x  = ow > d2 ? ((ow - d2) / 2).to_i : 0
        y  = oh > d1 ? ((oh - d1) / 2).to_i : 0
        c4 = calc_width_from_ratio(ratio, d1)
        c5 = d1
    end
    # Do the final crop.
    Blufin::Terminal::execute("convert #{target} -crop #{c4}x#{c5}+#{x}+#{y} +repage #{target}")
    # Output details about the image.
    img = info(target)
    ts  = target.split('/')
    puts
    puts "    \x1B[38;5;40m#{ts[ts.length - 1]}\x1B[38;5;240m \xe2\x80\x94 #{Filesize.from("#{img.size} B").pretty} | #{img.ratio}#{img.ratio != ratio ? ' - For this image, a perfect ratio couldn\'t be achieved :(' : nil}\x1B[0m"
    puts
end

.gcd(a, b) ⇒ Object

Gets the greatest-common-denominator. Used to calculate screen aspect ratio. See: stackoverflow.com/questions/14731745/what-exactly-does-do-in-javascript

Returns:

  • int

[View source]

124
125
126
127
128
129
# File 'lib/core/image.rb', line 124

def self.gcd(a, b)
    a = a.to_i
    b = b.to_i
    return a if b == 0
    gcd(b, a % b)
end

.info(path_and_file) ⇒ Object

Uses ImageMagick to return details about an image.

Returns:

  • Hash

Raises:

  • (RuntimeError)
[View source]

16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/core/image.rb', line 16

def self.info(path_and_file)
    check_image_magick_installed
    raise RuntimeError, "File not found: #{path_and_file}" unless Blufin::Files::file_exists(path_and_file)
    img                      = MiniMagick::Image.open(path_and_file)
    img_container            = ImageContainer.new
    img_container.width      = img.dimensions[0].to_i
    img_container.height     = img.dimensions[1].to_i
    img_container.size       = img.size
    img_container.size_human = img.human_size
    img_container.type       = img.type.downcase
    img_container.ratio      = calc_aspect_ratio(img.dimensions[0].to_i, img.dimensions[1].to_i)
    img_container
end