lib/psd/renderer/compose.rb in psd-2.1.2 vs lib/psd/renderer/compose.rb in psd-3.1.2
- old
+ new
@@ -4,24 +4,19 @@
#
# Mostly based on similar code from libpsd.
module Compose
extend self
- DEFAULT_OPTS = {
- opacity: 255,
- fill_opacity: 255
- }
-
#
# Normal blend modes
#
- def normal(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def normal(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = blend_channel(r(bg), r(fg), mix_alpha)
new_g = blend_channel(g(bg), g(fg), mix_alpha)
new_b = blend_channel(b(bg), b(fg), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
@@ -30,39 +25,39 @@
#
# Subtractive blend modes
#
- def darken(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def darken(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = r(fg) <= r(bg) ? blend_channel(r(bg), r(fg), mix_alpha) : r(bg)
new_g = g(fg) <= g(bg) ? blend_channel(g(bg), g(fg), mix_alpha) : g(bg)
new_b = b(fg) <= b(bg) ? blend_channel(b(bg), b(fg), mix_alpha) : b(bg)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def multiply(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def multiply(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = blend_channel(r(bg), r(fg) * r(bg) >> 8, mix_alpha)
new_g = blend_channel(g(bg), g(fg) * g(bg) >> 8, mix_alpha)
new_b = blend_channel(b(bg), b(fg) * b(bg) >> 8, mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def color_burn(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def color_burn(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
calculate_foreground = Proc.new do |b, f|
if f > 0
f = ((255 - b) << 8) / f
f > 255 ? 0 : (255 - f)
@@ -76,15 +71,15 @@
new_b = blend_channel(b(bg), calculate_foreground.call(b(bg), b(fg)), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def linear_burn(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def linear_burn(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = blend_channel(r(bg), (r(fg) < (255 - r(bg))) ? 0 : r(fg) - (255 - r(bg)), mix_alpha)
new_g = blend_channel(g(bg), (g(fg) < (255 - g(bg))) ? 0 : g(fg) - (255 - g(bg)), mix_alpha)
new_b = blend_channel(b(bg), (b(fg) < (255 - b(bg))) ? 0 : b(fg) - (255 - b(bg)), mix_alpha)
@@ -93,41 +88,41 @@
#
# Additive blend modes
#
- def lighten(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def lighten(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = r(fg) >= r(bg) ? blend_channel(r(bg), r(fg), mix_alpha) : r(bg)
new_g = g(fg) >= g(bg) ? blend_channel(g(bg), g(fg), mix_alpha) : g(bg)
new_b = b(fg) >= b(bg) ? blend_channel(b(bg), b(fg), mix_alpha) : b(bg)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def screen(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def screen(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = blend_channel(r(bg), 255 - ((255 - r(bg)) * (255 - r(fg)) >> 8), mix_alpha)
new_g = blend_channel(g(bg), 255 - ((255 - g(bg)) * (255 - g(fg)) >> 8), mix_alpha)
new_b = blend_channel(b(bg), 255 - ((255 - b(bg)) * (255 - b(fg)) >> 8), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def color_dodge(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def color_dodge(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
calculate_foreground = Proc.new do |b, f|
f < 255 ? [(b << 8) / (255 - f), 255].min : b
end
@@ -136,15 +131,15 @@
new_b = blend_channel(b(bg), calculate_foreground.call(b(bg), b(fg)), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def linear_dodge(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def linear_dodge(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = blend_channel(r(bg), (r(bg) + r(fg)) > 255 ? 255 : r(bg) + r(fg), mix_alpha)
new_g = blend_channel(g(bg), (g(bg) + g(fg)) > 255 ? 255 : g(bg) + g(fg), mix_alpha)
new_b = blend_channel(b(bg), (b(bg) + b(fg)) > 255 ? 255 : b(bg) + b(fg), mix_alpha)
@@ -154,15 +149,15 @@
#
# Contrasting blend modes
#
- def overlay(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def overlay(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
calculate_foreground = Proc.new do |b, f|
if b < 128
b * f >> 7
else
@@ -175,15 +170,15 @@
new_b = blend_channel(b(bg), calculate_foreground.call(b(bg), b(fg)), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def soft_light(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def soft_light(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
calculate_foreground = Proc.new do |b, f|
c1 = b * f >> 8
c2 = 255 - ((255 - b) * (255 - f) >> 8)
((255 - b) * c1 >> 8) + (b * c2 >> 8)
@@ -194,15 +189,15 @@
new_b = blend_channel(b(bg), calculate_foreground.call(b(bg), b(fg)), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def hard_light(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def hard_light(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
calculate_foreground = Proc.new do |b, f|
if f < 128
b * f >> 7
else
@@ -215,15 +210,15 @@
new_b = blend_channel(b(bg), calculate_foreground.call(b(bg), b(fg)), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def vivid_light(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def vivid_light(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
calculate_foreground = Proc.new do |b, f|
if f < 255
[(b * b / (255 - f) + f * f / (255 - b)) >> 1, 255].min
else
@@ -236,15 +231,15 @@
new_b = blend_channel(b(bg), calculate_foreground.call(b(bg), b(fg)), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def linear_light(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def linear_light(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
calculate_foreground = Proc.new do |b, f|
if b < 255
[f * f / (255 - b), 255].min
else
@@ -257,15 +252,15 @@
new_b = blend_channel(b(bg), calculate_foreground.call(b(bg), b(fg)), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def pin_light(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def pin_light(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
calculate_foreground = Proc.new do |b, f|
if f >= 128
[b, (f - 128) * 2].max
else
@@ -278,15 +273,15 @@
new_b = blend_channel(b(bg), calculate_foreground.call(b(bg), b(fg)), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def hard_mix(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def hard_mix(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = blend_channel(r(bg), (r(bg) + r(fg) <= 255) ? 0 : 255, mix_alpha)
new_g = blend_channel(g(bg), (g(bg) + g(fg) <= 255) ? 0 : 255, mix_alpha)
new_b = blend_channel(b(bg), (b(bg) + b(fg) <= 255) ? 0 : 255, mix_alpha)
@@ -295,60 +290,58 @@
#
# Inversion blend modes
#
- def difference(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def difference(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = blend_channel(r(bg), (r(bg) - r(fg)).abs, mix_alpha)
new_g = blend_channel(g(bg), (g(bg) - g(fg)).abs, mix_alpha)
new_b = blend_channel(b(bg), (b(bg) - b(fg)).abs, mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
- def exclusion(fg, bg, opts={})
- return apply_opacity(fg, opts) if fully_transparent?(bg)
+ def exclusion(fg, bg, opacity)
+ return apply_opacity(fg, opacity) if fully_transparent?(bg)
return bg if fully_transparent?(fg)
- mix_alpha, dst_alpha = calculate_alphas(fg, bg, DEFAULT_OPTS.merge(opts))
+ mix_alpha, dst_alpha = calculate_alphas(fg, bg, opacity)
new_r = blend_channel(r(bg), r(bg) + r(fg) - (r(bg) * r(fg) >> 7), mix_alpha)
new_g = blend_channel(g(bg), g(bg) + g(fg) - (g(bg) * g(fg) >> 7), mix_alpha)
new_b = blend_channel(b(bg), b(bg) + b(fg) - (b(bg) * b(fg) >> 7), mix_alpha)
rgba(new_r, new_g, new_b, dst_alpha)
end
+ [:r, :g, :b, :a, :rgba, :fully_transparent?].each do |meth|
+ define_method(meth) { |*args| ChunkyPNG::Color.send(meth, *args) }
+ end
+
# If the blend mode is missing, we fall back to normal composition.
def method_missing(method, *args, &block)
- return ChunkyPNG::Color.send(method, *args) if ChunkyPNG::Color.respond_to?(method)
normal(*args)
end
private
- def calculate_alphas(fg, bg, opts)
- opacity = calculate_opacity(opts)
+ def calculate_alphas(fg, bg, opacity)
src_alpha = a(fg) * opacity >> 8
dst_alpha = a(bg)
mix_alpha = (src_alpha << 8) / (src_alpha + ((256 - src_alpha) * dst_alpha >> 8))
dst_alpha = dst_alpha + ((256 - dst_alpha) * src_alpha >> 8)
return mix_alpha, dst_alpha
end
- def calculate_opacity(opts)
- opts[:opacity] * opts[:fill_opacity] / 255
- end
-
- def apply_opacity(color, opts)
- (color & 0xffffff00) | ((color & 0x000000ff) * calculate_opacity(opts) / 255)
+ def apply_opacity(color, opacity)
+ (color & 0xffffff00) | ((color & 0x000000ff) * opacity / 255)
end
def blend_channel(bg, fg, alpha)
((bg << 8) + (fg - bg) * alpha) >> 8
end
\ No newline at end of file