rmagick 7.1.6 → 7.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e308c3194e4a4a558c205843e22e8aa4a00435d18945b7d978e9c2a8a0d513c0
4
- data.tar.gz: e1d208d02935f3fd02cd20cf7331806820b42729898e9a6117ec1fd19fb625e8
3
+ metadata.gz: 765ab71db74f8e2497d857f4041b604abbf848bdcc49d91ae9eb83c13c8dc130
4
+ data.tar.gz: 1466fca25cf24eb504a197f5db46f1a2ae36e5750d4bc82b2e08836efc6fc3b1
5
5
  SHA512:
6
- metadata.gz: 8e9921e35f07f25cf2341dfac5d386362be9991ba52fc587a4556692d1fd32f86d02138a2338919a753a90c49450a18af708cb937058ae4db47580528882482d
7
- data.tar.gz: 2577bf2c6006d4322c0c5a304ea0fcc7b34c1bafcbcba16194d75417acc863c2cdb2ef0140230b7e756c948016477e1ca1c7c96c33fc94d45b76066785f965e0
6
+ metadata.gz: e5f6bc440331557b902c94cf9665f572bb50f48242f1760c5cec131d9b86af2d6a92592d43077429ad90cd11171000be3e8c9ba9fb848ed11be515a431e1ee3b
7
+ data.tar.gz: e8cc721b925f6ff4217870ad1234b7e2eb59573a3e951056bf494d50c9855230e80f4564270e0aea067782adacdb3d202b91cbafd8318fff711919c4138751b8
data/CHANGELOG.md CHANGED
@@ -3,6 +3,52 @@
3
3
  All notable changes to this project are documented in this file.
4
4
  This project adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
