Skip to content

Commit

Permalink
Refine purl core for processing animated GIFs.
Browse files Browse the repository at this point in the history
  • Loading branch information
nanki committed Oct 9, 2009
1 parent 6816d5c commit 5ab5c28
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 73 deletions.
33 changes: 28 additions & 5 deletions lib/cairo_util.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@


class Cairo::Surface
attr_accessor :delay
def to_blob
io = StringIO.new
self.write_to_png(io)
Expand All @@ -20,10 +21,13 @@ def self.from_blob(png_blob)
end

module CairoUtil
class CairoImageList < Array
end

def cairo(width = 10, height = 10)
context = cairo_context(width, height)
yield context
context.target
CairoImageList.new << context.target
end

def cairo_context(width = 10, height = 10)
Expand All @@ -32,12 +36,31 @@ def cairo_context(width = 10, height = 10)

def magick2cairo(image)
image.format = "png"
Cairo::ImageSurface.from_blob(image.to_blob)
new_image = Cairo::ImageSurface.from_blob(image.to_blob)
new_image.delay = image.delay if image.delay
new_image
end

def cairo2magick(image)
img = Magick::Image.from_blob(image.to_blob).shift
img.background_color = 'transparent'
img
new_image = Magick::Image.from_blob(image.to_blob).shift
new_image.background_color = 'transparent'
new_image.delay = image.delay if image.delay
new_image
end

def array2imagelist(array)
if array.all?{|v| Cairo::ImageSurface === v}
result = CairoImageList.new
elsif array.all?{|v| Magick::Image === v}
result = Magick::ImageList.new
else
array.each do |v|
Rails.logger.debug v.class
end

raise 'All images need to be same format.'
end
array.each{|v| result << v}
result
end
end
78 changes: 52 additions & 26 deletions lib/purl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,38 +24,64 @@ def initialize(options = {})
include ::CairoUtil

private
# process with block must return Result.new(ImageList or Image)
def process(image, options = {:as => :cairo})
if block_given?
case image
when Cairo::ImageSurface
if options[:as] == :cairo
yield image
elsif options[:as] == :magick
yield cairo2magick(image)
# process with block must return Result.new(ImageList)
def process(image, options = {:as => :cairo}, &block)
as = options[:as]
case image
when Cairo::ImageSurface
case as
when :cairo
process(CairoImageList.new << image, options, &block)
when :magick
process(cairo2magick(image), options, &block)
end
when Magick::Image
case as
when :cairo
process(magick2cairo(image), options, &block)
when :magick
process(Magick::ImageList.new << image, options, &block)
end
when Magick::ImageList
case as
when :cairo
if block_given?
process(process(image, options), options, &block)
else
result = CairoImageList.new
image.each{|img| result << magick2cairo(img)}
result
end
when Magick::Image
if options[:as] == :magick
yield image
elsif options[:as] == :cairo
yield magick2cairo(image)
when :magick
if block_given?
result = []
image.each{|img| result << yield(img)}
Result.new(array2imagelist(result.flatten))
else
image
end
end
when CairoImageList
case as
when :cairo
if block_given?
result = []
image.each{|img| result << yield(img)}
Result.new(array2imagelist(result.flatten))
else
image
end
when Magick::ImageList
if options[:as] == :magick
when :magick
if block_given?
process(process(image, options), options, &block)
else
result = Magick::ImageList.new
image.each do |img|
result.concat(yield(img))
end
Result.new(result)
elsif options[:as] == :cairo
raise "not implemented."
#yield magick2cairo(image)
image.each{|img| result << cairo2magick(img)}
result
end
else
Result.new(image)
end
else
image
raise 'never give up.'
end
end
end
2 changes: 1 addition & 1 deletion lib/purl/features/cairo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def ctx(w, h)
end

def xtc(ctx)
Result.new(ctx.target)
Result.new(CairoImageList.new << ctx.target)
end

def rgb(ctx, r, g, b)
Expand Down
11 changes: 4 additions & 7 deletions lib/purl/features/effect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ def blur(image, r)
r = [r, 10].min
d = r.quo(2.75)
process(image, :as => :magick) do |img|
Result.new(img.blur_channel(0, d, Magick::AllChannels))
img.blur_channel(0, d, Magick::AllChannels)
end
end

def round(image, r)
r = [r, 50].min
process(image, :as => :cairo) do |img|
img = cairo(img.width, img.height) do |ctx|
cairo(img.width, img.height) do |ctx|
w = img.width
h = img.height
c = [r, w / 2, h / 2].min
Expand All @@ -40,7 +40,6 @@ def round(image, r)
ctx.set_source(img, 0, 0)
ctx.paint
end
Result.new(img)
end
end

