painter 0.0.4
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.
- 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
|