morandi 0.12.1 → 0.99.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -2
- data/README.md +9 -21
- data/ext/gdk_pixbuf_cairo/extconf.rb +124 -0
- data/ext/gdk_pixbuf_cairo/gdk_pixbuf_cairo.c +260 -0
- data/ext/morandi_native/extconf.rb +121 -0
- data/ext/morandi_native/filter.h +229 -0
- data/ext/morandi_native/gamma.h +48 -0
- data/ext/morandi_native/mask.h +75 -0
- data/ext/morandi_native/morandi_native.c +1126 -0
- data/ext/morandi_native/rotate.h +79 -0
- data/ext/morandi_native/tint.h +72 -0
- data/lib/gdk_pixbuf_cairo.so +0 -0
- data/lib/morandi/cairo_ext.rb +56 -0
- data/lib/morandi/crop_utils.rb +95 -0
- data/lib/morandi/errors.rb +12 -0
- data/lib/morandi/image_operation.rb +31 -0
- data/lib/morandi/image_processor.rb +186 -180
- data/lib/morandi/operation/colourify.rb +45 -0
- data/lib/morandi/operation/image_border.rb +108 -0
- data/lib/morandi/operation/straighten.rb +41 -0
- data/lib/morandi/pixbuf_ext.rb +20 -0
- data/lib/morandi/profiled_pixbuf.rb +18 -71
- data/lib/morandi/redeye.rb +35 -47
- data/lib/morandi/srgb_conversion.rb +42 -0
- data/lib/morandi/version.rb +3 -1
- data/lib/morandi.rb +42 -12
- data/lib/morandi_native.so +0 -0
- metadata +57 -116
- data/.gitignore +0 -18
- data/.rspec +0 -3
- data/.ruby-version +0 -1
- data/Gemfile +0 -4
- data/Rakefile +0 -1
- data/lib/morandi/image-ops.rb +0 -307
- data/lib/morandi/utils.rb +0 -136
- data/morandi.gemspec +0 -33
- data/sample/sample.jpg +0 -0
- data/spec/morandi_spec.rb +0 -208
- data/spec/spec_helper.rb +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 72cf19456503d11e3729ece721a4f8cf542ec674d5c35497d53efc8d333ebbd4
|
4
|
+
data.tar.gz: 3d05af03a553609b7bb4901071b675273127a329b323fb45bd1d7c071fe8aed5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa60e3b5ccefa7a76fa8e4f70d28f3ef1ad80e81a8f1d07c35a258e6748e9a4ce4e5923f82be682456a74c5ca8acc185030a783f925246bf04fad5776d20aed1
|
7
|
+
data.tar.gz: 9bfff0edf6d1d52d2be6f836138b60d35c22a788fc337c46c7837a9b719f91ca6347ff746bd79f757737d6f6abba5615dd9f480e79ed07a8e9d67085cf6e3ad2
|
data/CHANGELOG.md
CHANGED
@@ -4,7 +4,44 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
-
##
|
7
|
+
## [0.99.4] 18.11.2024
|
8
|
+
### Added
|
9
|
+
- Better test coverage for straighten operation
|
10
|
+
- Support for visual image comparison in specs
|
11
|
+
- Automated visual comparison of images in specs, also serving as a record of exact rendering behaviour
|
12
|
+
- Development scripts for performing benchmarks
|
13
|
+
- Basic test coverage for transparency support
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
- Extracted image operations to separate files within a dedicated module
|
17
|
+
- [BREAKING] Introduced raising Morandi's own errors instead of bubbling Pixbuf's
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
- Updated required ruby version in gemspec to reflect dropping Ruby 2.3 support
|
21
|
+
|
22
|
+
### Removed
|
23
|
+
- [BREAKING] `config` accessor in ImageProcessor removed in favour of `user_options` supplied in constructor
|
24
|
+
|
25
|
+
## [0.99.03] 19.09.2024
|
26
|
+
### Added
|
27
|
+
- Copied pixbufutils and redeye gems into main gem
|
28
|
+
- Added gdk_pixbuf_cairo C extension to convert between GdkPixbufs and ImageSurfaces
|
29
|
+
- Added Ruby 3 support
|
30
|
+
- Bumped version to 0.99.01 in preparation for a 1.0 release
|
31
|
+
|
32
|
+
### Removed
|
33
|
+
- [BREAKING] support for Ruby 2.0 (and illusion of it being tested by CI)
|
34
|
+
- [BREAKING] support for Ruby 2.3
|
35
|
+
- gtk2 dependency
|
36
|
+
|
37
|
+
## [0.13.0] 16.12.2020
|
38
|
+
### Fixed
|
39
|
+
- Refactored test suite
|
40
|
+
- Fixed most rubocop offenses
|
41
|
+
### Added
|
42
|
+
- CI pipeline
|
43
|
+
- rubocop
|
44
|
+
- Development image
|
8
45
|
|
9
46
|
## [0.12.1] 10.12.2020
|
10
47
|
### Fixed
|
@@ -13,7 +50,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
13
50
|
## [0.12.0] 10.12.2020
|
14
51
|
### Fixed
|
15
52
|
- Compatability with gdk_pixbuf v3.4.0+ [TECH-14001]
|
16
|
-
###
|
53
|
+
### Added
|
17
54
|
- .ruby-version file
|
18
55
|
|
19
56
|
|
data/README.md
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
# Morandi
|
2
2
|
|
3
|
-
Library of simple image manipulations - replicating the behaviour of
|
4
|
-
morandi-js.
|
3
|
+
Library of simple image manipulations - replicating the behaviour of morandi-js.
|
5
4
|
|
6
5
|
## Installation
|
7
6
|
|
8
|
-
Install `liblcms2-utils` to provide the `jpgicc` command used by `Morandi::ProfiledPixbuf`
|
7
|
+
Install `liblcms2-utils` to provide the `jpgicc` command used by `Morandi::ProfiledPixbuf`. Also ensure that your host system has `imagemagick` installed, which is required by the `colorscore` gem.
|
9
8
|
|
10
9
|
Add this line to your application's Gemfile:
|
11
10
|
|
@@ -22,25 +21,10 @@ Or install it yourself as:
|
|
22
21
|
## Usage
|
23
22
|
|
24
23
|
````
|
25
|
-
Morandi.process(
|
24
|
+
Morandi.process(source, options, target_path)
|
26
25
|
````
|
27
|
-
|
28
|
-
|
29
|
-
- out_file is a string
|
30
|
-
|
31
|
-
Settings Key | Values | Description
|
32
|
-
-------------|--------|---------------
|
33
|
-
brighten | Integer -20..20 | Change image brightness
|
34
|
-
gamma | Float | Gamma correct image
|
35
|
-
contrast | Integer -20..20 | Change image contrast
|
36
|
-
sharpen | Integer -5..5 | Sharpen / Blur (negative value)
|
37
|
-
redeye | Array[[Integer,Integer],...] | Apply redeye correction at point
|
38
|
-
angle | Integer 0,90,180,270 | Rotate image
|
39
|
-
crop | Array[Integer,Integer,Integer,Integer] | Crop image
|
40
|
-
fx | String greyscale,sepia,bluetone | Apply colour filters
|
41
|
-
border-style | String square,retro | Set border style
|
42
|
-
background-style | String retro,black,white | Set border colour
|
43
|
-
quality | String '1'..'100' | Set JPG compression value, defaults to 97%
|
26
|
+
|
27
|
+
For the detailed documentation of options see `lib/morandi.rb`
|
44
28
|
|
45
29
|
## Contributing
|
46
30
|
|
@@ -49,3 +33,7 @@ quality | String '1'..'100' | Set JPG compression value, defaults to 97%
|
|
49
33
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
50
34
|
4. Push to the branch (`git push origin my-new-feature`)
|
51
35
|
5. Create new Pull Request
|
36
|
+
|
37
|
+
### Development
|
38
|
+
|
39
|
+
Since this gem depends on the `liblcms2-utils` library, which can be awkward to install on some operating systems, we also provide a development docker image. A Makefile is also provided as a simple CLI. To build the image and run the container, type `make` from the project root. The container itself runs `guard` as its main process. Running the container via `make` will drop you into the guard prompt, which will run the test suite whenever any of the source code or tests are changed. The tests can be kicked-off manually via the `all` command at the guard prompt. Individual test can be run using the `focus: true` annotation on an example or describe block. If you need to access a bash shell in the container (for example, to run rubocop), use the command `make shell`.
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Style/GlobalVars
|
4
|
+
require 'mkmf'
|
5
|
+
|
6
|
+
require 'English'
|
7
|
+
require 'rubygems'
|
8
|
+
gem 'glib2'
|
9
|
+
require 'mkmf-gnome'
|
10
|
+
|
11
|
+
def macos?
|
12
|
+
!!(RUBY_PLATFORM =~ /darwin/)
|
13
|
+
end
|
14
|
+
|
15
|
+
def clang?
|
16
|
+
cc_version = `#{RbConfig.expand('$(CC) --version'.dup)}`
|
17
|
+
cc_version.match?(/clang/i)
|
18
|
+
end
|
19
|
+
|
20
|
+
# XCode 14 warns if `-Wl,-undefined dynamic_lookup` is specified, and as
|
21
|
+
# a result Ruby interpreters compiled under XCode 14 no longer specify
|
22
|
+
# this flag by default in DLDFLAGS. Let's specify the list of dynamic symbols
|
23
|
+
# here to avoid compilation failures.
|
24
|
+
if clang? && macos?
|
25
|
+
dynamic_symbols = %w[
|
26
|
+
_rb_cairo_surface_from_ruby_object
|
27
|
+
_rb_cairo_surface_to_ruby_object_with_destroy
|
28
|
+
_rbgobj_instance_from_ruby_object
|
29
|
+
_rbgobj_ruby_object_from_instance
|
30
|
+
]
|
31
|
+
dynamic_symbols.each do |sym|
|
32
|
+
$DLDFLAGS << " -Wl,-U,#{sym.strip}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
%w[rbglib.h rbpango.h rcairo.h].each do |header|
|
37
|
+
Gem.find_files(header).each do |f|
|
38
|
+
$CFLAGS += " '-I#{File.dirname(f)}'"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
# Look for headers in {gem_root}/ext/{package}
|
42
|
+
%w[glib2 cairo].each do |package|
|
43
|
+
require package
|
44
|
+
if Gem.loaded_specs[package]
|
45
|
+
$CFLAGS += " -I#{Gem.loaded_specs[package].full_gem_path}/ext/#{package}"
|
46
|
+
else
|
47
|
+
fn = $LOADED_FEATURES.find { |n| n.sub(/[.](so|rb)$/, '') == package }
|
48
|
+
if fn
|
49
|
+
dr = $LOAD_PATH.find { |d| File.exist?(File.join(d, fn)) }
|
50
|
+
pt = File.join(dr, fn) if dr && fn
|
51
|
+
else
|
52
|
+
pt = '??'
|
53
|
+
end
|
54
|
+
warn "require '#{package}' loaded '#{pt}' instead of the gem - trying to continue, but build may fail"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
$CFLAGS += " -I#{RbConfig::CONFIG['rubyhdrdir']}/ruby" if RbConfig::CONFIG.key?('rubyhdrdir')
|
58
|
+
|
59
|
+
$CFLAGS += ' -I.'
|
60
|
+
have_func('rb_errinfo')
|
61
|
+
PKGConfig.have_package('gdk-pixbuf-2.0') or exit(-1)
|
62
|
+
# PKGConfig.have_package('gdk-2.0') or exit(-1)
|
63
|
+
PKGConfig.have_package('cairo') or exit(-1)
|
64
|
+
|
65
|
+
unless have_header('gdk-pixbuf/gdk-pixbuf.h')
|
66
|
+
paths = Gem.find_files('gdk-pixbuf/gdk-pixbuf.h')
|
67
|
+
paths.each do |path|
|
68
|
+
$CFLAGS += " '-I#{File.dirname(path)}'"
|
69
|
+
end
|
70
|
+
have_header('gdk-pixbuf/gdk-pixbuf.h') or exit(-1)
|
71
|
+
end
|
72
|
+
|
73
|
+
unless have_header('rbglib.h')
|
74
|
+
paths = Gem.find_files('rbglib.h')
|
75
|
+
paths.each do |path|
|
76
|
+
$CFLAGS += " '-I#{File.dirname(path)}'"
|
77
|
+
end
|
78
|
+
have_header('rbglib.h') or exit(-1)
|
79
|
+
end
|
80
|
+
|
81
|
+
unless have_header('rbgobject.h')
|
82
|
+
paths = Gem.find_files('rbgobject.h')
|
83
|
+
paths.each do |path|
|
84
|
+
$CFLAGS += " '-I#{File.dirname(path)}'"
|
85
|
+
end
|
86
|
+
have_header('rbgobject.h') or exit(-1)
|
87
|
+
end
|
88
|
+
|
89
|
+
$defs << '-DHAVE_OBJECT_ALLOCATE'
|
90
|
+
|
91
|
+
top = File.expand_path("#{File.dirname(__FILE__)}/..") # XXX
|
92
|
+
$CFLAGS << ' ' << ['glib/src'].map do |d|
|
93
|
+
"-I#{File.join(top, d)}"
|
94
|
+
end.join(' ')
|
95
|
+
|
96
|
+
begin
|
97
|
+
srcdir = File.expand_path(File.dirname($PROGRAM_NAME))
|
98
|
+
|
99
|
+
obj_ext = ".#{$OBJEXT}"
|
100
|
+
$libs = $libs.split(/ /).uniq.join(' ')
|
101
|
+
$source_files = Dir.glob(format('%s/*.c', srcdir)).map do |fname|
|
102
|
+
fname[0, srcdir.length + 1] = ''
|
103
|
+
fname
|
104
|
+
end
|
105
|
+
$objs = $source_files.collect do |item|
|
106
|
+
item.gsub(/.c$/, obj_ext)
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# create Makefile
|
111
|
+
#
|
112
|
+
$defs << '-DRUBY_MORANDI_NATIVE_COMPILATION'
|
113
|
+
create_makefile('gdk_pixbuf_cairo', srcdir)
|
114
|
+
raise Interrupt unless FileTest.exist? 'Makefile'
|
115
|
+
|
116
|
+
File.open('Makefile', 'a') do |mfile|
|
117
|
+
$source_files.each do |e|
|
118
|
+
mfile.print("#{e.gsub(/.c$/, obj_ext)}: #{e}\n")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
rescue Interrupt
|
122
|
+
print " [error] #{$ERROR_INFO}\n"
|
123
|
+
end
|
124
|
+
# rubocop:enable Style/GlobalVars
|
@@ -0,0 +1,260 @@
|
|
1
|
+
/* Includes */
|
2
|
+
#include <ruby.h>
|
3
|
+
#include <stdlib.h>
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <string.h>
|
6
|
+
|
7
|
+
#if defined GCC
|
8
|
+
#define OPTIONAL_ATTR __attribute__((unused))
|
9
|
+
#else
|
10
|
+
#define OPTIONAL_ATTR
|
11
|
+
#endif
|
12
|
+
|
13
|
+
#include "gdk-pixbuf/gdk-pixbuf.h"
|
14
|
+
#include "rbglib.h"
|
15
|
+
#include "rbgobject.h"
|
16
|
+
#include "rb_cairo.h"
|
17
|
+
|
18
|
+
static VALUE mGdkPixbufCairo;
|
19
|
+
void Init_gdk_pixbuf_cairo(void);
|
20
|
+
|
21
|
+
/**
|
22
|
+
* pixbuf_cairo_create:
|
23
|
+
* @pixbuf: GdkPixbuf that you wish to wrap with cairo context
|
24
|
+
*
|
25
|
+
* This function will initialize new cairo context with contents of @pixbuf. You
|
26
|
+
* can then draw using returned context. When finished drawing, you must call
|
27
|
+
* pixbuf_cairo_destroy() or your pixbuf will not be updated with new contents!
|
28
|
+
*
|
29
|
+
* Return value: New cairo_t context. When you're done with it, call
|
30
|
+
* pixbuf_cairo_destroy() to update your pixbuf and free memory.
|
31
|
+
*/
|
32
|
+
static cairo_surface_t *
|
33
|
+
pixbuf_to_surface(GdkPixbuf *pixbuf) {
|
34
|
+
gint width, /* Width of both pixbuf and surface */
|
35
|
+
height, /* Height of both pixbuf and surface */
|
36
|
+
p_stride, /* Pixbuf stride value */
|
37
|
+
p_n_channels, /* RGB -> 3, RGBA -> 4 */
|
38
|
+
s_stride, /* Surface stride value */
|
39
|
+
j;
|
40
|
+
guchar *p_pixels, /* Pixbuf's pixel data */
|
41
|
+
*s_pixels; /* Surface's pixel data */
|
42
|
+
cairo_surface_t *surface; /* Temporary image surface */
|
43
|
+
|
44
|
+
g_object_ref(G_OBJECT(pixbuf));
|
45
|
+
|
46
|
+
/* Inspect input pixbuf and create compatible cairo surface */
|
47
|
+
g_object_get(G_OBJECT(pixbuf), "width", &width,
|
48
|
+
"height", &height,
|
49
|
+
"rowstride", &p_stride,
|
50
|
+
"n-channels", &p_n_channels,
|
51
|
+
"pixels", &p_pixels,
|
52
|
+
NULL);
|
53
|
+
surface = cairo_image_surface_create(p_n_channels == 4 ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, width, height);
|
54
|
+
s_stride = cairo_image_surface_get_stride(surface);
|
55
|
+
s_pixels = cairo_image_surface_get_data(surface);
|
56
|
+
|
57
|
+
/* Copy pixel data from pixbuf to surface */
|
58
|
+
for (j = height; j; j--) {
|
59
|
+
guchar *p = p_pixels;
|
60
|
+
guchar *q = s_pixels;
|
61
|
+
|
62
|
+
if (p_n_channels == 3) {
|
63
|
+
guchar *end = p + 3 * width;
|
64
|
+
|
65
|
+
while (p < end) {
|
66
|
+
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
67
|
+
q[0] = p[2];
|
68
|
+
q[1] = p[1];
|
69
|
+
q[2] = p[0];
|
70
|
+
q[3] = 0xFF;
|
71
|
+
#else
|
72
|
+
q[0] = 0xFF;
|
73
|
+
q[1] = p[0];
|
74
|
+
q[2] = p[1];
|
75
|
+
q[3] = p[2];
|
76
|
+
#endif
|
77
|
+
p += 3;
|
78
|
+
q += 4;
|
79
|
+
}
|
80
|
+
} else {
|
81
|
+
guchar *end = p + 4 * width;
|
82
|
+
guint t1, t2, t3;
|
83
|
+
|
84
|
+
#define MULT(d, c, a, t) G_STMT_START { t = c * a + 0x80; d = ((t >> 8) + t) >> 8; } G_STMT_END
|
85
|
+
|
86
|
+
while (p < end) {
|
87
|
+
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
88
|
+
MULT(q[0], p[2], p[3], t1);
|
89
|
+
MULT(q[1], p[1], p[3], t2);
|
90
|
+
MULT(q[2], p[0], p[3], t3);
|
91
|
+
q[3] = p[3];
|
92
|
+
#else
|
93
|
+
q[0] = p[3];
|
94
|
+
MULT(q[1], p[0], p[3], t1);
|
95
|
+
MULT(q[2], p[1], p[3], t2);
|
96
|
+
MULT(q[3], p[2], p[3], t3);
|
97
|
+
#endif
|
98
|
+
|
99
|
+
p += 4;
|
100
|
+
q += 4;
|
101
|
+
}
|
102
|
+
|
103
|
+
#undef MULT
|
104
|
+
}
|
105
|
+
|
106
|
+
p_pixels += p_stride;
|
107
|
+
s_pixels += s_stride;
|
108
|
+
}
|
109
|
+
g_object_unref(G_OBJECT(pixbuf));
|
110
|
+
|
111
|
+
cairo_surface_mark_dirty(surface);
|
112
|
+
|
113
|
+
return surface;
|
114
|
+
}
|
115
|
+
|
116
|
+
/**
|
117
|
+
* pixbuf_cairo_destroy:
|
118
|
+
* @cr: Cairo context that you wish to destroy
|
119
|
+
* @create_new_pixbuf: If TRUE, new pixbuf will be created and returned. If
|
120
|
+
* FALSE, input pixbuf will be updated in place.
|
121
|
+
*
|
122
|
+
* This function will destroy cairo context, created with pixbuf_cairo_create().
|
123
|
+
*
|
124
|
+
* Return value: New or updated GdkPixbuf. You own a new reference on return
|
125
|
+
* value, so you need to call g_object_unref() on returned pixbuf when you don't
|
126
|
+
* need it anymore.
|
127
|
+
*/
|
128
|
+
static GdkPixbuf *
|
129
|
+
surface_to_pixbuf(cairo_surface_t *surface) {
|
130
|
+
gint width, /* Width of both pixbuf and surface */
|
131
|
+
height, /* Height of both pixbuf and surface */
|
132
|
+
p_stride, /* Pixbuf stride value */
|
133
|
+
p_n_channels, /* RGB -> 3, RGBA -> 4 */
|
134
|
+
s_stride, /* Surface stride value */
|
135
|
+
j;
|
136
|
+
guchar *p_pixels, /* Pixbuf's pixel data */
|
137
|
+
*s_pixels; /* Surface's pixel data */
|
138
|
+
GdkPixbuf *pixbuf; /* Pixbuf to be returned */
|
139
|
+
cairo_format_t format; /* cairo surface format */
|
140
|
+
|
141
|
+
format = cairo_image_surface_get_format(surface);
|
142
|
+
|
143
|
+
switch (format) {
|
144
|
+
case CAIRO_FORMAT_ARGB32:
|
145
|
+
p_n_channels = 4;
|
146
|
+
break;
|
147
|
+
case CAIRO_FORMAT_RGB24:
|
148
|
+
p_n_channels = 3;
|
149
|
+
break;
|
150
|
+
default:
|
151
|
+
return (GdkPixbuf *) 0;
|
152
|
+
break;
|
153
|
+
}
|
154
|
+
|
155
|
+
width = cairo_image_surface_get_width(surface);
|
156
|
+
height = cairo_image_surface_get_height(surface);
|
157
|
+
s_stride = cairo_image_surface_get_stride(surface);
|
158
|
+
s_pixels = cairo_image_surface_get_data(surface);
|
159
|
+
|
160
|
+
/* Create pixbuf to be returned */
|
161
|
+
pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, p_n_channels == 4, 8, width, height);
|
162
|
+
|
163
|
+
g_return_val_if_fail(pixbuf != NULL, NULL);
|
164
|
+
|
165
|
+
/* Inspect pixbuf and surface */
|
166
|
+
g_object_get(G_OBJECT(pixbuf),
|
167
|
+
"width", &width,
|
168
|
+
"height", &height,
|
169
|
+
"rowstride", &p_stride,
|
170
|
+
"n-channels", &p_n_channels,
|
171
|
+
"pixels", &p_pixels,
|
172
|
+
NULL);
|
173
|
+
|
174
|
+
|
175
|
+
/* Copy pixel data from surface to pixbuf */
|
176
|
+
for (j = height; j; j--) {
|
177
|
+
guchar *p = p_pixels;
|
178
|
+
guchar *q = s_pixels;
|
179
|
+
|
180
|
+
if (p_n_channels == 3) {
|
181
|
+
guchar *end = p + 3 * width;
|
182
|
+
|
183
|
+
while (p < end) {
|
184
|
+
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
185
|
+
p[2] = q[0];
|
186
|
+
p[1] = q[1];
|
187
|
+
p[0] = q[2];
|
188
|
+
#else
|
189
|
+
p[0] = q[1];
|
190
|
+
p[1] = q[2];
|
191
|
+
p[2] = q[3];
|
192
|
+
#endif
|
193
|
+
p += 3;
|
194
|
+
q += 4;
|
195
|
+
}
|
196
|
+
} else {
|
197
|
+
guchar *end = p + 4 * width;
|
198
|
+
guint t1, t2, t3;
|
199
|
+
|
200
|
+
#define UNMULT(s_byte, p_byte, a, t) G_STMT_START { \
|
201
|
+
t = s_byte * 255 / a; \
|
202
|
+
p_byte = t; \
|
203
|
+
} G_STMT_END
|
204
|
+
|
205
|
+
while (p < end) {
|
206
|
+
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
207
|
+
UNMULT(q[0], p[2], q[3], t1);
|
208
|
+
UNMULT(q[1], p[1], q[3], t2);
|
209
|
+
UNMULT(q[2], p[0], q[3], t3);
|
210
|
+
p[3] = q[3];
|
211
|
+
#else
|
212
|
+
p[3] = q[0];
|
213
|
+
UNMULT(q[1], p[0], q[3], t1);
|
214
|
+
UNMULT(q[2], p[1], q[3], t2);
|
215
|
+
UNMULT(q[3], p[2], q[3], t3);
|
216
|
+
#endif
|
217
|
+
|
218
|
+
p += 4;
|
219
|
+
q += 4;
|
220
|
+
}
|
221
|
+
|
222
|
+
#undef MULT
|
223
|
+
}
|
224
|
+
|
225
|
+
p_pixels += p_stride;
|
226
|
+
s_pixels += s_stride;
|
227
|
+
}
|
228
|
+
|
229
|
+
/* Return pixbuf */
|
230
|
+
return (pixbuf);
|
231
|
+
}
|
232
|
+
|
233
|
+
static
|
234
|
+
VALUE rb_pixbuf_to_surface(__attribute__((unused)) VALUE _self, VALUE pixbuf) {
|
235
|
+
cairo_surface_t *surface = pixbuf_to_surface(GDK_PIXBUF(RVAL2GOBJ(pixbuf)));
|
236
|
+
return CRSURFACE2RVAL_WITH_DESTROY(surface);
|
237
|
+
}
|
238
|
+
|
239
|
+
static
|
240
|
+
VALUE rb_surface_to_pixbuf(__attribute__((unused)) VALUE _self, VALUE surface) {
|
241
|
+
VALUE obj;
|
242
|
+
GdkPixbuf *pixbuf = surface_to_pixbuf(RVAL2CRSURFACE(surface));
|
243
|
+
if (pixbuf) {
|
244
|
+
obj = GOBJ2RVAL(pixbuf);
|
245
|
+
g_object_unref(pixbuf);
|
246
|
+
return obj;
|
247
|
+
}
|
248
|
+
|
249
|
+
rb_raise(rb_eRuntimeError, "Unable to convert Cairo::ImageSurface to Gdk::Pixbuf");
|
250
|
+
|
251
|
+
return Qnil;
|
252
|
+
}
|
253
|
+
|
254
|
+
/* Init */
|
255
|
+
void
|
256
|
+
Init_gdk_pixbuf_cairo(void) {
|
257
|
+
mGdkPixbufCairo = rb_define_module("GdkPixbufCairo");
|
258
|
+
rb_define_singleton_method(mGdkPixbufCairo, "pixbuf_to_surface", rb_pixbuf_to_surface, 1);
|
259
|
+
rb_define_singleton_method(mGdkPixbufCairo, "surface_to_pixbuf", rb_surface_to_pixbuf, 1);
|
260
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Style/GlobalVars
|
4
|
+
require 'mkmf'
|
5
|
+
|
6
|
+
require 'English'
|
7
|
+
require 'rubygems'
|
8
|
+
gem 'glib2'
|
9
|
+
require 'mkmf-gnome'
|
10
|
+
|
11
|
+
def macos?
|
12
|
+
!!(RUBY_PLATFORM =~ /darwin/)
|
13
|
+
end
|
14
|
+
|
15
|
+
def clang?
|
16
|
+
cc_version = `#{RbConfig.expand('$(CC) --version'.dup)}`
|
17
|
+
cc_version.match?(/clang/i)
|
18
|
+
end
|
19
|
+
|
20
|
+
# XCode 14 warns if `-Wl,-undefined dynamic_lookup` is specified, and as
|
21
|
+
# a result Ruby interpreters compiled under XCode 14 no longer specify
|
22
|
+
# this flag by default in DLDFLAGS. Let's specify the list of dynamic symbols
|
23
|
+
# here to avoid compilation failures.
|
24
|
+
if clang? && macos?
|
25
|
+
dynamic_symbols = %w[
|
26
|
+
_rbgobj_instance_from_ruby_object
|
27
|
+
_rbgobj_ruby_object_from_instance
|
28
|
+
]
|
29
|
+
dynamic_symbols.each do |sym|
|
30
|
+
$DLDFLAGS << " -Wl,-U,#{sym.strip}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
%w[rbglib.h rbpango.h].each do |header|
|
35
|
+
Gem.find_files(header).each do |f|
|
36
|
+
$CFLAGS += " '-I#{File.dirname(f)}'"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
# Look for headers in {gem_root}/ext/{package}
|
40
|
+
%w[glib2].each do |package|
|
41
|
+
require package
|
42
|
+
if Gem.loaded_specs[package]
|
43
|
+
$CFLAGS += " -I#{Gem.loaded_specs[package].full_gem_path}/ext/#{package}"
|
44
|
+
else
|
45
|
+
fn = $LOADED_FEATURES.find { |n| n.sub(/[.](so|rb)$/, '') == package }
|
46
|
+
if fn
|
47
|
+
dr = $LOAD_PATH.find { |d| File.exist?(File.join(d, fn)) }
|
48
|
+
pt = File.join(dr, fn) if dr && fn
|
49
|
+
else
|
50
|
+
pt = '??'
|
51
|
+
end
|
52
|
+
warn "require '#{package}' loaded '#{pt}' instead of the gem - trying to continue, but build may fail"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
$CFLAGS += " -I#{RbConfig::CONFIG['rubyhdrdir']}/ruby" if RbConfig::CONFIG.key?('rubyhdrdir')
|
56
|
+
|
57
|
+
$CFLAGS += ' -I.'
|
58
|
+
have_func('rb_errinfo')
|
59
|
+
PKGConfig.have_package('gdk-pixbuf-2.0') or exit(-1)
|
60
|
+
# PKGConfig.have_package('gdk-2.0') or exit(-1)
|
61
|
+
|
62
|
+
unless have_header('gdk-pixbuf/gdk-pixbuf.h')
|
63
|
+
paths = Gem.find_files('gdk-pixbuf/gdk-pixbuf.h')
|
64
|
+
paths.each do |path|
|
65
|
+
$CFLAGS += " '-I#{File.dirname(path)}'"
|
66
|
+
end
|
67
|
+
have_header('gdk-pixbuf/gdk-pixbuf.h') or exit(-1)
|
68
|
+
end
|
69
|
+
|
70
|
+
unless have_header('rbglib.h')
|
71
|
+
paths = Gem.find_files('rbglib.h')
|
72
|
+
paths.each do |path|
|
73
|
+
$CFLAGS += " '-I#{File.dirname(path)}'"
|
74
|
+
end
|
75
|
+
have_header('rbglib.h') or exit(-1)
|
76
|
+
end
|
77
|
+
|
78
|
+
unless have_header('rbgobject.h')
|
79
|
+
paths = Gem.find_files('rbgobject.h')
|
80
|
+
paths.each do |path|
|
81
|
+
$CFLAGS += " '-I#{File.dirname(path)}'"
|
82
|
+
end
|
83
|
+
have_header('rbgobject.h') or exit(-1)
|
84
|
+
end
|
85
|
+
|
86
|
+
$defs << '-DHAVE_OBJECT_ALLOCATE'
|
87
|
+
|
88
|
+
top = File.expand_path("#{File.dirname(__FILE__)}/..") # XXX
|
89
|
+
$CFLAGS << ' ' << ['glib/src'].map do |d|
|
90
|
+
"-I#{File.join(top, d)}"
|
91
|
+
end.join(' ')
|
92
|
+
|
93
|
+
begin
|
94
|
+
srcdir = File.expand_path(File.dirname($PROGRAM_NAME))
|
95
|
+
|
96
|
+
obj_ext = ".#{$OBJEXT}"
|
97
|
+
$libs = $libs.split(/ /).uniq.join(' ')
|
98
|
+
$source_files = Dir.glob(format('%s/*.c', srcdir)).map do |fname|
|
99
|
+
fname[0, srcdir.length + 1] = ''
|
100
|
+
fname
|
101
|
+
end
|
102
|
+
$objs = $source_files.collect do |item|
|
103
|
+
item.gsub(/.c$/, obj_ext)
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# create Makefile
|
108
|
+
#
|
109
|
+
$defs << '-DRUBY_MORANDI_NATIVE_COMPILATION'
|
110
|
+
create_makefile('morandi_native', srcdir)
|
111
|
+
raise Interrupt unless FileTest.exist? 'Makefile'
|
112
|
+
|
113
|
+
File.open('Makefile', 'a') do |mfile|
|
114
|
+
$source_files.each do |e|
|
115
|
+
mfile.print("#{e.gsub(/.c$/, obj_ext)}: #{e}\n")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
rescue Interrupt
|
119
|
+
print " [error] #{$ERROR_INFO}\n"
|
120
|
+
end
|
121
|
+
# rubocop:enable Style/GlobalVars
|