gd2-ffij 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +1 -1
- data/COPYRIGHT +1 -1
- data/lib/gd2-ffij.rb +25 -7
- data/lib/gd2/animated_gif.rb +79 -0
- data/lib/gd2/image.rb +59 -33
- data/lib/gd2/version.rb +1 -1
- data/test/animated_gif_tests.rb +42 -0
- data/test/image_tests.rb +11 -0
- data/test/images/test_animated_gif.gif +0 -0
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 052cd08377d7f0f72c6c17f29430aacc3abef04cd6e9544ed6303b6dd9f4ed0d
|
4
|
+
data.tar.gz: a00523e57b7df91beb60ed248c6b9f8a33447fef6c0eeb41bbbbcb9b5aca5d18
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4354f71430291fd6c4b3a986108561d37be95f409dcaa8062f1b0f5b46b19d24ff9d22654210e7b6758a9b7c5db7d22f1ed9b93d06d15863b73b626842a5005f
|
7
|
+
data.tar.gz: f741e53c0661bb400b21197a7b08ab04cd94b595cca2b83d5bb7d010f4deb1c2891b8193d5f2b199ba37f6377c5a81ec16e3ff362c52a429da5b64be0a98369c
|
data/.travis.yml
CHANGED
data/COPYRIGHT
CHANGED
@@ -16,7 +16,7 @@ See https://github.com/dark-panda/gd2-ffij/issues/10 ("Consider moving back to
|
|
16
16
|
BSD now that FFI is back to BSD?") for details.
|
17
17
|
|
18
18
|
|
19
|
-
Subsequent code added and modified to create gd2-ffij copyright (C) 2010-
|
19
|
+
Subsequent code added and modified to create gd2-ffij copyright (C) 2010-2019
|
20
20
|
J Smith <dark.panda@gmail.com> and published under the MIT license as follows:
|
21
21
|
|
22
22
|
Permission is hereby granted, free of charge, to any person
|
data/lib/gd2-ffij.rb
CHANGED
@@ -59,6 +59,7 @@ module GD2
|
|
59
59
|
:gdImageCreateFromGd2Part => [ :pointer, :pointer, :int, :int, :int, :int ],
|
60
60
|
:gdImageCreateFromXbm => [ :pointer, :pointer ],
|
61
61
|
:gdImageCreateFromXpm => [ :pointer, :pointer ],
|
62
|
+
:gdImagePaletteCopy => [ :void, :pointer, :pointer ],
|
62
63
|
:gdImageCompare => [ :int, :pointer, :pointer ],
|
63
64
|
:gdImageJpeg => [ :void, :pointer, :pointer, :int ],
|
64
65
|
:gdImageJpegPtr => [ :pointer, :pointer, :pointer, :int ],
|
@@ -105,6 +106,7 @@ module GD2
|
|
105
106
|
:gdImageColorExactAlpha => [ :int, :pointer, :int, :int, :int, :int ],
|
106
107
|
:gdImageColorClosestAlpha => [ :int, :pointer, :int, :int, :int, :int ],
|
107
108
|
:gdImageColorClosestHWB => [ :int, :pointer, :int, :int, :int ],
|
109
|
+
:gdImageColorAllocate => [ :int, :pointer, :int, :int, :int ],
|
108
110
|
:gdImageColorAllocateAlpha => [ :int, :pointer, :int, :int, :int, :int ],
|
109
111
|
:gdImageColorDeallocate => [ :void, :pointer, :int ],
|
110
112
|
:gdAlphaBlend => [ :int, :int, :int ],
|
@@ -122,6 +124,12 @@ module GD2
|
|
122
124
|
:gdImageStringUp => [ :void, :pointer, :pointer, :int, :int, :pointer, :int ],
|
123
125
|
:gdImageStringFTEx => [ :pointer, :pointer, :pointer, :int, :pointer, :double, :double, :int, :int, :pointer, :pointer ],
|
124
126
|
:gdImageStringFTCircle => [ :pointer, :pointer, :int, :int, :double, :double, :double, :pointer, :double, :pointer, :pointer, :int ],
|
127
|
+
:gdImageGifAnimBeginPtr => [ :pointer, :pointer, :pointer, :int, :int ],
|
128
|
+
:gdImageGifAnimBegin => [ :void, :pointer, :pointer, :int, :int ],
|
129
|
+
:gdImageGifAnimAddPtr => [ :pointer, :pointer, :pointer, :int, :int, :int, :int, :int, :pointer ],
|
130
|
+
:gdImageGifAnimAdd => [ :void, :pointer, :pointer, :int, :int, :int, :int, :int, :pointer ],
|
131
|
+
:gdImageGifAnimEnd => [ :void, :pointer ],
|
132
|
+
:gdImageGifAnimEndPtr => [ :pointer, :pointer ],
|
125
133
|
:gdFontGetSmall => [ :pointer ],
|
126
134
|
:gdFontGetLarge => [ :pointer ],
|
127
135
|
:gdFontGetMediumBold => [ :pointer ],
|
@@ -179,14 +187,24 @@ module GD2
|
|
179
187
|
ALPHA_TRANSPARENT = 127
|
180
188
|
|
181
189
|
class LibraryError < StandardError; end
|
182
|
-
end
|
183
190
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
191
|
+
GD2_BASE = File.join(File.dirname(__FILE__), 'gd2')
|
192
|
+
|
193
|
+
autoload :Image,
|
194
|
+
File.join(GD2_BASE, 'image')
|
195
|
+
autoload :Color,
|
196
|
+
File.join(GD2_BASE, 'color')
|
197
|
+
autoload :Palette,
|
198
|
+
File.join(GD2_BASE, 'palette')
|
199
|
+
autoload :Canvas,
|
200
|
+
File.join(GD2_BASE, 'canvas')
|
201
|
+
autoload :Font,
|
202
|
+
File.join(GD2_BASE, 'font')
|
203
|
+
autoload :FFIStruct,
|
204
|
+
File.join(GD2_BASE, 'ffi_struct')
|
205
|
+
autoload :AnimatedGif,
|
206
|
+
File.join(GD2_BASE, 'animated_gif')
|
207
|
+
end
|
190
208
|
|
191
209
|
class Numeric
|
192
210
|
if not self.instance_methods.include? 'degrees'
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true; encoding: ASCII-8BIT
|
2
|
+
#
|
3
|
+
# See COPYRIGHT for license details.
|
4
|
+
|
5
|
+
class GD2::AnimatedGif
|
6
|
+
class Frame < Struct.new(:ptr, :size)
|
7
|
+
def read
|
8
|
+
ptr.get_bytes(0, size.get_int(0))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(ptr, options = {})
|
13
|
+
size = FFI::MemoryPointer.new(:int)
|
14
|
+
|
15
|
+
frame_ptr = if frames.empty?
|
16
|
+
::GD2::GD2FFI.send(:gdImageGifAnimBeginPtr, ptr.image_ptr, size, -1, 0)
|
17
|
+
else
|
18
|
+
previous_frame_ptr = if options[:previous_frame]
|
19
|
+
options[:previous_frame].image_ptr
|
20
|
+
else
|
21
|
+
FFI::Pointer::NULL
|
22
|
+
end
|
23
|
+
|
24
|
+
::GD2::GD2FFI.send(:gdImageGifAnimAddPtr, ptr.image_ptr, size, 0, 0, 0, options[:delay].to_i, 1, previous_frame_ptr)
|
25
|
+
end
|
26
|
+
|
27
|
+
raise LibraryError if frame_ptr.null?
|
28
|
+
|
29
|
+
frames << Frame.new(frame_ptr, size)
|
30
|
+
end
|
31
|
+
|
32
|
+
def end
|
33
|
+
size = FFI::MemoryPointer.new(:int)
|
34
|
+
|
35
|
+
frame_ptr = ::GD2::GD2FFI.send(:gdImageGifAnimEndPtr, size)
|
36
|
+
|
37
|
+
raise LibraryError if frame_ptr.null?
|
38
|
+
|
39
|
+
frames << Frame.new(frame_ptr, size)
|
40
|
+
end
|
41
|
+
|
42
|
+
def export(filename_or_io)
|
43
|
+
output = case filename_or_io
|
44
|
+
when String
|
45
|
+
File.open(filename_or_io, 'wb')
|
46
|
+
else
|
47
|
+
filename_or_io
|
48
|
+
end
|
49
|
+
|
50
|
+
frames.each do |frame|
|
51
|
+
output.write(frame.read)
|
52
|
+
end
|
53
|
+
|
54
|
+
output.flush
|
55
|
+
output.rewind
|
56
|
+
|
57
|
+
output
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def frames
|
63
|
+
return @frames if defined?(@frames)
|
64
|
+
|
65
|
+
@frames = []
|
66
|
+
|
67
|
+
ObjectSpace.define_finalizer(@frames, frames_finalizer)
|
68
|
+
|
69
|
+
@frames
|
70
|
+
end
|
71
|
+
|
72
|
+
def frames_finalizer
|
73
|
+
proc do
|
74
|
+
each do |frame|
|
75
|
+
::GD2::GD2FFI.send(:gdFree, frame[:frame_ptr])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/gd2/image.rb
CHANGED
@@ -126,10 +126,31 @@ module GD2
|
|
126
126
|
:wbmp
|
127
127
|
when /^gd2/
|
128
128
|
:gd2
|
129
|
+
when /^\xff\xff/
|
130
|
+
:gd
|
129
131
|
end
|
130
132
|
end
|
131
133
|
private_class_method :data_type
|
132
134
|
|
135
|
+
def self.extract_format(filename_or_io, options)
|
136
|
+
format = options[:format]
|
137
|
+
|
138
|
+
if !format
|
139
|
+
case filename_or_io
|
140
|
+
when String
|
141
|
+
md = filename_or_io.match(/\.([^.]+)\z/)
|
142
|
+
format = md ? md[1].downcase : nil
|
143
|
+
else
|
144
|
+
magic = filename_or_io.read(4)
|
145
|
+
filename_or_io.seek(-magic.bytes.length, IO::SEEK_CUR)
|
146
|
+
format = data_type(magic.strip)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
format = format.to_sym if format
|
151
|
+
format
|
152
|
+
end
|
153
|
+
|
133
154
|
# Import an image from a file with the given +filename+. The :format option
|
134
155
|
# or the file extension is used to determine the image type (jpeg, png,
|
135
156
|
# gif, wbmp, gd, gd2, xbm, or xpm). The resulting image will be either of
|
@@ -138,21 +159,17 @@ module GD2
|
|
138
159
|
# If the file format is gd2, it is optionally possible to extract only a
|
139
160
|
# part of the image. Use options :x, :y, :width, and :height to specify the
|
140
161
|
# part of the image to import.
|
141
|
-
def self.import(
|
142
|
-
|
143
|
-
|
144
|
-
unless format = options.delete(:format)
|
145
|
-
md = filename.match(/\.([^.]+)\z/)
|
146
|
-
format = md ? md[1].downcase : nil
|
147
|
-
end
|
148
|
-
format = format.to_sym if format
|
162
|
+
def self.import(filename_or_io, options = {})
|
163
|
+
format = extract_format(filename_or_io, options)
|
149
164
|
|
150
165
|
ptr = # TODO: implement xpm and xbm imports
|
166
|
+
|
151
167
|
#if format == :xpm
|
152
168
|
#raise ArgumentError, "Unexpected options #{options.inspect}" unless options.empty?
|
153
169
|
#::GD2::GD2FFI.send(:gdImageCreateFromXpm, filename)
|
154
170
|
#elsif format == :xbm
|
155
171
|
#::GD2::GD2FFI.send(:gdImageCreateFromXbm, filename)
|
172
|
+
|
156
173
|
if format == :gd2 && !options.empty?
|
157
174
|
x, y, width, height =
|
158
175
|
options.delete(:x) || 0, options.delete(:y) || 0,
|
@@ -181,12 +198,18 @@ module GD2
|
|
181
198
|
raise UnrecognizedImageTypeError,
|
182
199
|
'Format (or file extension) is not recognized' unless create_sym
|
183
200
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
201
|
+
output = case filename_or_io
|
202
|
+
when String
|
203
|
+
File.open(filename_or_io, 'rb').read
|
204
|
+
else
|
205
|
+
filename_or_io.read
|
206
|
+
end
|
207
|
+
|
208
|
+
output = output.force_encoding("ASCII-8BIT") if output.respond_to? :force_encoding
|
209
|
+
file_ptr = FFI::MemoryPointer.new(output.size, 1, false)
|
210
|
+
file_ptr.put_bytes(0, output)
|
188
211
|
|
189
|
-
::GD2::GD2FFI.send(create_sym,
|
212
|
+
::GD2::GD2FFI.send(create_sym, output.size, file_ptr)
|
190
213
|
end
|
191
214
|
raise LibraryError if ptr.null?
|
192
215
|
|
@@ -425,17 +448,13 @@ module GD2
|
|
425
448
|
# implemented by subclass
|
426
449
|
end
|
427
450
|
|
428
|
-
# Export this image to a file with the given +filename+. The
|
429
|
-
# is determined by the :format option, or by the file
|
430
|
-
# gif, wbmp, gd, or gd2). Returns the size of the
|
431
|
-
# Additional +options+ are as arguments for the
|
432
|
-
# Image#wbmp, or Image#gd2 methods.
|
433
|
-
def export(
|
434
|
-
|
435
|
-
md = filename.match(/\.([^.]+)\z/)
|
436
|
-
format = md ? md[1].downcase : nil
|
437
|
-
end
|
438
|
-
format = format.to_sym if format
|
451
|
+
# Export this image to a file or stream with the given +filename+. The
|
452
|
+
# image format is determined by the :format option, or by the file
|
453
|
+
# extension (jpeg, png, gif, wbmp, gd, or gd2). Returns the size of the
|
454
|
+
# written image data. Additional +options+ are as arguments for the
|
455
|
+
# Image#jpeg, Image#png, Image#wbmp, or Image#gd2 methods.
|
456
|
+
def export(filename_or_io, options = {})
|
457
|
+
format = self.class.extract_format(filename_or_io, options)
|
439
458
|
|
440
459
|
size = FFI::MemoryPointer.new(:pointer)
|
441
460
|
|
@@ -465,17 +484,24 @@ module GD2
|
|
465
484
|
'Format (or file extension) is not recognized'
|
466
485
|
end
|
467
486
|
|
468
|
-
|
469
|
-
|
487
|
+
output = case filename_or_io
|
488
|
+
when String
|
489
|
+
File.open(filename_or_io, 'wb')
|
490
|
+
else
|
491
|
+
filename_or_io
|
492
|
+
end
|
470
493
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
::GD2::GD2FFI.gdFree(img)
|
477
|
-
end
|
494
|
+
begin
|
495
|
+
img = ::GD2::GD2FFI.send(write_sym, image_ptr, *args)
|
496
|
+
output.write(img.get_bytes(0, size.get_int(0)))
|
497
|
+
ensure
|
498
|
+
::GD2::GD2FFI.gdFree(img)
|
478
499
|
end
|
500
|
+
|
501
|
+
output.flush
|
502
|
+
output.rewind
|
503
|
+
|
504
|
+
output
|
479
505
|
end
|
480
506
|
|
481
507
|
# Encode and return data for this image in JPEG format. The +quality+
|
data/lib/gd2/version.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true; encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require './test/test_helper'
|
4
|
+
|
5
|
+
class AnimatedGifTest < Minitest::Test
|
6
|
+
include TestHelper
|
7
|
+
|
8
|
+
def test_animation
|
9
|
+
image1 = GD2::Image::IndexedColor.new(256, 256)
|
10
|
+
|
11
|
+
black = image1.palette.allocate(GD2::Color[0, 0, 0])
|
12
|
+
white = image1.palette.allocate(GD2::Color[255, 255, 255])
|
13
|
+
lime = image1.palette.allocate(GD2::Color[0, 255, 0])
|
14
|
+
|
15
|
+
image1.draw do |pen|
|
16
|
+
pen.color = white
|
17
|
+
pen.line(64, 64, 192, 192)
|
18
|
+
end
|
19
|
+
|
20
|
+
image2 = GD2::Image::IndexedColor.new(256, 256)
|
21
|
+
black = image2.palette.allocate(GD2::Color[0, 0, 0])
|
22
|
+
white = image2.palette.allocate(GD2::Color[255, 255, 255])
|
23
|
+
lime = image2.palette.allocate(GD2::Color[0, 255, 0])
|
24
|
+
|
25
|
+
image2.draw do |pen|
|
26
|
+
pen.color = lime
|
27
|
+
pen.rectangle(32, 64, 192, 192)
|
28
|
+
end
|
29
|
+
|
30
|
+
anim = GD2::AnimatedGif.new
|
31
|
+
anim.add(image1)
|
32
|
+
anim.add(image2, delay: 50)
|
33
|
+
anim.add(image1, delay: 50)
|
34
|
+
anim.end
|
35
|
+
|
36
|
+
output = StringIO.new
|
37
|
+
|
38
|
+
anim.export(output)
|
39
|
+
|
40
|
+
assert(output.read == File.read('test/images/test_animated_gif.gif'))
|
41
|
+
end
|
42
|
+
end
|
data/test/image_tests.rb
CHANGED
@@ -77,6 +77,17 @@ class ImageTest < Minitest::Test
|
|
77
77
|
File.unlink(out)
|
78
78
|
end
|
79
79
|
|
80
|
+
def test_export_and_import_#{ext}_io
|
81
|
+
img = GD2::Image.import(File.join(PATH_TO_IMAGES, 'test.gd2'))
|
82
|
+
out = StringIO.new
|
83
|
+
img.export(out, format: :#{ext})
|
84
|
+
|
85
|
+
imgA = GD2::Image.import(out)
|
86
|
+
imgB = GD2::Image.import(File.join(PATH_TO_IMAGES, 'test.#{ext}'))
|
87
|
+
|
88
|
+
assert(imgA == imgB)
|
89
|
+
end
|
90
|
+
|
80
91
|
def test_compare_#{ext}
|
81
92
|
imgA = GD2::Image.import(File.join(PATH_TO_IMAGES, 'test.#{ext}'))
|
82
93
|
imgB = GD2::Image.import(File.join(PATH_TO_IMAGES, 'test.#{ext}'))
|
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gd2-ffij
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- J Smith
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -40,6 +40,7 @@ files:
|
|
40
40
|
- Rakefile
|
41
41
|
- gd2-ffij.gemspec
|
42
42
|
- lib/gd2-ffij.rb
|
43
|
+
- lib/gd2/animated_gif.rb
|
43
44
|
- lib/gd2/canvas.rb
|
44
45
|
- lib/gd2/color.rb
|
45
46
|
- lib/gd2/ffi_struct.rb
|
@@ -47,6 +48,7 @@ files:
|
|
47
48
|
- lib/gd2/image.rb
|
48
49
|
- lib/gd2/palette.rb
|
49
50
|
- lib/gd2/version.rb
|
51
|
+
- test/animated_gif_tests.rb
|
50
52
|
- test/canvas_tests.rb
|
51
53
|
- test/image_tests.rb
|
52
54
|
- test/images/test.bmp
|
@@ -59,6 +61,7 @@ files:
|
|
59
61
|
- test/images/test.xbm
|
60
62
|
- test/images/test.xcf
|
61
63
|
- test/images/test.xpm
|
64
|
+
- test/images/test_animated_gif.gif
|
62
65
|
- test/images/test_arc.gd2
|
63
66
|
- test/images/test_canvas_filled_polygon.gd2
|
64
67
|
- test/images/test_canvas_filled_rectangle.gd2
|
@@ -108,12 +111,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
111
|
- !ruby/object:Gem::Version
|
109
112
|
version: '0'
|
110
113
|
requirements: []
|
111
|
-
|
112
|
-
rubygems_version: 2.6.13
|
114
|
+
rubygems_version: 3.0.3
|
113
115
|
signing_key:
|
114
116
|
specification_version: 4
|
115
117
|
summary: gd2-ffij is a refactoring of the Ruby/GD2 library implemented with FFI
|
116
118
|
test_files:
|
119
|
+
- test/animated_gif_tests.rb
|
117
120
|
- test/canvas_tests.rb
|
118
121
|
- test/image_tests.rb
|
119
122
|
- test/images/test.bmp
|
@@ -126,6 +129,7 @@ test_files:
|
|
126
129
|
- test/images/test.xbm
|
127
130
|
- test/images/test.xcf
|
128
131
|
- test/images/test.xpm
|
132
|
+
- test/images/test_animated_gif.gif
|
129
133
|
- test/images/test_arc.gd2
|
130
134
|
- test/images/test_canvas_filled_polygon.gd2
|
131
135
|
- test/images/test_canvas_filled_rectangle.gd2
|