Title: [1030] trunk/image_voodoo: - Added new methods: flip_vertically, flip_horizontally, alpha, add_border
Revision
1030
Author
enebo
Date
2008-06-23 16:06:16 -0400 (Mon, 23 Jun 2008)

Log Message

- Added new methods: flip_vertically, flip_horizontally, alpha, add_border
- Add alias grayscale for greyscale
- Add documentation to code by popular demand

Modified Paths

Diff

Modified: trunk/image_voodoo/History.txt (1029 => 1030)


--- trunk/image_voodoo/History.txt	2008-06-18 05:38:58 UTC (rev 1029)
+++ trunk/image_voodoo/History.txt	2008-06-23 20:06:16 UTC (rev 1030)
@@ -8,3 +8,9 @@
 - Fixed greyscale now honors alpha channel
 - Added from_url which allows loading images from url
 - Allow all methods to be callable without block
+
+== 0.3
+
+- Added new methods: flip_vertically, flip_horizontally, alpha, add_border
+- Add alias grayscale for greyscale
+- Add documentation to code by popular demand

Modified: trunk/image_voodoo/README.txt (1029 => 1030)


--- trunk/image_voodoo/README.txt	2008-06-18 05:38:58 UTC (rev 1029)
+++ trunk/image_voodoo/README.txt	2008-06-23 20:06:16 UTC (rev 1030)
@@ -2,8 +2,8 @@
 
 == DESCRIPTION:
 
-ImageVoodoo is an Image manipulation library with a ImageScience-compatible API for
-JRuby.
+ImageVoodoo is an Image manipulation library with a ImageScience-compatible API
+for JRuby.
 
 http://jruby-extras.rubyforge.org/image_voodoo/
 

Modified: trunk/image_voodoo/bin/image_voodoo (1029 => 1030)


--- trunk/image_voodoo/bin/image_voodoo	2008-06-18 05:38:58 UTC (rev 1029)
+++ trunk/image_voodoo/bin/image_voodoo	2008-06-23 20:06:16 UTC (rev 1030)
@@ -27,12 +27,26 @@
   opts.separator ""
   opts.separator "Actions:"
 
+  opts.on("-a", "--alpha color_value", "Make color transparent in image") do |c|
+    if c !~ /[[:xdigit:]]{6,6}/
+      opts.usage "color_value is rrggbb in hexidecimal format"
+    end
+    actions << lambda {|img| img.alpha(c) }
+  end
+
   opts.on("-b", "--brightness SCALE,OFFSET", "Adjust brightness") do |args|
     scale, offset = args.split(/\,/i).map {|v| v.to_f}
     opts.usage "You need to specify proper scale and offset" unless scale && offset
     actions << lambda {|img| img.adjust_brightness(scale, offset) }
   end
 
+  opts.on("-B" "--border WIDTH,COLOR,STYLE", "Add a simple border") do |args|
+    width, color, style = args.split(/\,/i)
+    options = {:width => width, :color => color, :style => style }
+
+    actions << lambda {|img| img.add_border(options) }
+  end
+
   opts.on("-d", "--dimensions", "Print the image dimensions") do
     actions << lambda {|img| puts "#{img.width}x#{img.height}"; img }
   end
@@ -41,6 +55,10 @@
     actions << lambda {|img| img.greyscale }
   end
 
+  opts.on("-h", "--flip_horizontally") do
+    actions << lambda {|img| img.flip_horizontally }
+  end
+
   opts.on("-n", "--negative", "Make a negative out of the image") do
     actions << lambda {|img| img.negative }
   end
@@ -53,6 +71,10 @@
     actions << lambda {|img| img.thumbnail(size) }
   end
 
+  opts.on("-v", "--flip_vertically") do
+    actions << lambda {|img| img.flip_vertically }
+  end
+
   opts.on("-p", "--preview", "Preview the image. Close the frame window",
     "to continue, or quit the application to", "abort the action pipeline") do
     actions << lambda do |img|

Modified: trunk/image_voodoo/lib/image_voodoo/version.rb (1029 => 1030)


--- trunk/image_voodoo/lib/image_voodoo/version.rb	2008-06-18 05:38:58 UTC (rev 1029)
+++ trunk/image_voodoo/lib/image_voodoo/version.rb	2008-06-23 20:06:16 UTC (rev 1030)
@@ -1,3 +1,3 @@
 class ImageVoodoo
