是非に及ばず

プログラミングに関する話題などを書いていきます(主にRailsやAndroidアプリ開発について)

Rubyでも画像のリサイズ、切り抜きが自由自在(かもしれない)クラスライブラリを作ってみた

PHPでの画像のリサイズ、切り抜きが自由自在「class.image.php」を読んだら、無償に作りたくなったので脊髄反射的にやってみた。
PHPのほうはGDを使っているみたいだけど、こっちはImageMagick(RMagick)を使ってます。RMagickだとWindowsLinuxの両方で動くので便利。クライアントPCがWindowsだからWindowsで開発して、テスト/本番はLinuxというパターンの自分の職場では重宝します。
そういえば、明日も仕事なんだけど、気がついたら既に4時過ぎという罠orz
かなりやっつけ仕事なので、突っ込みとか大歓迎(って誰も読んでないと思うけどw)

simple_image.rb

$KCODE = 'u'
require 'jcode'
require 'rubygems'
require 'RMagick'
require 'base64'

#
#= RMagickを利用した画像処理を簡単にできるはずのクラスライブラリ
#
#Author:: mingos
#Version:: 1.0 2008-02-22
#License:: Rubyライセンスに準拠
#          自由に使ってやってください
#
#=== 使い方
#==== PNGをGIFに変換して保存
#   image = SimpleImage.from_file('sample.png')
#   image.to_gif
#   image.path = 'sample.gif'
#   image.save
#
#==== リサイズ
#   30x50にリサイズして、pngに変換して保存
#   image.resize(30, 50)
#   image.to_png
#   image.path = 'new.png'
#   image.save
#
#==== 切り取り
#   (0,30)から100x120の部分を切り取る
#   image.crop({
#     :x => 0, :y => 30,
#     :width => 100, :height => 120
#   })
# 
#==== 画像の中心を切り取る
#   中心から100x100の部分を切り取る
#   image.crop_center(100, 100)
# 
#==== 指定した座標に画像を重ねる
#   (0, 50)にsample.pngを合成
#   image.composite('sample.png', 0, 50)
#
#==== 中心に画像を重ねる
#   sample.pngを中心に合成
#   image.composite_center('sample.png')
#
#=== 開発履歴
#* 1.0 2008-02-22
#  * とりあえず作成してみた 
class SimpleImage
  # 保存先のパス
  # from_fileで生成された場合、読み込んだファイルのパスが設定される
  # from_blobで生成された場合は設定されないので、saveメソッドを実行する前に
  # pathに保存先のパスを設定しておく必要がある
  attr_accessor :path

  #=== ファイルからSimpleImageオブジェクトを生成する
  #
  #_path_ :: 画像ファイルのパス 
  #戻り値 :: SimpleImageオブジェクト
  def self.from_file(path)
    unless File.exist?(path)
      raise ArgumentError, "File Not Found:'#{path}'"
    end
    image = SimpleImage.new(Magick::Image.read(path).first)
    image.path = path
    image
  end
  
  #=== blobからSimpleImageオブジェクトを生成する
  #
  #_blob_ :: blobデータ
  #戻り値 :: SimpleImageオブジェクト
  def self.from_blob(blob)
    SimpleImage.new(Magick::Image.from_blob(blob).first)
  end
  
  #=== 現在のフォーマットを返す
  #
  #戻り値 :: 現在の画像フォーマット(JPEG,GIF,PNG)
  def format
    @image.format
  end
 
  #=== 画像の幅
  #
  #戻り値 :: 画像の幅
  def width
    @image.columns
  end

  #=== 画像の高さ
  #
  #戻り値 :: 画像の高さ  
  def height
    @image.rows
  end

  #=== 画像データ(blog)を返す
  #
  #戻り値 :: 画像データ(blob)
  def content
    @image.to_blob
  end

  #=== PNGに変換する
  def to_png
    unless @image.format == "PNG"
      @image.format = 'PNG' 
    end
  end

  #=== JPEGに変換する
  # to_jpegと同じ
  def to_jpg
    to_jpeg
  end
  
  #=== JPEGに変換する
  def to_jpeg
    unless @image.format == "JPEG"
      @image.format = "JPEG"
    end
  end

  #=== GIFに変換する  
  def to_gif
    unless @image.format == "GIF"
      @image.format = "GIF"
    end
  end

  #=== 指定したサイズに変更する
  #
  #_width_ :: 変更後の幅
  #_height_ :: 変更後の高さ
  def resize(width, height)
    @image = @image.resize(width, height)
  end

  #=== 任意の座標から指定したサイズに切り抜く
  # 使用例: (0,30)の位置から100x50の画像を切り抜く
  # image = SimpleImage.from_file('sample.jpg')
  # image.crop({:x => 0, :y => 30, :width => 100, :height => 50})
  #
  #_opts_ :: オプション
  def crop(opts = {})
    opts = {
      :x => 0,
      :y => 0,
      :width => self.width,
      :height => self.height
    }.update opts
    
    @image = @image.crop(opts[:x], opts[:y], opts[:width], opts[:height], true)
  end

  #=== ファイルに保存する
  # pathメソッドで指定したパスに保存される
  # 
  # 例1: この場合はsample.pngが保存先として設定される
  # image = SimpleImage.from_file('sample.png')
  # image.save
  # 
  # 例2: pathメソッドで保存先をnew.pngに変更(別名保存)する
  # image = SimpleImage.from_file('sample.png')
  # image.path = 'new.png'
  # image.save
  # 
  # 例3: from_blobで生成した場合、pathを実行しないと保存先が設定されないため保存に失敗する
  # image = SimpleImage.from_blog(blob)
  # image.save これは失敗する
  # image.path = './sample.png' 保存先を指定する
  # image.save これは成功する
  # 
  #戻り値 :: 保存に成功した場合true、失敗時false
  def save
    unless @path
      return false
    end

    begin
      File.open(@path, 'w'){|f|
        f.binmode
        f.print @image.to_blob
      }
      return true
    rescue
    end

    return false
  end
  
  #=== 画像を中心から指定したサイズに切り抜く
  # 例: 画像の中心から50x100の画像を切り抜く
  # image = SimpleImage.from_file('sample.gif')
  # image.crop_center(50, 100)
  #
  #_width_ :: 幅
  #_height_ :: 高さ
  def crop_center(width, height)
    @image = @image.crop(Magick::CenterGravity, width, height, true)
  end

  #=== 指定のフォーマットに変換する
  # PNG, JPEG, GIFへ変換するメソッド(to_png, to_jpeg, to_gif)を内部で呼んでいるだけ
  #
  #_format_ :: 変換先のフォーマット。PNG,JPEG,GIFのいずれかを指定する
  def change_format(format)
    format = format.to_s.upcase
    if format == 'JPG'
      format = 'JPEG'
    end
    format = format.gsub(/jpeg/, 'jpg')
    case format
    when 'JPEG'
      self.to_jpeg
    when 'GIF'
      self.to_gif
    when 'PNG'
      self.to_png
    end    
  end
  
  #=== 指定したファイルを画像の任意の座標に合成する
  # 例: base.pngの(30, 50)にsample.pngを重ねる
  # image = SimpleImage.from_file('base.png')
  # image.composite('sample.png', 30, 50)
  #
  #_path_ :: 合成する画像ファイルのパス
  #_x_ :: 配置するx座標
  #_y_ :: 配置するy座標
  def composite(path, x, y)    
     begin
      src = Magick::Image.read(path).first
      # srcと自身のフォーマットを合わせる
      change_format(src.format)
      @image = @image.composite(src, x, y, Magick::OverCompositeOp)
    rescue Exception => e
      puts e.message
    end
  end
  
  #== 指定したファイル画像の中心に合成する
  # 例: base.pngの中心にsample.pngを重ねる
  # image = SimpleImage.from_file('base.png')
  # image.composite_center('sample.png')
  #
  #_path_ :: 合成する画像ファイルのパス
  def composite_center(path)
    begin
      src = Magick::Image.read(path).first
      # srcと自身のフォーマットを合わせる
      change_format(src.format)
      @image = @image.composite(src, Magick::CenterGravity, Magick::OverCompositeOp)
    rescue Exception => e
      puts e.message
    end
  end
  
private
  def initialize(image)
    @image = image
  end
end