class Rainbow::Gradient

Constants

SUPPORTED_ANGLES

Attributes

args[R]
canvas[R]
color_ranges[R]
height[R]
opacity_ranges[R]
scale_end_location_in_pixel[R]
scale_half_distance_in_pixel[R]
scale_mid_location_in_pixel[R]
scale_start_location_in_pixel[R]
style[R]
width[R]

Public Class Methods

new(width, height, args) click to toggle source

wdith - The gradient width height - The gradient height args - Hash that describes the property of the gradient

{
  blend_mode: 'norm|diss|dark|...',
  dither: true|false,
  opacity: 0..100,
  gradient: {
    type: 'solid|noise',
    smoothness: 0..100,
    color_ranges: [],
    opacity_ranges: []
  },
  reverse: true|false,
  style: 'linear|radial|angle|reflected|diamond',
  align_with_layer: true|false,
  angle: 0..360,
  scale: 10..150
}
# File lib/rainbow/gradient.rb, line 30
def initialize(width, height, args)
  @width  = width
  @height = height
  @args   = args

  @style          = args[:style]
  @color_ranges   = args[:gradient][:color_ranges]
  @opacity_ranges = OpacityRanges.new(args[:gradient][:opacity_ranges], self)

  assert_arguments!
  compute_scale_variables!
end

Public Instance Methods

align_with_layer() click to toggle source
# File lib/rainbow/gradient.rb, line 77
def align_with_layer
  args.fetch(:align_with_layer, true)
end
angle() click to toggle source
# File lib/rainbow/gradient.rb, line 81
def angle
  value = args.fetch(:angle, 0)
  value % 360
end
blend_mode() click to toggle source
# File lib/rainbow/gradient.rb, line 53
def blend_mode
  args.fetch(:blend_mode, 'norm')
end
create_canvas() click to toggle source
# File lib/rainbow/gradient.rb, line 43
def create_canvas
  @canvas = ChunkyPNG::Canvas.new(width, height, ChunkyPNG::Color::TRANSPARENT)

  paint_canvas!(@canvas)
  set_opacity!(@canvas) if opacity != 100
  reverse!(@canvas) if reverse || angle == 180

  @canvas
end
dither() click to toggle source
# File lib/rainbow/gradient.rb, line 57
def dither
  args.fetch(:dither, false)
end
opacity() click to toggle source
# File lib/rainbow/gradient.rb, line 61
def opacity
  args.fetch(:opacity, 100)
end
reverse() click to toggle source
# File lib/rainbow/gradient.rb, line 73
def reverse
  args.fetch(:reverse, false)
end
save_as_png(path) click to toggle source
# File lib/rainbow/gradient.rb, line 94
def save_as_png(path)
  create_canvas unless canvas
  canvas.save(path, :fast_rgba)

  path
end
scale() click to toggle source
# File lib/rainbow/gradient.rb, line 86
def scale
  args.fetch(:scale, 100)
end
scale_100?() click to toggle source
# File lib/rainbow/gradient.rb, line 90
def scale_100?
  scale == 100
end
smoothness() click to toggle source
# File lib/rainbow/gradient.rb, line 69
def smoothness
  args[:gradient][:smoothness]
end
type() click to toggle source
# File lib/rainbow/gradient.rb, line 65
def type
  args[:gradient][:type]
end

Private Instance Methods

assert_arguments!() click to toggle source
# File lib/rainbow/gradient.rb, line 103
def assert_arguments!
  _opacity_ranges = args[:gradient][:opacity_ranges]

  raise ArgumentError, 'args[:gradient][:color_ranges] cannot not be blank' if !color_ranges || color_ranges.size == 0
  raise ArgumentError, 'args[:gradient][:opacity_ranges] cannot not be blank' if !_opacity_ranges || _opacity_ranges.size == 0
  raise ArgumentError, 'args[:style] - Sorry, for now we support only linear gradient' if args[:style] != 'linear'
  raise ArgumentError, 'args[:scale] must be between 10 and 150' if scale < 10 || scale > 150
  raise ArgumentError, 'Sorry, we support only two colors stop if scale is not 100' if scale != 100 && color_ranges.size != 1
  raise ArgumentError, 'Sorry, we support only 0° or 180° angle' unless SUPPORTED_ANGLES.include?(angle)
