gd2-ffij 0.3.0 → 0.4.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.
- 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
|