-  VERSION = "0.2"
+  VERSION = "0.3"
 end

Modified: trunk/image_voodoo/lib/image_voodoo.rb (1029 => 1030)


--- trunk/image_voodoo/lib/image_voodoo.rb	2008-06-18 05:38:58 UTC (rev 1029)
+++ trunk/image_voodoo/lib/image_voodoo.rb	2008-06-23 20:06:16 UTC (rev 1030)
@@ -1,7 +1,14 @@
-# ImageVoodoo is an ImageScience-API-compatible image manipulation library for JRuby.
 #
-# Example of usage:
+# = ImageVoodoo
+# == Description
 #
+# ImageVoodoo is an ImageScience-API-compatible image manipulation library for 
+# JRuby.  
+#
+# == Examples
+#
+# === Simple block-based examples
+#
 #    ImageVoodoo.with_image(ARGV[0]) do |img|
 #      img.cropped_thumbnail(100) { |img2| img2.save "CTH.jpg" }
 #      img.with_crop(100, 200, 400, 600) { |img2| img2.save "CR.jpg" }
@@ -11,6 +18,12 @@
 #        img2.save "HEH.png"
 #      end
 #    end
+#
+# === Non-block return (not image_science compatible)
+#
+# img = ImageVoodoo.with_image(ARGV[0])
+# negative_img = img.negative
+#
 class ImageVoodoo
   include Java
 
@@ -23,41 +36,88 @@
   import java.awt.image.RescaleOp
   import java.awt.image.BufferedImage
   JFile = java.io.File
-  BAIS = java.io.ByteArrayInputStream
-  BAOS = java.io.ByteArrayOutputStream
+  import java.io.ByteArrayInputStream
+  import java.io.ByteArrayOutputStream
   import javax.imageio.ImageIO
   import javax.swing.JFrame
 
   NEGATIVE_OP = LookupOp.new(ByteLookupTable.new(0, (0...254).to_a.reverse.to_java(:byte)), nil)
   GREY_OP = ColorConvertOp.new(ColorSpace.getInstance(ColorSpace::CS_GRAY), nil)
 
-  class JImagePanel < javax.swing.JPanel
-    def initialize(image, x=0, y=0)
-      super()
-      @image, @x, @y = image, x, y
-    end
-    def image=(image)
-      @image = image
-      invalidate
-    end
-    def getPreferredSize
-      java.awt.Dimension.new(@image.width, @image.height)
-    end
-    def paintComponent(graphics)
-      graphics.drawImage(@image.to_java, @x, @y, nil)
-    end
-  end
-
   def initialize(src)
     @src = ""
   end
 
-  # Foreach pixel new_pixel = pixel * scale + offset
+  #
+  # Add a border to the image and yield/return a new image.  The following
+  # options are supported:
+  #   - width: How thick is the border (default: 3)
+  #   - color: Which color is the border (in rrggbb hex value) 
+  #   - style: etched, raised, plain (default: plain)
+  #
+  def add_border(options = {})
+    border_width = options[:width].to_i || 2
+    color = hex_to_color(options[:color]) || hex_to_color("000000")
+    style = options[:style]
+    style = nil if style.to_sym == :plain
+    new_width, new_height = width + 2*border_width, height + 2*border_width
+    target = BufferedImage.new(new_width, new_height, color_type)
+    graphics = target.graphics
+    graphics.color = color
+    if style
+      raised = style.to_sym == :raised ? true : false
+      graphics.fill3DRect(0, 0, new_width, new_height, raised)
+    else
+      graphics.fill_rect(0, 0, new_width, new_height)
+    end
+    graphics.draw_image(@src, nil, border_width, border_width)
+    graphics.dispose
+    target = ImageVoodoo.new target
+    block_given? ? yield(target) : target
+  end
+
+  #
+  # Adjusts the brightness of each pixel in image by the following formula:
+  # new_pixel = pixel * scale + offset
+  #
   def adjust_brightness(scale, offset)
     image = ImageVoodoo.new internal_transform(RescaleOp.new(scale, offset, nil))
     block_given? ? yield(image) : image
   end
 