Expand All @@ -50,22 +49,20 @@ def shadow(image, r)
process(image, :as => :magick) do |img|
shadow = magick2cairo(img.blur_channel(0, d, Magick::AlphaChannel))

img = cairo(img.columns, img.rows) do |c|
cairo(img.columns, img.rows) do |c|
c.identity_matrix
c.set_source_rgba 0, 0, 0, 1
c.mask shadow, 0, 0
end
Result.new(img)
end
end

def opacify(image, opacify)
process(image, :as => :cairo) do |img|
img = cairo(img.width, img.height) do |ctx|
cairo(img.width, img.height) do |ctx|
ctx.set_source(img, 0, 0)
ctx.paint opacify
end
Result.new(img)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/purl/features/gyazo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def gyazo(id)
data = open(path).read
end

Result.new(Magick::Image.from_blob(data).shift.strip!)
Result.new(Magick::ImageList.new.from_blob(data))
end
end
end
Expand Down
64 changes: 36 additions & 28 deletions lib/purl/features/image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,68 +16,76 @@ def operators
end

def geom(target)
case
when target.respond_to?(:columns) && target.respond_to?(:rows)
# Magick::Image
Result.new(target, target.columns, target.rows)
when target.respond_to?(:target)
# Cairo::Context
Result.new(target, target.target.width, target.target.height)
when target.respond_to?(:width) && target.respond_to?(:height)
# Cairo::Surface
Result.new(target, target.width, target.height)
else
raise UnexpectedArgument
process(target, :as => :magick) do |img|
return Result.new(img, img.columns, img.rows)
end
end

def rotate(image, angle)
process(image, :as => :magick) do |img|
img.background_color = 'transparent'
img.rotate!(angle)
Result.new(img)
end
end

def _extend(image, x1, y1, x2, y2)
process(image, :as => :magick) do |img|
img.background_color = 'transparent'
img = img.extent(img.columns + x1 + x2, img.rows + y1 + y2, -x1, -y1)
Result.new(img)
img.extent(img.columns + x1 + x2, img.rows + y1 + y2, -x1, -y1)
end
end

def flip_x(image)
process(image, :as => :magick) do |img|
Result.new(img.flop!)
img.flop!
end
end

def flip_y(image)
process(image, :as => :magick) do |img|
Result.new(img.flip!)
img.flip!
end
end

def crop(image, x, y, w, h)
process(image, :as => :magick) do |img|
img.crop!(x, y, w, h, true)
Result.new(img)
end
end

require 'rational'
def composite(image1, image2)
process(image1, :as => :magick) do |img1|
process(image2, :as => :magick) do |img2|
h = [img1.rows, img2.rows].max
w = [img1.columns, img2.columns].max
w = [w, @max_width].min
h = [h, @max_height].min

img1.background_color = 'transparent'
Result.new(img1.extent(w, h).composite(img2, 0, 0, Magick::OverCompositeOp))
end
images1 = process(image1, :as => :magick)
images2 = process(image2, :as => :magick)

c1, c2 = 0, 0
gcd, lcm = images1.size.gcdlcm(images2.size)

result = Magick::ImageList.new
while lcm > 0
img1 = images1[c1]
img2 = images2[c2]
h = [img1.rows, img2.rows].max
w = [img1.columns, img2.columns].max
w = [w, @max_width].min
h = [h, @max_height].min


img2.background_color = 'transparent'

img = img1.extent(w, h).composite(img2.extent(w, h), 0, 0, Magick::OverCompositeOp)

# TODO: hmm...
delay = (images1.size > images2.size) ? img1.delay : img2.delay
img.delay = delay if delay
result << img

lcm -= 1
c1 += 1;c1 %= images1.size
c2 += 1;c2 %= images2.size
end

Result.new(result)
end
end
end
Expand Down
3 changes: 1 addition & 2 deletions lib/purl/features/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ def options=(options)

def load(image_id)
image = @image_class.find(image_id)
image = Magick::Image.from_blob(image.send(@data_column)).shift.strip!
Result.new(image)
Result.new(Magick::ImageList.new.from_blob(image.send(@data_column)))
end
end
end
Expand Down
5 changes: 2 additions & 3 deletions lib/purl/features/resize.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ def resize(image, w, h)
h = [h, @max_height].min
process(image, :as => :magick) do |img|
img.crop_resized!(w, h, Magick::CenterGravity)
Result.new(img)
end
end

Expand All @@ -30,7 +29,7 @@ def resize_fitto(image, w, h)
img.resize!(cols, rows)
end

Result.new(img)
img
end
end

Expand All @@ -44,7 +43,7 @@ def resize_upto(image, w, h)
end
end

Result.new(img)
img
end
end
end
Expand Down

0 comments on commit 5ab5c28

Please sign in to comment.