end
compute_scale_variables!() click to toggle source
# File lib/rainbow/gradient.rb, line 114
def compute_scale_variables!
  scale_start = (100 - scale) / 2
  scale_end   = scale_start == 0 ? 100 : scale_start + scale

  @scale_start_location_in_pixel = scale_start * width / 100
  @scale_end_location_in_pixel   = scale_end * width / 100
  @scale_half_distance_in_pixel  = (@scale_end_location_in_pixel - @scale_start_location_in_pixel) / 2.0
  @scale_mid_location_in_pixel   = @scale_start_location_in_pixel + @scale_half_distance_in_pixel
end
paint_body(canvas, color_range, x_coverred) click to toggle source
# File lib/rainbow/gradient.rb, line 171
def paint_body(canvas, color_range, x_coverred)
  n = 0
  color_range.width.times do |x|
    if x == 0 && x + x_coverred != color_range.from_location_in_pixel
      n = x + x_coverred - color_range.from_location_in_pixel
    end

    x += x_coverred - n
    puts "  x: #{x}, n: #{n}" if ENV['RAINBOW_DEBUG']

    break if x == width

    color_range.current_x = x
    height.times { |y| canvas[x, y] = color_range.current_color }
  end

  color_range.width
end
paint_canvas!(canvas) click to toggle source
# File lib/rainbow/gradient.rb, line 141
def paint_canvas!(canvas)
  x_coverred           = 0
  previous_color_range = nil

  color_ranges.each_with_index do |color_range, index|
    color_range.previous       = previous_color_range
    color_range.opacity_ranges = opacity_ranges
    color_range.gradient       = self

    # When the first color location is not 0
    x_coverred += paint_prefix(canvas, color_range, x_coverred) if index == 0

    x_coverred += paint_body(canvas, color_range, x_coverred)

    # When the last color location is not 100
    x_coverred += paint_suffix(canvas, color_range, x_coverred) if index + 1 == color_ranges.size

    previous_color_range = color_range if index != 0
  end
end
paint_prefix(canvas, color_range, x_coverred) click to toggle source
# File lib/rainbow/gradient.rb, line 162
def paint_prefix(canvas, color_range, x_coverred)
  color_range.from_location_in_pixel.times do |x|
    color_range.current_x = x
    height.times { |y| canvas[x, y] = color_range.current_color }
  end

  color_range.from_location_in_pixel
end
paint_suffix(canvas, color_range, x_coverred) click to toggle source
# File lib/rainbow/gradient.rb, line 190
def paint_suffix(canvas, color_range, x_coverred)
  color_range.leftover_width.times do |x|
    x += x_coverred

    break if x == width

    color_range.current_x = x
    height.times { |y| canvas[x, y] = color_range.current_color }
  end

  color_range.leftover_width
end
reverse!(canvas) click to toggle source
# File lib/rainbow/gradient.rb, line 124
def reverse!(canvas)
  canvas.flip_vertically!
end
set_opacity!(canvas) click to toggle source
# File lib/rainbow/gradient.rb, line 128
def set_opacity!(canvas)
  width.times.each do |x|
    height.times.each do |y|
      color        = canvas[x, y]
      red          = ChunkyPNG::Color.r(color)
      green        = ChunkyPNG::Color.g(color)
      blue         = ChunkyPNG::Color.b(color)
      new_alpha    = ChunkyPNG::Color.a(color) * opacity / 100
      canvas[x, y] = ChunkyPNG::Color.rgba(red, green, blue, new_alpha)
    end
  end
end