+  #
+  # Converts rgb hex color value to an alpha value an yields/returns the new 
+  # image.
+  #
+  def alpha(rgb)
+    color = hex_to_color(rgb)
+    target = BufferedImage.new(width, height, BufferedImage::TYPE_INT_ARGB)
+    graphics = target.graphics
+    graphics.set_composite(java.awt.AlphaComposite::Src)
+    graphics.draw_image(@src, nil, 0, 0)
+    graphics.dispose
+    0.upto(height-1) do |i|
+      0.upto(width-1) do |j|
+        target.setRGB(j, i, 0x8F1C1C) if target.getRGB(j, i) == color.getRGB
+      end
+    end
+    target = ImageVoodoo.new target
+    block_given? ? yield(target) : target
+  end
+
+  # 
+  # Write current image out as a stream of bytes using provided format.
+  # 
+  def bytes(format)
+    out = ByteArrayOutputStream.new
+    ImageIO.write(@src, format, out)
+    String.from_java_bytes(out.to_byte_array)
+  end
+
+  #
+  # Creates a square thumbnail of the image cropping the longest edge to 
+  # match the shortest edge, resizes to size, and yields/returns the new image. 
+  #
   def cropped_thumbnail(size)
     l, t, r, b, half = 0, 0, width, height, (width - height).abs / 2
     l, r = half, half + height if width > height
@@ -71,23 +131,51 @@
     end
   end
 
-  def height
-    @src.height
+  #
+  # Flips the image horizontally and yields/returns the new image.
+  #
+  def flip_horizontally
+    target = BufferedImage.new(width, height, color_type)
+    graphics = target.graphics
+    graphics.draw_image(@src, 0, 0, width, height, width, 0, 0, height, nil)
+    graphics.dispose
+    target = ImageVoodoo.new target
+    block_given? ? yield(target) : target
   end
 
-  def width
-    @src.width
+  #
+  # Flips the image vertically and yields/returns the new image.
+  #
+  def flip_vertically
+    target = BufferedImage.new(width, height, color_type)
+    graphics = target.graphics
+    graphics.draw_image(@src, 0, 0, width, height, 0, height, width, 0, nil)
+    graphics.dispose
+    target = ImageVoodoo.new target
+    block_given? ? yield(target) : target
   end
 
-  def to_java
-    @src
+  # 
+  # Creates a grayscale version of image and yields/returns the new image.
+  #
+  def greyscale
+    image = ImageVoodoo.new internal_transform(GREY_OP)
+    block_given? ? yield(image) : image
   end
+  alias_method :grayscale, :greyscale
 
+  # 
+  # Creates a negative and yields/returns the new image.
+  #
   def negative
     image = ImageVoodoo.new internal_transform(NEGATIVE_OP)
     block_given? ? yield(image) : image
   end
 
+  #
+  # Resizes the image to width and height using bicubic interpolation and 
+  # yields/returns the new image. 
+  #
   def resize(width, height)
     target = BufferedImage.new(width, height, color_type)
     graphics = target.graphics
@@ -105,11 +193,10 @@
     raise ArgumentError, ne.message
   end
 
-  def greyscale
-    image = ImageVoodoo.new internal_transform(GREY_OP)
-    block_given? ? yield(image) : image
-  end
-
+  # 
+  # Saves the image out to path. Changing the file extension will convert 
+  # the file type to the appropriate format. 
+  #
   def save(file)
     format = File.extname(file)
     return false if format == ""
@@ -118,6 +205,60 @@
     true
   end
 
+  #
+  # Resize (scale) the current image by the provided ratio and yield/return
+  # the new image.
+  #
+  def scale(ratio)
+    new_width, new_height = (width * ratio).to_i, (height * ratio).to_i
+
+    resize(new_width, new_height) do |image|
+      target = ImageVoodoo.new image
+      block_given? ? yield(target) : target
+    end
+  end
+
+  #
+  # Creates a proportional thumbnail of the image scaled so its longest 
+  # edge is resized to size and yields/returns the new image. 
+  #
+  def thumbnail(size)
+    scale(size.to_f / (width > height ? width : height))
+  end
+
+  #
+  # Crops an image to left, top, right, and bottom and then yields/returns the 
+  # new image. 
+  #
+  def with_crop(left, top, right, bottom)
+    image = ImageVoodoo.new @src.get_subimage(left, top, right-left, bottom-top)
+    block_given? ? yield(image) : image
+  end
+
+  # 
+  # A simple swing wrapper around an image voodoo object.
+  #
+  class JImagePanel < javax.swing.JPanel
+    def initialize(image, x=0, y=0)
+      super()
+      @image, @x, @y = image, x, y
+    end
+
+    def image=(image)
+      @image = image
+      invalidate
+    end
+
+    def getPreferredSize
+      java.awt.Dimension.new(@image.width, @image.height)
+    end
+
+    def paintComponent(graphics)
+      graphics.drawImage(@image.to_java, @x, @y, nil)
+    end
+  end
+
+  # Internal class for closing preview window
   class WindowClosed
     def initialize(block = nil)
       @block = block || proc { java.lang.System.exit(0) }
