painter 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README +0 -0
- data/Rakefile +0 -0
- data/lib/painter.rb +133 -0
- data/lib/painter/cgd2.rb +43 -0
- data/lib/painter/color.rb +110 -0
- data/lib/painter/image_struct.rb +35 -0
- data/lib/painter/line.rb +8 -0
- data/lib/painter/operation.rb +45 -0
- data/lib/painter/pixel.rb +8 -0
- data/lib/painter/point.rb +4 -0
- data/lib/painter/rect.rb +19 -0
- data/lib/painter/stdio.rb +8 -0
- data/lib/painter/text.rb +23 -0
- data/spec/color_spec.rb +36 -0
- data/spec/fixtures/Vera.ttf +0 -0
- data/spec/fixtures/sample.jpg +0 -0
- data/spec/image_spec.rb +24 -0
- data/spec/integration.rb +52 -0
- data/spec/output/captcha.png +0 -0
- data/spec/output/format.gif +0 -0
- data/spec/output/format.jpg +0 -0
- data/spec/output/format.png +0 -0
- data/spec/output/format.wbmp +0 -0
- data/spec/output/resized.png +0 -0
- data/spec/rect_spec.rb +27 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/text_spec.rb +23 -0
- metadata +111 -0
data/README
ADDED
File without changes
|
data/Rakefile
ADDED
File without changes
|
data/lib/painter.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require 'painter/image_struct'
|
3
|
+
require 'painter/stdio'
|
4
|
+
require 'painter/cgd2'
|
5
|
+
require 'painter/operation'
|
6
|
+
require 'painter/image'
|
7
|
+
require 'painter/font'
|
8
|
+
require 'painter/canvas'
|
9
|
+
require 'painter/color'
|
10
|
+
require 'painter/rect'
|
11
|
+
require 'painter/pixel'
|
12
|
+
require 'painter/text'
|
13
|
+
require 'painter/point'
|
14
|
+
require 'painter/line'
|
15
|
+
|
16
|
+
class Painter
|
17
|
+
attr_reader :image_pointer
|
18
|
+
|
19
|
+
def self.write(path, x, y, file_format)
|
20
|
+
instance = new
|
21
|
+
instance.initializeFromCreate(x,y)
|
22
|
+
yield instance
|
23
|
+
File.open(path, 'w') do |f|
|
24
|
+
instance.format(file_format) do |data|
|
25
|
+
f.write data
|
26
|
+
end
|
27
|
+
end
|
28
|
+
ensure
|
29
|
+
instance.release!
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.open(path, format = nil)
|
33
|
+
instance = new
|
34
|
+
instance.initializeFromOpen(path, format)
|
35
|
+
yield instance
|
36
|
+
ensure
|
37
|
+
instance.release!
|
38
|
+
end
|
39
|
+
|
40
|
+
def release!
|
41
|
+
CGD2::gdImageDestroy(@image_pointer)
|
42
|
+
end
|
43
|
+
|
44
|
+
def degrees(n)
|
45
|
+
n * 2 * Math::PI / 360
|
46
|
+
end
|
47
|
+
|
48
|
+
ALPHA_MAX = 127
|
49
|
+
ALPHA_OPAQUE = 0
|
50
|
+
ALPHA_TRANSPARENT = 127
|
51
|
+
RGB_MAX = 255
|
52
|
+
|
53
|
+
def release(ptr)
|
54
|
+
CGD2::gdImageDestroy(ptr)
|
55
|
+
end
|
56
|
+
|
57
|
+
def initializeFromCreate(x, y, options = {})
|
58
|
+
@image_pointer = FFI::Pointer.new(CGD2::gdImageCreateTrueColor(x, y))
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
FORMAT_TO_FUNCTION = {
|
63
|
+
'jpg' => :gdImageCreateFromJpeg,
|
64
|
+
'png' => :gdImageCreateFromPng,
|
65
|
+
'gif' => :gdImageCreateFromGif,
|
66
|
+
'gd' => :gdImageCreateFromGd,
|
67
|
+
'gd2' => :gdImageCreateFromGd2,
|
68
|
+
'wbmp' => :gdImageCreateFromWBMP,
|
69
|
+
'xbm' => :gdImageCreateFromXbm
|
70
|
+
}
|
71
|
+
|
72
|
+
def format_from_filename(path)
|
73
|
+
path[/\.([^.]*)$/, 1].downcase
|
74
|
+
end
|
75
|
+
|
76
|
+
def initializeFromOpen(path, format = nil)
|
77
|
+
f = Painter::Stdio.fopen(path, "rb")
|
78
|
+
format ||= format_from_filename(path)
|
79
|
+
function_name = FORMAT_TO_FUNCTION[format.to_s]
|
80
|
+
raise "Unknown format: #{format}" unless function_name
|
81
|
+
@image_pointer = CGD2.send(function_name, f)
|
82
|
+
Painter::Stdio.fclose(f)
|
83
|
+
end
|
84
|
+
|
85
|
+
def info
|
86
|
+
@info ||= Painter::ImageStruct.new @image_pointer
|
87
|
+
end
|
88
|
+
|
89
|
+
# Operations
|
90
|
+
{'pixel' => 'Pixel', 'rect' => 'Rect', 'text' => 'Text', 'line' => 'Line'}.each do |op, klass|
|
91
|
+
class_eval <<-END
|
92
|
+
def #{op}
|
93
|
+
#{op}_instance = #{klass}.new(self)
|
94
|
+
yield #{op}_instance
|
95
|
+
#{op}_instance.draw
|
96
|
+
end
|
97
|
+
END
|
98
|
+
end
|
99
|
+
|
100
|
+
def resize_to(destination)
|
101
|
+
CGD2::gdImageCopyResampled(destination.image_pointer, self.image_pointer,
|
102
|
+
0, 0, 0, 0,
|
103
|
+
destination.info.x, destination.info.y, self.info.x, self.info.y)
|
104
|
+
end
|
105
|
+
|
106
|
+
OUTPUT_FORMAT_TO_FUNCTION = {
|
107
|
+
'gif' => :gdImageGifPtr,
|
108
|
+
'jpeg' => :gdImageJpegPtr,
|
109
|
+
'jpg' => :gdImageJpegPtr,
|
110
|
+
'png' => :gdImagePngPtr,
|
111
|
+
'wbmp' => :gdImageWBMPPtr,
|
112
|
+
'gd' => :gdImageGdPtr,
|
113
|
+
'gd2' => :gdImageGd2
|
114
|
+
}
|
115
|
+
def format(file_format, quality = 100)
|
116
|
+
size = FFI::MemoryPointer.new(:pointer)
|
117
|
+
function_name = OUTPUT_FORMAT_TO_FUNCTION[file_format.to_s]
|
118
|
+
raise "Unknown format: #{file_format}" unless function_name
|
119
|
+
args = [@image_pointer, size]
|
120
|
+
if function_name == :gdImageJpegPtr
|
121
|
+
args << quality
|
122
|
+
end
|
123
|
+
begin
|
124
|
+
ip = CGD2.send(function_name, *args)
|
125
|
+
size = size.get_int(0)
|
126
|
+
data = ip.get_bytes(0, size)
|
127
|
+
yield data
|
128
|
+
ensure
|
129
|
+
CGD2::gdFree(ip)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
data/lib/painter/cgd2.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
class Painter
|
2
|
+
module CGD2
|
3
|
+
extend FFI::Library
|
4
|
+
local_path = ["/opt/local/lib/libgd.2.dylib", '/usr/lib/libgd.so.2'].detect do |lib|
|
5
|
+
File.exists?(lib)
|
6
|
+
end
|
7
|
+
ffi_lib local_path
|
8
|
+
|
9
|
+
# BGD_DECLARE(gdImagePtr) gdImageCreate (int sx, int sy)
|
10
|
+
attach_function :gdImageCreate, [:int, :int], :pointer
|
11
|
+
attach_function :gdImageCreateTrueColor, [:int, :int], :pointer
|
12
|
+
|
13
|
+
# void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color) (FUNCTION)
|
14
|
+
attach_function :gdImageFilledRectangle, [:pointer, :int, :int, :int, :int, :int], :void
|
15
|
+
|
16
|
+
attach_function :gdImageSetPixel, [:pointer, :int, :int, :int], :void
|
17
|
+
attach_function :gdImageLine, [:pointer, :int, :int, :int, :int, :int], :void
|
18
|
+
|
19
|
+
#char *gdImageStringFT(gdImagePtr im, int *brect, int fg, char *fontname, double ptsize, double angle, int x, int y, char *string) (FUNCTION)
|
20
|
+
attach_function :gdImageStringFT, [:pointer, :pointer, :int, :string, :double, :double, :int, :int, :string], :pointer
|
21
|
+
|
22
|
+
# void* gdImagePngPtr(gdImagePtr im, int *size) (FUNCTION)
|
23
|
+
[:gdImageGifPtr, :gdImagePngPtr, :gdImageWBMPPtr,
|
24
|
+
:gdImageGdPtr, :gdImageGd2].each do |format_function|
|
25
|
+
attach_function format_function, [:pointer, :pointer], :pointer
|
26
|
+
end
|
27
|
+
|
28
|
+
attach_function :gdImageJpegPtr, [:pointer, :pointer, :int], :pointer
|
29
|
+
|
30
|
+
[:gdImageCreateFromJpeg, :gdImageCreateFromPng, :gdImageCreateFromGif,
|
31
|
+
:gdImageCreateFromGd, :gdImageCreateFromGd2, :gdImageCreateFromWBMP,
|
32
|
+
:gdImageCreateFromXbm].each do |format_function|
|
33
|
+
attach_function format_function, [:pointer], :pointer
|
34
|
+
end
|
35
|
+
|
36
|
+
#Resizing
|
37
|
+
attach_function :gdImageCopyResampled, [:pointer, :pointer, :int, :int, :int, :int, :int, :int, :int, :int], :void
|
38
|
+
|
39
|
+
# gdImageDestroy(gdImagePtr im) (FUNCTION)
|
40
|
+
attach_function :gdImageDestroy, [:pointer], :void
|
41
|
+
attach_function :gdFree, [:pointer], :void
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
class Painter
|
2
|
+
class Color
|
3
|
+
attr_reader :rgba
|
4
|
+
alias :to_i :rgba
|
5
|
+
class << self
|
6
|
+
alias [] new
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(r, g, b, a = ALPHA_OPAQUE)
|
10
|
+
r = self.class.normalize(r, RGB_MAX, :red)
|
11
|
+
g = self.class.normalize(g, RGB_MAX, :green)
|
12
|
+
b = self.class.normalize(b, RGB_MAX, :blue)
|
13
|
+
a = self.class.normalize(a, ALPHA_MAX, :alpha)
|
14
|
+
|
15
|
+
init_with_rgba(self.class.pack(r, g, b, a))
|
16
|
+
end
|
17
|
+
|
18
|
+
def init_with_rgba(rgba, index = nil, palette = nil) #:nodoc:
|
19
|
+
@rgba = rgba
|
20
|
+
@index = index
|
21
|
+
@palette = palette
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.pack(r, g, b, a) #:nodoc:
|
26
|
+
(a << 24) + (r << 16) + (g << 8) + b
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
s = 'RGB'
|
31
|
+
s += "A" if alpha != ALPHA_OPAQUE
|
32
|
+
s += "[#{@index}]" if @index
|
33
|
+
s += '#' + [red, green, blue].map { |e| '%02X' % e }.join('')
|
34
|
+
s += '%02X' % alpha if alpha != ALPHA_OPAQUE
|
35
|
+
s
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def self.normalize(value, max, component = nil) #:nodoc:
|
40
|
+
case value
|
41
|
+
when Integer
|
42
|
+
(value < 0) ? 0 : (value > max) ? max : value
|
43
|
+
when Float
|
44
|
+
normalize((value * max).round, max, component)
|
45
|
+
when Color
|
46
|
+
value.send(component)
|
47
|
+
else
|
48
|
+
return normalize(value.to_i, max) if value.respond_to?(:to_i)
|
49
|
+
raise TypeError
|
50
|
+
end
|
51
|
+
end
|
52
|
+
# Return the red component of this color (0..RGB_MAX).
|
53
|
+
def red
|
54
|
+
(rgba & 0xFF0000) >> 16
|
55
|
+
end
|
56
|
+
alias r red
|
57
|
+
|
58
|
+
# Modify the red component of this color. If this color is associated
|
59
|
+
# with a palette entry, this also modifies the palette.
|
60
|
+
#def red=(value)
|
61
|
+
# self.rgba = (rgba & ~0xFF0000) |
|
62
|
+
# (self.class.normalize(value, RGB_MAX, :red) << 16)
|
63
|
+
#end
|
64
|
+
#alias r= red=
|
65
|
+
|
66
|
+
# Return the green component of this color (0..RGB_MAX).
|
67
|
+
def green
|
68
|
+
(rgba & 0x00FF00) >> 8
|
69
|
+
end
|
70
|
+
alias g green
|
71
|
+
|
72
|
+
# Modify the green component of this color. If this color is associated
|
73
|
+
# with a palette entry, this also modifies the palette.
|
74
|
+
#def green=(value)
|
75
|
+
# self.rgba = (rgba & ~0x00FF00) |
|
76
|
+
# (self.class.normalize(value, RGB_MAX, :green) << 8)
|
77
|
+
#end
|
78
|
+
#alias g= green=
|
79
|
+
|
80
|
+
# Return the blue component of this color (0..RGB_MAX).
|
81
|
+
def blue
|
82
|
+
rgba & 0x0000FF
|
83
|
+
end
|
84
|
+
alias b blue
|
85
|
+
|
86
|
+
# Modify the blue component of this color. If this color is associated
|
87
|
+
# with a palette entry, this also modifies the palette.
|
88
|
+
#def blue=(value)
|
89
|
+
# self.rgba = (rgba & ~0x0000FF) |
|
90
|
+
# self.class.normalize(value, RGB_MAX, :blue)
|
91
|
+
#end
|
92
|
+
#alias b= blue=
|
93
|
+
|
94
|
+
# Return the alpha component of this color (0..ALPHA_MAX).
|
95
|
+
def alpha
|
96
|
+
(rgba & 0x7F000000) >> 24
|
97
|
+
end
|
98
|
+
alias a alpha
|
99
|
+
|
100
|
+
# Modify the alpha component of this color. If this color is associated
|
101
|
+
# with a palette entry, this also modifies the palette.
|
102
|
+
def alpha=(value)
|
103
|
+
self.rgba = (rgba & ~0xFF000000) |
|
104
|
+
(self.class.normalize(value, ALPHA_MAX, :alpha) << 24)
|
105
|
+
end
|
106
|
+
alias a= alpha=
|
107
|
+
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Painter
|
2
|
+
class ImageStruct < FFI::Struct
|
3
|
+
MAXCOLORS = 256
|
4
|
+
layout :pixels, :pointer,
|
5
|
+
:sx, :int,
|
6
|
+
:sy, :int,
|
7
|
+
:colorsTotal, :int,
|
8
|
+
:red, [:int, MAXCOLORS],
|
9
|
+
:green, [:int, MAXCOLORS],
|
10
|
+
:blue, [:int, MAXCOLORS],
|
11
|
+
:open, [:int, MAXCOLORS],
|
12
|
+
:transparent, :int,
|
13
|
+
:polyInts, :pointer,
|
14
|
+
:polyAllocated, :int,
|
15
|
+
:brush, :pointer,
|
16
|
+
:tile, :pointer,
|
17
|
+
:brushColorMap, [:int, MAXCOLORS],
|
18
|
+
:tileColorMap, [:int, MAXCOLORS],
|
19
|
+
:styleLength, :int,
|
20
|
+
:stylePos, :int,
|
21
|
+
:style, :pointer,
|
22
|
+
:interlace, :int,
|
23
|
+
:alpha, [:int, MAXCOLORS],
|
24
|
+
:trueColor, :int,
|
25
|
+
:tpixels, :pointer,
|
26
|
+
:alphaBlendingFlag, :int,
|
27
|
+
:saveAlphaFlag, :int
|
28
|
+
def x
|
29
|
+
self[:sx]
|
30
|
+
end
|
31
|
+
def y
|
32
|
+
self[:sy]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/painter/line.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
class Painter
|
2
|
+
class Operation
|
3
|
+
def self.pretty_accessor(*names)
|
4
|
+
names.each do |n|
|
5
|
+
attr_reader n.to_sym
|
6
|
+
class_eval <<-END
|
7
|
+
def #{n}(v)
|
8
|
+
@_#{n} = v
|
9
|
+
end
|
10
|
+
def _#{n}
|
11
|
+
@_#{n}
|
12
|
+
end
|
13
|
+
END
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def initialize(i)
|
17
|
+
@image = i
|
18
|
+
end
|
19
|
+
|
20
|
+
pretty_accessor :at
|
21
|
+
|
22
|
+
%w[at from to].each do |point|
|
23
|
+
class_eval <<-END
|
24
|
+
def #{point}(x,y)
|
25
|
+
@_#{point} = Point.new(x,y)
|
26
|
+
end
|
27
|
+
END
|
28
|
+
end
|
29
|
+
|
30
|
+
def angle(n)
|
31
|
+
@_angle = (n * Math::PI / 180.0)
|
32
|
+
end
|
33
|
+
|
34
|
+
def _angle
|
35
|
+
@_angle || 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def color(r,g,b,a = nil)
|
39
|
+
@_color = Painter::Color.new(r,g,b,a)
|
40
|
+
end
|
41
|
+
def _color
|
42
|
+
@_color || Painter::Color.new(1, 1, 1)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/painter/rect.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class Painter
|
2
|
+
class Rect < Operation
|
3
|
+
pretty_accessor :bottomX, :bottomY
|
4
|
+
|
5
|
+
def topLeft(x,y)
|
6
|
+
@_topLeft = Point.new(x,y)
|
7
|
+
end
|
8
|
+
|
9
|
+
def bottomRight(x,y)
|
10
|
+
@_bottomRight = Point.new(x,y)
|
11
|
+
end
|
12
|
+
|
13
|
+
def draw
|
14
|
+
::Painter::CGD2::gdImageFilledRectangle(@image.image_pointer,
|
15
|
+
@_topLeft.x, @_topLeft.y,
|
16
|
+
@_bottomRight.x, @_bottomRight.y, _color.rgba)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/painter/text.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
class Painter
|
2
|
+
class Text < Operation
|
3
|
+
class BlankFontError < StandardError
|
4
|
+
end
|
5
|
+
class MissingFontError < StandardError
|
6
|
+
end
|
7
|
+
pretty_accessor :font, :size, :text, :point
|
8
|
+
|
9
|
+
def draw
|
10
|
+
raise BlankFontError if _font.nil?
|
11
|
+
raise MissingFontError, _font unless File.exists?(_font)
|
12
|
+
brect = FFI::MemoryPointer.new(:int, 8)
|
13
|
+
error = CGD2::gdImageStringFT(@image.image_pointer, brect, _color.rgba, _font,
|
14
|
+
_size.to_f, _angle.to_f, _at.x, _at.y, _text.gsub('&', '&'))
|
15
|
+
raise "Drawing error: #{error}" unless error.null?
|
16
|
+
brect = brect.read_array_of_int(8)
|
17
|
+
{ :lower_left => [brect[0], brect[1]],
|
18
|
+
:lower_right => [brect[2], brect[3]],
|
19
|
+
:upper_right => [brect[4], brect[5]],
|
20
|
+
:upper_left => [brect[6], brect[7]]}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/spec/color_spec.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
2
|
+
|
3
|
+
describe Painter::Color do
|
4
|
+
it "should initialize from int" do
|
5
|
+
Painter::Color.new(50,100,200).rgba.should eql(3302600)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should initialize with []" do
|
9
|
+
Painter::Color[50,100,200].rgba.should eql(3302600)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should have to_i" do
|
13
|
+
color = Painter::Color[50,100,200]
|
14
|
+
color.rgba.should eql(3302600)
|
15
|
+
color.to_i.should eql(color.rgba)
|
16
|
+
end
|
17
|
+
|
18
|
+
[
|
19
|
+
[:red, 50],
|
20
|
+
[:green, 100],
|
21
|
+
[:blue, 200]
|
22
|
+
].each do |color_name, value|
|
23
|
+
it "should return the #{color_name} value" do
|
24
|
+
color = Painter::Color[50,100,200]
|
25
|
+
color.send(color_name).should eql(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return color #{color_name} by first letter" do
|
29
|
+
color_name = color_name.to_s[0..0].to_sym
|
30
|
+
color = Painter::Color[50,100,200]
|
31
|
+
color.send(color_name).should eql(value)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
end
|
Binary file
|
Binary file
|
data/spec/image_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
2
|
+
|
3
|
+
describe Painter::Image::TrueColor do
|
4
|
+
it_should_behave_like "painter"
|
5
|
+
|
6
|
+
before do
|
7
|
+
Painter::CGD2.stub!(:gdFree)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should generate png string" do
|
11
|
+
image = Painter::Image::TrueColor.new(100, 100)
|
12
|
+
Painter::CGD2.should_receive(:gdImagePngPtr).and_return(mock("Pointer", :get_bytes => ''))
|
13
|
+
image.png do |data|
|
14
|
+
data.size.should > 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should read the image struct" do
|
19
|
+
Painter.open("#{File.dirname(__FILE__)}/fixtures/sample.jpg", :jpg) do |i|
|
20
|
+
i.info.x.should == 438
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/spec/integration.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
2
|
+
|
3
|
+
describe 'integration' do
|
4
|
+
it "should draw an image" do
|
5
|
+
srand(Time.now.to_i)
|
6
|
+
Painter.write("#{File.dirname(__FILE__)}/output/captcha.png", 200, 100, :png) do |painter|
|
7
|
+
painter.rect do |rect|
|
8
|
+
rect.color 255, 255, 255
|
9
|
+
rect.topLeft 0, 0
|
10
|
+
rect.bottomRight 200, 100
|
11
|
+
end
|
12
|
+
painter.text do |text|
|
13
|
+
text.font "#{File.dirname(__FILE__)}/fixtures/Vera.ttf"
|
14
|
+
text.at 10, 50
|
15
|
+
text.text "Hello World"
|
16
|
+
text.angle rand(20)
|
17
|
+
text.size 14
|
18
|
+
end
|
19
|
+
offset = rand(10)
|
20
|
+
0.upto(300) do |i|
|
21
|
+
painter.pixel do |pixel|
|
22
|
+
pixel.at i, ((Math.sin((i + offset) / (4 * Math::PI)) * 30).to_i + 50)
|
23
|
+
pixel.color(rand(5), rand(5), rand(5))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
painter.line do |line|
|
27
|
+
line.from 0, (25 + rand(50))
|
28
|
+
line.to 200, (25 + rand(50))
|
29
|
+
line.color rand(10), rand(10), rand(10)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should write to a variety of formats" do
|
35
|
+
[:jpg, :png, :wbmp, :gif].each do |format|
|
36
|
+
Painter.write("#{File.dirname(__FILE__)}/output/format.#{format}", 48, 48, format) do |output|
|
37
|
+
Painter.open("#{File.dirname(__FILE__)}/fixtures/sample.jpg", :jpg) do |source|
|
38
|
+
source.resize_to(output)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should resize images" do
|
45
|
+
Painter.write("#{File.dirname(__FILE__)}/output/resized.png", 48, 48, :png) do |output|
|
46
|
+
Painter.open("#{File.dirname(__FILE__)}/fixtures/sample.jpg", :jpg) do |source|
|
47
|
+
source.resize_to(output)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/spec/rect_spec.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
2
|
+
|
3
|
+
describe "rect" do
|
4
|
+
it_should_behave_like "painter"
|
5
|
+
before(:each) do
|
6
|
+
@painter = Painter.new
|
7
|
+
@painter.initializeFromCreate(640, 480)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should call GD draw function" do
|
11
|
+
Painter::CGD2.should_receive(:gdImageFilledRectangle).with(@painter.image_pointer, 0, 0, 10, 10, @white.rgba)
|
12
|
+
@painter.rect do |rect|
|
13
|
+
rect.topLeft 0, 0
|
14
|
+
rect.bottomRight 10, 10
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should convert Color to int" do
|
19
|
+
Painter::CGD2.should_receive(:gdImageFilledRectangle).with(@painter.image_pointer, 0, 0, 10, 10, @black.rgba)
|
20
|
+
@painter.rect do |rect|
|
21
|
+
rect.color 0, 0, 0
|
22
|
+
rect.topLeft 0, 0
|
23
|
+
rect.bottomRight 10, 10
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/text_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
|
2
|
+
|
3
|
+
describe "text" do
|
4
|
+
it_should_behave_like "painter"
|
5
|
+
before(:each) do
|
6
|
+
@painter = Painter.new
|
7
|
+
@painter.initializeFromCreate(640, 480)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should call C function on draw" do
|
11
|
+
Painter::CGD2.should_receive(:gdImageStringFT).
|
12
|
+
with(@painter.image_pointer, anything, @black.rgba, "#{File.dirname(__FILE__)}/fixtures/Vera.ttf", 14.0, 0.0, 100, 50, "Hello World").
|
13
|
+
and_return(mock("Pointer", :null? => true))
|
14
|
+
@painter.text do |text|
|
15
|
+
text.color 0, 0, 0
|
16
|
+
text.font "#{File.dirname(__FILE__)}/fixtures/Vera.ttf"
|
17
|
+
text.at 100, 50
|
18
|
+
text.text "Hello World"
|
19
|
+
text.angle 0
|
20
|
+
text.size 14
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: painter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ben Sandofsky
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-05-20 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: ffi
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: A clean wrapper around the GD2 graphics library, based on gd2-ffi
|
36
|
+
email: sandofsky@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- lib/painter/cgd2.rb
|
45
|
+
- lib/painter/color.rb
|
46
|
+
- lib/painter/image_struct.rb
|
47
|
+
- lib/painter/line.rb
|
48
|
+
- lib/painter/operation.rb
|
49
|
+
- lib/painter/pixel.rb
|
50
|
+
- lib/painter/point.rb
|
51
|
+
- lib/painter/rect.rb
|
52
|
+
- lib/painter/stdio.rb
|
53
|
+
- lib/painter/text.rb
|
54
|
+
- lib/painter.rb
|
55
|
+
- spec/color_spec.rb
|
56
|
+
- spec/fixtures/sample.jpg
|
57
|
+
- spec/fixtures/Vera.ttf
|
58
|
+
- spec/image_spec.rb
|
59
|
+
- spec/integration.rb
|
60
|
+
- spec/output/captcha.png
|
61
|
+
- spec/output/format.gif
|
62
|
+
- spec/output/format.jpg
|
63
|
+
- spec/output/format.png
|
64
|
+
- spec/output/format.wbmp
|
65
|
+
- spec/output/resized.png
|
66
|
+
- spec/rect_spec.rb
|
67
|
+
- spec/spec_helper.rb
|
68
|
+
- spec/text_spec.rb
|
69
|
+
- README
|
70
|
+
- Rakefile
|
71
|
+
has_rdoc: true
|
72
|
+
homepage: http://github.com/sandofsky/painter
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 0
|
88
|
+
version: "0"
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
hash: 3
|
95
|
+
segments:
|
96
|
+
- 0
|
97
|
+
version: "0"
|
98
|
+
requirements: []
|
99
|
+
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 1.3.7
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: A clean wrapper around the GD2 graphics library, based on gd2-ffi
|
105
|
+
test_files:
|
106
|
+
- spec/color_spec.rb
|
107
|
+
- spec/image_spec.rb
|
108
|
+
- spec/image_spec.rb
|
109
|
+
- spec/rect_spec.rb
|
110
|
+
- spec/text_spec.rb
|
111
|
+
- spec/spec_helper.rb
|