paulnicholson-acrylic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,79 @@
1
+ require 'cairo_tools'
2
+ require 'shape'
3
+ class ImageGenerator
4
+ include CairoTools
5
+
6
+ def self.colors
7
+ @@colors ||= {}
8
+ end
9
+
10
+ def self.color(name, color)
11
+ color = Color::HSL.new(color)
12
+ colors[name] = color
13
+ self.class_eval <<-"end;"
14
+ def #{name}
15
+ self.class.colors[:#{name}]
16
+ end
17
+ def #{name}!(a=nil)
18
+ set_color(a ? #{name}.a(a) : #{name})
19
+ end
20
+ end;
21
+ end
22
+
23
+ color :black, '000'
24
+ color :white, 'FFF'
25
+
26
+ def self.shapes
27
+ @@shapes ||= {}
28
+ end
29
+
30
+ def self.shape(name, width, height, &block)
31
+ shapes[name] = shape = Shape.new(name, width, height, block)
32
+ define_method("draw_#{name}") do |x, y|
33
+ shape.draw(cr, x, y)
34
+ end
35
+ end
36
+
37
+ def self.generate_image(path, *options)
38
+ new.generate_image(path, options)
39
+ end
40
+
41
+ def self.generate(name, *options)
42
+ generate_image(File.join(File.dirname(__FILE__), "../public/images", name), *options)
43
+ end
44
+
45
+ def self.preview(*options)
46
+ return unless $0.match(/#{name.underscore}.rb$/)
47
+ path = File.join(File.dirname($0), "generated.png")
48
+ options = Array(yield) if block_given?
49
+ instance = self.new
50
+ instance.preview = true
51
+ instance.generate_image(path, options)
52
+ `open #{path}`
53
+ sleep 1
54
+ File.unlink(path)
55
+ end
56
+
57
+ def self.image(name, options={:suite => true}, &block)
58
+ images[name] = block
59
+ suite_images << name if options[:suite]
60
+ end
61
+
62
+ def self.suite_images
63
+ @suite_images ||= []
64
+ end
65
+
66
+ def self.images
67
+ @images ||= {}
68
+ end
69
+
70
+ def self.suite(prefix, *args)
71
+ suite_images.each do |name|
72
+ generate("#{prefix}_#{name}.png", name, *args)
73
+ end
74
+ end
75
+
76
+ def draw(suffix, *args)
77
+ instance_eval(*args, &self.class.images[suffix])
78
+ end
79
+ end
@@ -0,0 +1,14 @@
1
+ require 'native_image_surface_extensions'
2
+ require 'color'
3
+
4
+ class Cairo::ImageSurface
5
+
6
+ def get_pixel(x, y)
7
+ integer = get_values(x, y)
8
+ a = integer >> 24 & 0xFF
9
+ r = integer >> 16 & 0xFF
10
+ g = integer >> 8 & 0xFF
11
+ b = integer & 0xFF
12
+ Color::RGB.new([r, g, b, a].map {|v| v.to_f/255})
13
+ end
14
+ end
data/lib/pascal.rb ADDED
@@ -0,0 +1,32 @@
1
+ # This was used to generate the coefficients for the gaussian blur. It would be better to make it dynamic.
2
+
3
+ class PascalTriangle
4
+ def pascal_triangle(levels)
5
+ values = [[1]]
6
+ (levels - 1).times do
7
+ level = [1]
8
+ values.last[1..-1].each_with_index do |x, i|
9
+ level << x + values.last[i]
10
+ end
11
+ level << 1
12
+ values << level
13
+ end
14
+ values
15
+ end
16
+
17
+ def sums(max_radius)
18
+ sums = []
19
+ (max_radius + 1).times do |i|
20
+ sums << 2**(i*2)
21
+ end
22
+ sums
23
+ end
24
+
25
+ def generate_c(max_radius=10)
26
+ values = pascal_triangle(max_radius*2 + 1).reject {|a| a.size % 2 == 0}
27
+ puts values.inspect.gsub('[', '{').gsub(']', '}')
28
+ puts sums(max_radius).inspect.gsub('[', '{').gsub(']', '}')
29
+ end
30
+ end
31
+
32
+ PascalTriangle.new.generate_c
data/lib/shape.rb ADDED
@@ -0,0 +1,14 @@
1
+ class Shape
2
+ include CairoTools
3
+ attr_reader :name, :width, :height, :cr, :proc
4
+ def initialize(name, width, height, proc)
5
+ @name, @width, @height, @proc = name, width, height, proc
6
+ end
7
+
8
+ def draw(cr, x, y)
9
+ @cr = cr
10
+ transform cr.matrix.translate(x, y) do
11
+ instance_eval(&proc)
12
+ end
13
+ end
14
+ end
data/lib/text_box.rb ADDED
@@ -0,0 +1,96 @@
1
+ class TextBox
2
+ attr_reader :generator, :lines, :x, :y, :valign, :width
3
+ attr_writer :x, :y, :valign, :width
4
+ def initialize(generator, x, y, width, height, valign)
5
+ @generator, @x, @y, @width, @height, @valign = generator, x, y, width, height, valign
6
+ @lines = []
7
+ end
8
+
9
+ def cr
10
+ generator.cr
11
+ end
12
+
13
+ def split_lines(text, options)
14
+ text.split(/\r?\n/).each do |line|
15
+ line(line, options)
16
+ end
17
+ end
18
+
19
+ def line(text, options={})
20
+ return if text.empty?
21
+ return split_lines(text, options) if text.include?("\n")
22
+ set_options(options)
23
+ extents = cr.text_extents(text)
24
+ options[:line_height] ||= cr.font_extents.height
25
+ if @width && extents.width > width
26
+ lines.push(*wrap(text).map {|line| [line, options]})
27
+ else
28
+ lines << [text, options]
29
+ end
30
+ end
31
+
32
+ def set_options(options)
33
+ cr.select_font_face(options[:face] || "Arial",
34
+ options[:italic] ? Cairo::FontSlant::ITALIC : Cairo::FontSlant::NORMAL,
35
+ options[:bold] ? Cairo::FontWeight::BOLD : Cairo::FontWeight::NORMAL)
36
+ cr.set_font_size(options[:size]) if options[:size]
37
+ end
38
+
39
+ def wrap(text)
40
+ split = text.split(' ')
41
+ lines = []
42
+ line = []
43
+ until split.empty?
44
+ until cr.text_extents(line.join(' ')).width > width || split.empty?
45
+ line << split.shift
46
+ end
47
+ split.unshift(line.pop) if cr.text_extents(line.join(' ')).width > width && line.size > 1
48
+ lines << line.join(' ')
49
+ line = []
50
+ end
51
+ lines
52
+ end
53
+
54
+ def draw
55
+ line_y = valign == :center ? y + (@height - height)/2 : y
56
+ cr.line_width = 1
57
+ # cr.rectangle(x, line_y, width, height)
58
+ # cr.stroke
59
+ lines.each do |(line, options)|
60
+ set_options(options)
61
+ baseline = line_y + cr.font_extents.ascent# - cr.font_extents.descent
62
+ draw_line(x, baseline, line, options)
63
+ # cr.move_to(x, baseline); cr.line_to(x+cr.text_extents(line).width, baseline)
64
+ # cr.stroke
65
+ line_y += options[:line_height]
66
+ end
67
+ end
68
+
69
+ def draw_line(x, y, text, options)
70
+ start = options[:align] == :center ? (x + width/2 - cr.text_extents(text).width/2) : x
71
+ cr.move_to(start, y)
72
+ cr.show_text(text)
73
+ cr.fill
74
+ if options[:underline]
75
+ cr.line_width = 1
76
+ underline = y + 3
77
+ cr.move_to(start, underline)
78
+ cr.line_to(start + cr.text_extents(text).width, underline)
79
+ cr.stroke
80
+ end
81
+ end
82
+
83
+ def move(x, y)
84
+ @x, @y = x, y
85
+ end
86
+
87
+ def width
88
+ @width || lines.map {|(text, options)| set_options(options); cr.text_extents(text).width}.max
89
+ end
90
+
91
+ def height
92
+ lines.sum do |(line, options)|
93
+ options[:line_height]
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,60 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class BumpMapTest < Nitrous::Test
4
+ test "bump map" do
5
+ dimensions 1, 1
6
+ tb = create_text_box(10, 10)
7
+ tb.line("Austin & Saki", :size => 160, :face => 'Savoye LET')
8
+ dimensions tb.width + 40, tb.height
9
+ black!
10
+ # cr.paint
11
+ # cr.operator = Cairo::OPERATOR_CLEAR
12
+ tb.draw
13
+
14
+ mask = @surface
15
+ dimensions width, height
16
+ cr.set_source(Cairo::SurfacePattern.new(mask))
17
+ cr.paint
18
+ @surface.blur(2)
19
+ # 8.times {@surface.blur(10)}
20
+
21
+ height_map = @surface
22
+ dimensions width, height
23
+ @surface.bump_map(height_map, width.to_i/2, -80, 800, 100)
24
+
25
+ bump = @surface
26
+ dimensions width, height
27
+ # cr.set_source_rgb(0.6, 0.6, 0.6)
28
+ # cr.mask(Cairo::SurfacePattern.new(mask))
29
+ cr.set_source(Cairo::SurfacePattern.new(bump))
30
+ cr.mask(Cairo::SurfacePattern.new(mask))
31
+ # cr.paint
32
+
33
+
34
+ preview
35
+ end
36
+
37
+ ztest "bump map3" do
38
+ dimensions 100, 100
39
+ load_image '../test/surface.png'
40
+ cr.paint
41
+ height_map = @surface
42
+ dimensions 100, 100
43
+ @surface.bump_map(height_map, -10, -30, 200, 30)
44
+ preview
45
+ end
46
+
47
+ ztest "bump map2" do
48
+ dimensions 5, 5
49
+ cr.circle(width.to_f/2, height.to_f/2, width/2 - 1)
50
+ black!
51
+ cr.fill
52
+
53
+ height_map = @surface
54
+
55
+ dimensions 5, 5
56
+ @surface.bump_map(height_map, -1, -1, 10, 2)
57
+
58
+ preview
59
+ end
60
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class CairoToolsTest < Nitrous::Test
4
+ test "get_pixel" do
5
+ dimensions 3, 3
6
+ assert_equal rgb(0, 0, 0, 0), get_pixel(1, 3)
7
+
8
+ cr.set_source_rgb(0, 1, 0)
9
+ cr.paint
10
+ assert_equal rgb(0, 1, 0), get_pixel(0, 0)
11
+
12
+ set_color rgb(1, 0, 0)
13
+ cr.paint
14
+ assert_equal rgb(1, 0, 0), get_pixel(0, 0)
15
+
16
+ set_color rgb(0, 0, 1)
17
+ cr.paint
18
+ assert_equal rgb(0, 0, 1), get_pixel(0, 0)
19
+
20
+ dimensions 10, 10
21
+ cr.rectangle 0.5, 0.5, 9, 9
22
+ cr.line_width = 1
23
+ cr.stroke
24
+ assert_equal black.to_rgb, get_pixel(0, 0)
25
+ assert_equal black.a(0).to_rgb, get_pixel(1, 1)
26
+ end
27
+
28
+ test "blur shouldn't wrap around" do
29
+ dimensions 3, 2
30
+ cr.rectangle 2, 0, 1, 2
31
+ black!
32
+ cr.fill
33
+ @surface.blur(1)
34
+ assert_equal rgb(0, 0, 0, 0), @surface.get_pixel(0, 1)
35
+ end
36
+ end
@@ -0,0 +1,71 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+ require 'color'
3
+
4
+ class ColorTest < Nitrous::Test
5
+ include Color
6
+
7
+ test "new with three components" do
8
+ assert_equal 0.5, rgb(0.4, 0.5, 0.6).g
9
+ end
10
+
11
+ test "new with alpha" do
12
+ assert_equal 0.5, rgb(0.2, 0.3, 0.4, 0.5).a
13
+ end
14
+
15
+ test "new with css" do
16
+ assert_equal rgb(0.25, 0.5, 1.0), rgb("4080FF")
17
+ assert_equal rgb(1.0, 2.0/3.0, 0.8), rgb("FAC")
18
+ assert_equal rgb(1.0, 2.0/3.0, 0.8), rgb("#FAC")
19
+ end
20
+
21
+ test "alpha should default to 1.0" do
22
+ assert_equal 1.0, rgb(1, 1, 1).a
23
+ assert_equal 1.0, hsl(1, 1, 1).a
24
+ end
25
+
26
+ test "to_css" do
27
+ assert_equal '3F7FFF', rgb(0.25, 0.5, 1.0).to_css
28
+ assert_equal '010101', rgb(1.0/255.0, 1.0/255.0, 1.0/255.0).to_css
29
+ assert_equal '000000', rgb(0, 0, 0).to_css
30
+ assert_equal 'FFAACC', rgb(1.0, 2.0/3.0, 0.8).to_css
31
+ assert_equal 'FF5BFE', hsl(5.0/6.0, 1.0, 0.68).to_css
32
+ end
33
+
34
+ test "equals" do
35
+ assert_equal rgb(0.1, 0.1, 0.1), rgb(0.1, 0.1, 0.1)
36
+ assert_not_equal rgb(0.1, 0.1, 0.1), rgb(0.1, 0.1, 0.2)
37
+ assert_not_equal rgb(0.1, 0.1, 0.1), hsl(0.1, 0.1, 0.1)
38
+ end
39
+
40
+ test "addition and subtraction" do
41
+ assert_equal rgb(0.5, 0.4, 0.3), rgb(0.3, 0.3, 0.3) + rgb(0.2, 0.1, 0.0)
42
+ # TODO: is this the right behavior for alpha?
43
+ assert_equal rgb(0.1, 0.2, 0.3, 0.0), rgb(0.3, 0.3, 0.3) - rgb(0.2, 0.1, 0.0)
44
+ assert_equal rgb(0.5, 0.4, 0.3), rgb(0.3, 0.3, 0.3) + [0.2, 0.1, 0.0]
45
+ assert_equal rgb(0.1, 0.2, 0.3), rgb(0.3, 0.3, 0.3) - [0.2, 0.1, 0.0]
46
+ end
47
+
48
+ test "to_hsl" do
49
+ assert_equal hsl(0.0, 1.0, 0.5), rgb(1.0, 0.0, 0.0).to_hsl
50
+ assert_equal hsl(1.0/3.0, 1.0, 0.5), rgb(0.0, 1.0, 0.0).to_hsl
51
+ assert_equal hsl(2.0/3.0, 1.0, 0.5), rgb(0.0, 0.0, 1.0).to_hsl
52
+ assert_equal hsl(7.0/12.0, 1.0, 0.25), rgb(0.0, 0.25, 0.5).to_hsl
53
+ assert_equal hsl(5.0/6.0, 1.0, 0.679), rgb("FF5BFF").to_hsl
54
+ assert_equal hsl(7.0/12.0, 1.0, 0.25, 0.5), rgb(0.0, 0.25, 0.5, 0.5).to_hsl
55
+ end
56
+
57
+ test "to_rgb" do
58
+ assert_equal rgb(1.0, 0.0, 0.0), hsl(0.0, 1.0, 0.5).to_rgb
59
+ assert_equal rgb(0.0, 1.0, 0.0), hsl(1.0/3.0, 1.0, 0.5).to_rgb
60
+ assert_equal rgb(0.0, 0.0, 1.0), hsl(2.0/3.0, 1.0, 0.5).to_rgb
61
+ assert_equal rgb(0.0, 0.25, 0.5), hsl(7.0/12.0, 1.0, 0.25).to_rgb
62
+ assert_equal rgb(0.0, 0.25, 0.5, 0.5), hsl(7.0/12.0, 1.0, 0.25, 0.5).to_rgb
63
+ assert_equal rgb(1, 1, 1, 0), hsl(0, 0, 1, 0).to_rgb
64
+ end
65
+
66
+ test "non-destructive setters" do
67
+ white = rgb(1, 1, 1)
68
+ assert_equal rgb(1, 1, 1, 0.5), white.a(0.5)
69
+ assert_equal rgb(1, 1, 1), white
70
+ end
71
+ end
data/test/surface.png ADDED
Binary file
@@ -0,0 +1,16 @@
1
+ $: << File.dirname(__FILE__) + "/../../nitrous/plugin/lib/" << File.dirname(__FILE__) + "/../ext/image_surface_extensions/"
2
+ require 'nitrous'
3
+ require 'cairo_tools'
4
+
5
+ class Nitrous::Test
6
+ include CairoTools
7
+
8
+ def preview
9
+ return unless Nitrous::TestContext.textmate?
10
+ path = File.join(File.dirname($0), "generated.png")
11
+ cr.target.write_to_png(path)
12
+ `open #{path}`
13
+ sleep 1
14
+ File.unlink(path)
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paulnicholson-acrylic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Austin Taylor
8
+ - Paul Nicholson
9
+ autorequire: acrylic
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-01-27 00:00:00 -08:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activesupport
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: A set of image manipulation tools built on top of Cairo
26
+ email:
27
+ executables: []
28
+
29
+ extensions:
30
+ - ext/image_surface_extensions/extconf.rb
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - Rakefile
35
+ - README
36
+ - TODO
37
+ - VERSION.yml
38
+ - lib/acrylic
39
+ - lib/acrylic.rb
40
+ - lib/border_tools.rb
41
+ - lib/cairo_tools.rb
42
+ - lib/color.rb
43
+ - lib/core_ext.rb
44
+ - lib/curve.rb
45
+ - lib/image_generator.rb
46
+ - lib/image_surface_extensions.rb
47
+ - lib/pascal.rb
48
+ - lib/shape.rb
49
+ - lib/text_box.rb
50
+ - test/bump_map_test.rb
51
+ - test/cairo_tools_test.rb
52
+ - test/color_test.rb
53
+ - test/surface.png
54
+ - test/test_helper.rb
55
+ - ext/image_surface_extensions/native_image_surface_extensions.c
56
+ - ext/image_surface_extensions/extconf.rb
57
+ has_rdoc: true
58
+ homepage: http://github.com/dotjerky/acrylic
59
+ post_install_message:
60
+ rdoc_options:
61
+ - --inline-source
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.2.0
81
+ signing_key:
82
+ specification_version: 2
83
+ summary: Photoshop for cool people.
84
+ test_files: []
85
+