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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6aa4ff4f4aa7e5c580dd3c9d464c8c1df57a98a5
4
- data.tar.gz: 211bf9eef3b61bffe85ec4a2e5a2a97affe26bad
2
+ SHA256:
3
+ metadata.gz: 052cd08377d7f0f72c6c17f29430aacc3abef04cd6e9544ed6303b6dd9f4ed0d
4
+ data.tar.gz: a00523e57b7df91beb60ed248c6b9f8a33447fef6c0eeb41bbbbcb9b5aca5d18
5
5
  SHA512:
6
- metadata.gz: b7ed5a027a534a36f2b144392aa8e1f1f56e2a3c2e230d31f4d31409353ab8d7a6d7a9846d12d024ccc9d9b892219e8a4e27a48d2a4b7cc641cdfcc3e7efde88
7
- data.tar.gz: 3b5973311c1080fe04273a0dd4923589f9192b1265fe3e5b2fe607acb648af87001fd7fc54cd72a35a02664a26c23c5db60595182d404f1aabbc9e254092a411
6
+ metadata.gz: 4354f71430291fd6c4b3a986108561d37be95f409dcaa8062f1b0f5b46b19d24ff9d22654210e7b6758a9b7c5db7d22f1ed9b93d06d15863b73b626842a5005f
7
+ data.tar.gz: f741e53c0661bb400b21197a7b08ab04cd94b595cca2b83d5bb7d010f4deb1c2891b8193d5f2b199ba37f6377c5a81ec16e3ff362c52a429da5b64be0a98369c
@@ -3,9 +3,9 @@ cache: bundler
3
3
  sudo: false
4
4
  language: ruby
5
5
  rvm:
6
+ - 2.5.1
6
7
  - 2.4.2
7
8
  - 2.3.5
8
- - 2.2.8
9
9
  - rbx-3
10
10
  - jruby-head
11
11
  matrix:
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-2017
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
@@ -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
- require 'gd2/image'
185
- require 'gd2/color'
186
- require 'gd2/palette'
187
- require 'gd2/canvas'
188
- require 'gd2/font'
189
- require 'gd2/ffi_struct'
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
@@ -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(filename, options = {})
142
- raise Errno::ENOENT.new(filename) unless File.exist?(filename)
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
- file = File.open(filename, 'rb').read
185
- file = file.force_encoding("ASCII-8BIT") if file.respond_to? :force_encoding
186
- file_ptr = FFI::MemoryPointer.new(file.size, 1, false)
187
- file_ptr.put_bytes(0, file)
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, file.size, file_ptr)
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 image format
429
- # is determined by the :format option, or by the file extension (jpeg, png,
430
- # gif, wbmp, gd, or gd2). Returns the size of the written image data.
431
- # Additional +options+ are as arguments for the Image#jpeg, Image#png,
432
- # Image#wbmp, or Image#gd2 methods.
433
- def export(filename, options = {})
434
- unless format = options.delete(:format)
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
- raise ArgumentError, "Unrecognized options #{options.inspect}" unless
469
- options.empty?
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
- File.open(filename, 'wb') do |file|
472
- begin
473
- img = ::GD2::GD2FFI.send(write_sym, image_ptr, *args)
474
- file.write(img.get_bytes(0, size.get_int(0)))
475
- ensure
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+
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true; encoding: ASCII-8BIT
2
2
 
3
3
  module GD2
4
- VERSION = '0.3.0'.freeze
4
+ VERSION = '0.4.0'.freeze
5
5
  end
6
6
 
@@ -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
@@ -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}'))
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.3.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: 2017-11-09 00:00:00.000000000 Z
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
- rubyforge_project:
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