@@ -126,6 +267,9 @@
     def windowClosing(event); @block.call; end
   end
 
+  #
+  # Creates a viewable frame displaying current image within it.
+  #
   def preview(&block)
     frame = JFrame.new("Preview")
     frame.add_window_listener WindowClosed.new(block)
@@ -134,31 +278,11 @@
     frame.visible = true
   end
 
-  def bytes(format)
-    out = BAOS.new
-    ImageIO.write(@src, format, out)
-    String.from_java_bytes(out.to_byte_array)
-  end
-
-  def scale(ratio)
-    new_width = (width * ratio).to_i
-    new_height = (height * ratio).to_i
-    resize(new_width, new_height) do |image|
-      target = ImageVoodoo.new image
-      block_given? ? yield(target) : target
-    end
-  end
-
-  def thumbnail(size)
-    scale(size.to_f / (width > height ? width : height))
-  end
-
-  def with_crop(left, top, right, bottom)
-    image = ImageVoodoo.new(@src.get_subimage(left, top, right-left, bottom-top))
-    block_given? ? yield(image) : image
-  end
-
+  #
   # TODO: Figure out how to determine whether source has alpha or not
+  # Experimental: Read an image from the url source and yield/return that
+  # image.
+  #
   def self.from_url(source)
     url = ""
     image = java.awt.Toolkit.default_toolkit.create_image(url)
@@ -175,6 +299,9 @@
     raise ArgumentError.new "Trouble retrieving image: #{$!.message}"
   end
 
+  # 
+  # A top-level image loader opens path and then yields/returns the image.
+  #
   def self.with_image(file)
     readers = ImageIO.getImageReadersBySuffix(File.extname(file)[1..-1])
     raise TypeError, "unrecognized format for #{file}" unless readers.hasNext
@@ -184,18 +311,60 @@
     nil
   end
 
+  # 
+  # A top-level image loader reads bytes and then yields/returns the image.
+  #
   def self.with_bytes(bytes)
     bytes = bytes.to_java_bytes if String === bytes
-    image = ImageVoodoo.new ImageIO.read(BAIS.new(bytes))
+    image = ImageVoodoo.new ImageIO.read(ByteArrayInputStream.new(bytes))
     block_given? ? yield(image) : image
   end
 
+  #
+  # Returns the height of the image, in pixels. 
+  #
+  def height
+    @src.height
+  end
+
+  #
+  # Returns the width of the image, in pixels. 
+  #
+  def width
+    @src.width
+  end
+
+  #
+  # Returns the underlying Java BufferedImage associated with this object.
+  #
+  def to_java
+    @src
+  end
+
   private
+
+  #
+  # Converts a RGB hex value into a java.awt.Color object or dies trying
+  # with an ArgumentError.
+  #
+  def hex_to_color(rgb)
+    raise ArgumentError.new "hex rrggbb needed" if rgb !~ /[[:xdigit:]]{6,6}/
+
+    java.awt.Color.new(rgb[0,2].to_i(16), rgb[2,2].to_i(16), rgb[4,2].to_i(16))
+  end
+
+  # 
+  # Determines the best colorspace for a new image based on whether the
+  # existing image contains an alpha channel or not.
+  #
   def color_type
     return BufferedImage::TYPE_INT_ARGB if @src.color_model.has_alpha
     BufferedImage::TYPE_INT_RGB
   end
 
+  #
+  # Do simple AWT operation transformation to target.
+  #
   def internal_transform(operation, target=BufferedImage.new(width, height, color_type))
     graphics = target.graphics
     graphics.drawImage(@src, 0, 0, nil)
_______________________________________________
Jruby-extras-devel mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/jruby-extras-devel

Reply via email to