+ ## RMagick 7.2.0
7
+
8
+ > [!IMPORTANT]
9
+ > Heavy calls can run on a Fiber scheduler's worker pool, `Image#fx` rejects an expression that names a file, and some results change with ImageMagick 7.
10
+ >
11
+ > On Ruby 4.0 and later, a Fiber scheduler with a worker pool, such as `Async::Scheduler.new(worker_pool: IO::Event::WorkerPool.new)`, now runs heavy ImageMagick calls on its workers, and the other fibers keep running. While such a call uses an image, using that image from another fiber raises `RuntimeError` ("object is in use by another fiber") instead of waiting. `Thread#raise`, `Timeout` and Ctrl-C now reach a thread after the ImageMagick call returns, with or without a scheduler. See "Threads, fibers and fork" in README for the rules.
12
+ >
13
+ > `Image#fx` now raises `ArgumentError` for an expression that starts with `@`, or that has a `%[fx:@...]`, `%[hex:@...]` or `%[pixel:@...]` escape. ImageMagick read the file it names, so an expression taken from user input could read any file the process can open. The message is "the expression must not name a file with '@'", as for the other values that reject `@`.
14
+ >
15
+ > With ImageMagick 7, `Pixel.new(red, green, blue, opacity)` now takes the fourth argument as opacity, as with ImageMagick 6, so `Pixel.new(0, 0, 0, 0)` is opaque. `Image#to_color` now uses the depth and alpha channel of the image, as with ImageMagick 6, so it returns `"#D8936A"` instead of `"#D8D893936A6A"` for an 8-bit image without alpha.
16
+
17
+ Breaking Changes
18
+
19
+ * Reject an Image#fx expression beginning with '@' instead of reading that file (#1919)
20
+ * Fix Pixel.new treating the fourth argument as alpha instead of opacity with ImageMagick 7 (#1907)
21
+ * Fix Image#to_color ignoring the image depth and alpha channel with ImageMagick 7 (#1908)
22
+
23
+ Improvements
24
+
25
+ * Improve performance by running ImageMagick calls on a Fiber scheduler's worker pool (#1921)
26
+ * Describe how to use RMagick with threads, fibers and fork in README (#1932)
27
+ * Fix the API reference pages that disagree with the code (#1903-#1906)
28
+
29
+ Bug Fixes
30
+
31
+ * Fix a crash when an image is destroyed in the block of Image#polaroid and similar methods (#1933)
32
+ * Fix an image staying "in use by another fiber" in the child of a fork (#1931)
33
+ * Fix Image#write and other calls doing nothing right after an Async task is stopped (#1930)
34
+ * Fix memory leak when Thread#raise or Timeout interrupts an image operation (#1926)
35
+ * Fix wrong pixels when two calls read the same image at once (#1925)
36
+ * Fix Image#to_color returning the wrong color for a CMYK image (#1920)
37
+ * Fix Image#pixel_color crashing for a coordinate whose pixel offset overflows (#1918)
38
+ * Fix Image.read_inline crashing when ImageMagick returns no image (#1915)
39
+ * Fix pixel_color and get_pixels returning a huge value for a negative HDRI pixel with ImageMagick 7 (#1914)
40
+ * Fix Image.constitute accepting a pixel array whose size overflows (#1913)
41
+ * Fix Image.constitute reading freed memory and leaking on a bad pixel value (#1912)
42
+ * Fix out-of-range values wrapping around in Pixel and the alpha keyword (#1911)
43
+ * Fix highlight_color and lowlight_color having no effect in compare_channel (#1910)
44
+ * Fix Pixel#to_color rejecting depth 16, 32 and 64 with a 64-bit quantum depth (#1909)
45
+
46
+ ## RMagick 7.1.7
47
+
48
+ Bug Fixes
49
+
50
+ * Fix installing the gem with the system Ruby on Ubuntu 26.04, whose LDFLAGS contain '%' (#1927)
51
+
6
52
  ## RMagick 7.1.6
7
53
 
8
54
  > [!IMPORTANT]
data/README.md CHANGED
@@ -192,6 +192,17 @@ require 'rmagick'
192
192
 
193
193
  See <https://rmagick.github.io/usage.html> for links to more information.
194
194
 
195
+ ### Threads, fibers and fork
196
+
197
+ RMagick releases the GVL while ImageMagick works, so calls on different images run in parallel.
198
+
199
+ - Use each image from one thread at a time. To work in parallel, give each thread its own images.
200
+ - On Ruby 4.0 and later, a Fiber scheduler with a worker pool, such as `Async::Scheduler.new(worker_pool: IO::Event::WorkerPool.new)`, runs heavy calls on its workers, and the other fibers keep running. While such a call uses an image, using that image from another fiber raises `RuntimeError` ("object is in use by another fiber").
201
+ - Each call in flight holds its images in memory, so memory use grows with the number of workers. Limit the calls that run at once, for example with `Async::Semaphore`.
202
+ - `Thread#raise`, `Timeout` and Ctrl-C reach a thread after the ImageMagick call returns. An exception raised in a `trap` handler can unwind through ImageMagick instead.
203
+ - Do not fork while other threads or workers are running ImageMagick. The child can hang on a lock that they held.
204
+ - After ImageMagick has used OpenMP, a call that uses OpenMP in a forked child can hang. A process that forks after using RMagick should set `MAGICK_THREAD_LIMIT=1`.
205
+
195
206
  Things that can go wrong
196
207
  ------------------------
197
208
 
data/Rakefile CHANGED
@@ -191,6 +191,7 @@ if RUBY_PLATFORM.include?('linux')
191
191
 
192
192
  RubyMemcheck.config(
193
193
  binary_name: 'RMagick2',
194
+ valgrind_options: RubyMemcheck::Configuration::DEFAULT_VALGRIND_OPTIONS + ['--max-stackframe=65536'],
194
195
  valgrind_suppressions_dir: 'spec/valgrind'
195
196
  )
196
197
 
@@ -81,7 +81,7 @@ module RMagick
81
81
  end
82
82
 
83
83
  def configure_headers
84
- @headers = %w[assert.h ctype.h stdio.h stdlib.h math.h time.h sys/types.h ruby.h ruby/io.h]
84
+ @headers = %w[assert.h ctype.h stdio.h stdlib.h math.h time.h sys/types.h ruby.h ruby/io.h ruby/fiber/scheduler.h]
85
85
 
86
86
  if have_header('MagickCore/MagickCore.h')
87
87
  headers << 'MagickCore/MagickCore.h'
@@ -134,7 +134,7 @@ module RMagick
134
134
  def exit_failure(msg)
135
135
  msg = "ERROR: #{msg}"
136
136
 
137
- Logging.message msg
137
+ Logging.message "%s", msg
138
138
 
139
139
  @stdout.puts "\n\n"
140
140
  if ENV['NO_COLOR']
@@ -172,11 +172,11 @@ module RMagick
172
172
  return if packages.empty?
173
173
 
174
174
  msg = "\nDetected ImageMagick packages:\n"
175
- Logging.message msg
176
- message msg
175
+ Logging.message "%s", msg
176
+ message "%s", msg
177
177
  package_paths = packages.map { |package| "- #{PKGConfig.package_config(package).pc_path}" }.join("\n")
178
- Logging.message package_paths + "\n\n"
179
- message package_paths + "\n\n"
178
+ Logging.message "%s", package_paths + "\n\n"
179
+ message "%s", package_paths + "\n\n"
180
180
 
181
181
  if installed_im6_packages.any? && installed_im7_packages.any?
182
182
  checking_for('forced use of ImageMagick 6') do
@@ -192,8 +192,8 @@ module RMagick
192
192
 
193
193
  if packages.length > 1
194
194
  msg = "\nWarning: Found more than one ImageMagick installation. This could cause problems at runtime.\n\n"
195
- Logging.message msg
196
- message msg
195
+ Logging.message "%s", msg
196
+ message "%s", msg
197
197
  end
198
198
 
199
199
  packages.first
@@ -201,8 +201,8 @@ module RMagick
201
201
 
202
202
  def search_paths_for_windows
203
203
  msg = 'searching PATH for the ImageMagick library...'
204
- Logging.message msg
205
- message msg + "\n"
204
+ Logging.message "%s", msg
205
+ message "%s", msg + "\n"
206
206
 
207
207
  found = false
208
208
  dir_paths = {}
@@ -261,7 +261,7 @@ module RMagick
261
261
  # Ensure minimum ImageMagick version
262
262
  # Check minimum ImageMagick version if possible
263
263
  checking_for("outdated ImageMagick version") do
264
- Logging.message("Detected ImageMagick version: #{$magick_version}\n")
264
+ Logging.message("%s", "Detected ImageMagick version: #{$magick_version}\n")
265
265
 
266
266
  required_version = im_version_at_least?('7.0.0') ? Magick::MIN_IM7_VERSION : Magick::MIN_IM6_VERSION
267
267
 
@@ -275,7 +275,8 @@ module RMagick
275
275
 
276
276
  def create_header_file
277
277
  ruby_api = [
278
- 'rb_io_path' # Ruby 3.3.0
278
+ 'rb_io_path', # Ruby 3.3.0
279
+ 'rb_fiber_scheduler_blocking_operation_extract' # Ruby 4.0.0
279
280
  ]
280
281
  memory_api = %w[
281
282
  posix_memalign
@@ -354,8 +355,8 @@ module RMagick
354
355
 
355
356
  END_SUMMARY
356
357
 
357
- Logging.message summary
358
- message summary
358
+ Logging.message "%s", summary
359
+ message "%s", summary
359
360
  end
360
361
  end
361
362
  end
@@ -363,8 +364,8 @@ end
363
364
  extconf = RMagick::Extconf.new
364
365
  at_exit do
365
366
  msg = "Configured compile options: #{extconf.configured_compile_options}"
366
- Logging.message msg
367
- message msg + "\n"
367
+ Logging.message "%s", msg
368
+ message "%s", msg + "\n"
368
369
  end
369
370
  extconf.create_makefile_file
370
371
  extconf.create_compile_flags_txt
@@ -104,7 +104,7 @@ extern "C" {
104
104
 
105
105
  // Handle Quantum <-> Ruby Numeric object conversion
106
106
  #if (MAGICKCORE_QUANTUM_DEPTH == 8 || MAGICKCORE_QUANTUM_DEPTH == 16)
107
- #define QUANTUM2NUM(q) INT2FIX((q)) /**< Quantum -> Ruby Numeric conversion */
107
+ #define QUANTUM2NUM(q) INT2NUM((q)) /**< Quantum -> Ruby Numeric conversion */
108
108
  #define NUM2QUANTUM(n) (Quantum)NUM2UINT((n)) /**< Quantum <- Ruby Numeric conversion */
109
109
  #elif (MAGICKCORE_QUANTUM_DEPTH == 32)
110
110
  #define QUANTUM2NUM(q) UINT2NUM((q)) /**< Quantum -> Ruby Numeric conversion */
@@ -443,7 +443,7 @@ extern const rb_data_type_t rm_kernel_info_data_type;
443
443
  */
444
444
  #define C_boolean_to_R_boolean(attr) (attr) ? Qtrue : Qfalse /**< C boolean -> Ruby boolean */
445
445
  #define R_boolean_to_C_boolean(attr) (MagickBooleanType)RTEST(attr) /**< C boolean <- Ruby boolean */
446
- #define C_int_to_R_int(attr) INT2FIX(attr) /**< C int -> Ruby int */
446
+ #define C_int_to_R_int(attr) INT2NUM(attr) /**< C int -> Ruby int */
447
447
  #define R_int_to_C_int(attr) NUM2INT(attr) /**< C int <- Ruby int */
448
448
  #define C_long_to_R_long(attr) LONG2NUM(attr) /**< C long -> Ruby long */
449
449
  #define R_long_to_C_long(attr) NUM2LONG(attr) /**< C long <- Ruby long */
@@ -458,7 +458,7 @@ extern const rb_data_type_t rm_kernel_info_data_type;
458
458
  {\
459
459
  klass *ptr;\
460
460
  if (rb_obj_is_kind_of(self, Class_Image) == Qtrue) {\
461
- rm_check_destroyed(self); \
461
+ rm_check_readable(self); \
462
462
  }\
463
463
  TypedData_Get_Struct(self, klass, data_type, ptr);\
464
464
  return C_##type##_to_R_##type(ptr->attr);\
@@ -468,7 +468,7 @@ extern const rb_data_type_t rm_kernel_info_data_type;
468
468
  #define IMPLEMENT_TYPED_ATTR_READERF(klass, attr, field, type, data_type) \
469
469
  {\
470
470
  klass *ptr;\
471
- rm_check_destroyed(self); \
471
+ rm_check_readable(self); \
472
472
  TypedData_Get_Struct(self, klass, data_type, ptr);\
473
473
  return C_##type##_to_R_##type(ptr->field);\
474
474
  }
@@ -478,10 +478,11 @@ extern const rb_data_type_t rm_kernel_info_data_type;
478
478
  {\
479
479
  klass *ptr;\
480
480
  if (rb_obj_is_kind_of(self, Class_Image) == Qtrue) {\
481
- rm_check_destroyed(self); \
481
+ rm_check_readable(self); \
482
482
  }\
483
483
  rb_check_frozen(self);\
484
484
  TypedData_Get_Struct(self, klass, data_type, ptr);\
485
+ rm_gvl_check_writable(ptr);\
485
486
  ptr->attr = R_##type##_to_C_##type(val);\
486
487
  return val;\
487
488
  }
@@ -491,10 +492,11 @@ extern const rb_data_type_t rm_kernel_info_data_type;
491
492
  {\
492
493
  klass *ptr;\
493
494
  if (rb_obj_is_kind_of(self, Class_Image) == Qtrue) {\
494
- rm_check_destroyed(self); \
495
+ rm_check_readable(self); \
495
496
  }\
496
497
  rb_check_frozen(self);\
497
498
  TypedData_Get_Struct(self, klass, data_type, ptr);\
499
+ rm_gvl_check_writable(ptr);\
498
500
  ptr->field = R_##type##_to_C_##type(val);\
499
501
  return self;\
500
502
  }
@@ -1157,7 +1159,8 @@ extern size_t rm_strnlen_s(const char *, size_t);
1157
1159
  extern bool rm_has_file_reference(const char *);
1158
1160
  extern void rm_check_ary_len(VALUE, long);
1159
1161
  extern VALUE rm_check_ary_type(VALUE ary);
1160
- extern Image *rm_check_destroyed(VALUE);
1162
+ extern Image *rm_check_readable(VALUE);
1163
+ extern Image *rm_check_writable(VALUE);
1161
1164
  extern Image *rm_check_frozen(VALUE);
1162
1165
  extern char *rm_str2cstr(VALUE *, size_t *);
1163
1166
  extern char *rm_path2cstr(VALUE *, size_t *);
@@ -1215,4 +1218,6 @@ extern void rm_check_image_exception(Image *, ErrorRetention);
1215
1218
 
1216
1219
  } // extern "C"
1217
1220
 
1221
+ #include "rmagick_gvl.hpp"
1222
+
1218
1223
  #endif