lib/chunky_png/canvas/drawing.rb



module ChunkyPNG
  class Canvas
    
    module Drawing
      
      # Sets a point on the canvas by composing a pixel with its background color.
      def point(x, y, color)
        set_pixel(x, y, ChunkyPNG::Color.compose(color, get_pixel(x, y)))
      end
      
      # Draws an anti-aliased line using Xiaolin Wu's algorithm.
      #
      def line_xiaolin_wu(x0, y0, x1, y1, color)
        y0, y1, x0, x1 = y1, y0, x1, x0 if y0 > y1
        dx = x1 - x0
        sx = dx < 0 ? -1 : 1
        dx *= sx
        dy = y1 - y0
        
        if dy == 0 # vertical line
          Range.new(*[x0,x1].sort).each do |x|
            point(x, y0, color)
          end
        elsif dx == 0 # horizontal line
          (y0..y1).each do |y|
            point(x0, y, color)
          end
        elsif dx == dy # diagonal
          x0.step(x1, sx) do |x|
            point(x, y0, color)
            y0 += 1
          end
          
        elsif dy > dx  # vertical displacement
          point(x0, y0, color)
          e_acc = 0          
          e = ((dx << 16) / dy.to_f).round
          (y0...y1-1).each do |i|
            e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff
            x0 = x0 + sx if (e_acc <= e_acc_temp)
            w = 0xff - (e_acc >> 8)
            point(x0, y0, ChunkyPNG::Color.fade(color, w)) if include_xy?(x0, y0)
            y0 = y0 + 1
            point(x0 + sx, y0, ChunkyPNG::Color.fade(color, 0xff - w)) if include_xy?(x0 + sx, y0)
          end
          point(x1, y1, color)
          
        else # horizontal displacement
          point(x0, y0, color)
          e_acc = 0
          e = (dy << 16) / dx
          (dx - 1).downto(0) do |i|
            e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xffff
            y0 += 1 if (e_acc <= e_acc_temp)
            w = 0xff - (e_acc >> 8)
            point(x0, y0, ChunkyPNG::Color.fade(color, w)) if include_xy?(x0, y0)
            x0 += sx
            point(x0, y0 + 1, ChunkyPNG::Color.fade(color, 0xff - w)) if include_xy?(x0, y0 + 1)
          end
          point(x1, y1, color)
        end
        
        return self
      end
      
      alias_method :line, :line_xiaolin_wu
      
      def rect(x0, y0, x1, y1, line_color, fill_color = ChunkyPNG::Color::TRANSPARENT)
      
        # Fill
        [x0, x1].min.upto([x0, x1].max) do |x|
          [y0, y1].min.upto([y0, y1].max) do |y|
            point(x, y, fill_color)
          end
        end
        
        # Stroke
        line(x0, y0, x0, y1, line_color)
        line(x0, y1, x1, y1, line_color)
        line(x1, y1, x1, y0, line_color)
        line(x1, y0, x0, y0, line_color)
        
        return self
      end
    end
  end
end