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
@@ -0,0 +1,79 @@
|
|
1
|
+
typedef enum {
|
2
|
+
ANGLE_0 = 0,
|
3
|
+
ANGLE_90 = 90,
|
4
|
+
ANGLE_180 = 180,
|
5
|
+
ANGLE_270 = 270
|
6
|
+
} rotate_angle_t;
|
7
|
+
|
8
|
+
static GdkPixbuf *
|
9
|
+
pixbuf_rotate(GdkPixbuf *src, rotate_angle_t angle) {
|
10
|
+
GdkPixbuf *dest;
|
11
|
+
int has_alpha;
|
12
|
+
int s_width, s_height, s_rowstride;
|
13
|
+
int d_width, d_height, d_rowstride;
|
14
|
+
guchar *s_pix;
|
15
|
+
guchar *d_pix;
|
16
|
+
guchar *sp;
|
17
|
+
guchar *dp;
|
18
|
+
int i, j, pix_width;
|
19
|
+
|
20
|
+
if (!src) return NULL;
|
21
|
+
|
22
|
+
if (angle == ANGLE_0)
|
23
|
+
return gdk_pixbuf_copy(src);
|
24
|
+
|
25
|
+
s_width = gdk_pixbuf_get_width(src);
|
26
|
+
s_height = gdk_pixbuf_get_height(src);
|
27
|
+
has_alpha = gdk_pixbuf_get_has_alpha(src);
|
28
|
+
s_rowstride = gdk_pixbuf_get_rowstride(src);
|
29
|
+
s_pix = gdk_pixbuf_get_pixels(src);
|
30
|
+
|
31
|
+
switch (angle) {
|
32
|
+
case ANGLE_90:
|
33
|
+
case ANGLE_270:
|
34
|
+
d_width = s_height;
|
35
|
+
d_height = s_width;
|
36
|
+
break;
|
37
|
+
default:
|
38
|
+
case ANGLE_0:/* Avoid compiler warnings... */
|
39
|
+
case ANGLE_180:
|
40
|
+
d_width = s_width;
|
41
|
+
d_height = s_height;
|
42
|
+
break;
|
43
|
+
}
|
44
|
+
|
45
|
+
dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, has_alpha, 8, d_width, d_height);
|
46
|
+
d_rowstride = gdk_pixbuf_get_rowstride(dest);
|
47
|
+
d_pix = gdk_pixbuf_get_pixels(dest);
|
48
|
+
|
49
|
+
pix_width = (has_alpha ? 4 : 3);
|
50
|
+
|
51
|
+
for (i = 0; i < s_height; i++) {
|
52
|
+
sp = s_pix + (i * s_rowstride);
|
53
|
+
for (j = 0; j < s_width; j++) {
|
54
|
+
switch (angle) {
|
55
|
+
case ANGLE_180:
|
56
|
+
dp = d_pix + ((d_height - i - 1) * d_rowstride) + ((d_width - j - 1) * pix_width);
|
57
|
+
break;
|
58
|
+
case ANGLE_90:
|
59
|
+
dp = d_pix + (j * d_rowstride) + ((d_width - i - 1) * pix_width);
|
60
|
+
break;
|
61
|
+
case ANGLE_270:
|
62
|
+
dp = d_pix + ((d_height - j - 1) * d_rowstride) + (i * pix_width);
|
63
|
+
break;
|
64
|
+
default:
|
65
|
+
case ANGLE_0:/* Avoid compiler warnings... */
|
66
|
+
dp = d_pix + (i * d_rowstride) + (j * pix_width);
|
67
|
+
break;
|
68
|
+
}
|
69
|
+
|
70
|
+
*(dp++) = *(sp++); /* red */
|
71
|
+
*(dp++) = *(sp++); /* green */
|
72
|
+
*(dp++) = *(sp++); /* blue */
|
73
|
+
if (has_alpha) *(dp) = *(sp++); /* alpha */
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
return dest;
|
78
|
+
}
|
79
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#define RLUM (0.3086)
|
2
|
+
#define GLUM (0.6094)
|
3
|
+
#define BLUM (0.0820)
|
4
|
+
|
5
|
+
// Graphica Obscure
|
6
|
+
#define GO_RGB_TO_GREY(r, g, b) ((int)((RLUM * (double)r) + (GLUM * (double)g) + (BLUM * (double)b)))
|
7
|
+
|
8
|
+
static inline unsigned char pu_clamp(int x) {
|
9
|
+
return (x > 255) ? 255 : (x < 0 ? 0 : x);
|
10
|
+
}
|
11
|
+
|
12
|
+
static GdkPixbuf *pixbuf_tint(GdkPixbuf *src, GdkPixbuf *dest, int r, int g, int b, int alpha) {
|
13
|
+
int s_has_alpha, d_has_alpha;
|
14
|
+
int s_width, s_height, s_rowstride;
|
15
|
+
int d_width, d_height, d_rowstride;
|
16
|
+
guchar *s_pix, *sp;
|
17
|
+
guchar *d_pix, *dp;
|
18
|
+
int i, j, pix_width, grey;
|
19
|
+
|
20
|
+
g_return_val_if_fail(src != NULL, NULL);
|
21
|
+
g_return_val_if_fail(dest != NULL, NULL);
|
22
|
+
|
23
|
+
s_width = gdk_pixbuf_get_width(src);
|
24
|
+
s_height = gdk_pixbuf_get_height(src);
|
25
|
+
s_has_alpha = gdk_pixbuf_get_has_alpha(src);
|
26
|
+
s_rowstride = gdk_pixbuf_get_rowstride(src);
|
27
|
+
s_pix = gdk_pixbuf_get_pixels(src);
|
28
|
+
|
29
|
+
d_width = gdk_pixbuf_get_width(dest);
|
30
|
+
d_height = gdk_pixbuf_get_height(dest);
|
31
|
+
d_has_alpha = gdk_pixbuf_get_has_alpha(dest);
|
32
|
+
d_rowstride = gdk_pixbuf_get_rowstride(dest);
|
33
|
+
d_pix = gdk_pixbuf_get_pixels(dest);
|
34
|
+
|
35
|
+
g_return_val_if_fail(d_width == s_width, NULL);
|
36
|
+
g_return_val_if_fail(d_height == s_height, NULL);
|
37
|
+
g_return_val_if_fail(d_has_alpha == s_has_alpha, NULL);
|
38
|
+
|
39
|
+
pix_width = (s_has_alpha ? 4 : 3);
|
40
|
+
|
41
|
+
for (i = 0; i < s_height; i++) {
|
42
|
+
sp = s_pix;
|
43
|
+
dp = d_pix;
|
44
|
+
|
45
|
+
for (j = 0; j < s_width; j++) {
|
46
|
+
grey = GO_RGB_TO_GREY(sp[0], sp[1], sp[2]);
|
47
|
+
|
48
|
+
dp[0] = pu_clamp(pu_clamp(((int) grey + r) * alpha / 255) +
|
49
|
+
pu_clamp((int) sp[0] * (255 - alpha) / 255)); /* red */
|
50
|
+
|
51
|
+
//fprintf(stderr, "alpha=%i, r=%i, grey=%i -> %i + %i = %i\n", alpha, r, grey, pu_clamp(((int)grey + r) * alpha / 255), pu_clamp((int)sp[0] * (255 - alpha) / 255), dp[0]); /* red */
|
52
|
+
|
53
|
+
dp[1] = pu_clamp(
|
54
|
+
pu_clamp((grey + g) * alpha / 255) + pu_clamp((int) sp[1] * (255 - alpha) / 255)); /* green */
|
55
|
+
dp[2] = pu_clamp(
|
56
|
+
pu_clamp((grey + b) * alpha / 255) + pu_clamp((int) sp[2] * (255 - alpha) / 255)); /* blue */
|
57
|
+
|
58
|
+
if (s_has_alpha) {
|
59
|
+
dp[3] = sp[3]; /* alpha */
|
60
|
+
}
|
61
|
+
|
62
|
+
dp += pix_width;
|
63
|
+
sp += pix_width;
|
64
|
+
}
|
65
|
+
|
66
|
+
d_pix += d_rowstride;
|
67
|
+
s_pix += s_rowstride;
|
68
|
+
}
|
69
|
+
|
70
|
+
return dest;
|
71
|
+
}
|
72
|
+
|
Binary file
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cairo'
|
4
|
+
require 'gdk_pixbuf_cairo'
|
5
|
+
require 'pango'
|
6
|
+
|
7
|
+
module Morandi
|
8
|
+
# Rounded rectangle function for photo borders
|
9
|
+
module CairoExt
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def rounded_rectangle(cr, x1, y1, x2, y2, x_radius = 4, y_radius = nil)
|
13
|
+
width = x2 - x1
|
14
|
+
height = y2 - y1
|
15
|
+
y_radius ||= x_radius
|
16
|
+
|
17
|
+
x_radius = [x_radius, width / 2].min
|
18
|
+
y_radius = [y_radius, height / 2].min
|
19
|
+
|
20
|
+
xr1 = x_radius
|
21
|
+
xr2 = x_radius / 2.0
|
22
|
+
yr1 = y_radius
|
23
|
+
yr2 = y_radius / 2.0
|
24
|
+
|
25
|
+
cr.new_path
|
26
|
+
cr.move_to(x1 + xr1, y1)
|
27
|
+
cr.line_to(x2 - xr1, y1)
|
28
|
+
cr.curve_to(x2 - xr2, y1, x2, y1 + yr2, x2, y1 + yr1)
|
29
|
+
cr.line_to(x2, y2 - yr1)
|
30
|
+
cr.curve_to(x2, y2 - yr2, x2 - xr2, y2, x2 - xr1, y2)
|
31
|
+
cr.line_to(x1 + xr1, y2)
|
32
|
+
cr.curve_to(x1 + xr2, y2, x1, y2 - yr2, x1, y2 - yr1)
|
33
|
+
cr.line_to(x1, y1 + yr1)
|
34
|
+
cr.curve_to(x1, y1 + yr2, x1 + xr2, y1, x1 + xr1, y1)
|
35
|
+
cr.close_path
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Monkey patch Cairo::Context
|
41
|
+
module Cairo
|
42
|
+
# Add Cairo::Context#set_source_pixbuf without gtk2 depdendency
|
43
|
+
class Context
|
44
|
+
def set_source_pixbuf(pixbuf, x = 0, y = 0)
|
45
|
+
set_source(pixbuf.to_cairo_image_surface, x, y)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Add ImageSurface.to_gdk_pixbuf
|
50
|
+
# for converting back to pixbuf without exporting as PNG
|
51
|
+
class ImageSurface
|
52
|
+
def to_gdk_pixbuf
|
53
|
+
GdkPixbufCairo.surface_to_pixbuf(self)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gdk_pixbuf2'
|
4
|
+
|
5
|
+
module Morandi
|
6
|
+
# Utility functions relating to cropping
|
7
|
+
module CropUtils
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def autocrop_coords(pixbuf_width, pixbuf_height, target_width, target_height)
|
11
|
+
return nil unless target_width
|
12
|
+
|
13
|
+
aspect = target_width.to_f / target_height
|
14
|
+
pixbuf_aspect = pixbuf_width.to_f / pixbuf_height
|
15
|
+
|
16
|
+
# TODO: this looks wrong - typically relative aspect ratios matter more
|
17
|
+
# than whether this is portrait or landscape
|
18
|
+
if pixbuf_height > pixbuf_width
|
19
|
+
# Portrait image
|
20
|
+
# Check whether the aspect ratio is greater or smaller
|
21
|
+
# ie. where constraints will hit
|
22
|
+
aspect = target_height.to_f / target_width
|
23
|
+
end
|
24
|
+
|
25
|
+
# Landscape
|
26
|
+
if aspect > pixbuf_aspect
|
27
|
+
# Width constraint - aspect-rect wider
|
28
|
+
crop_width = pixbuf_width
|
29
|
+
crop_height = (crop_width / aspect).to_i
|
30
|
+
else
|
31
|
+
# Height constraint - aspect-rect wider
|
32
|
+
crop_height = pixbuf_height
|
33
|
+
crop_width = (crop_height * aspect).to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
[
|
37
|
+
((pixbuf_width - crop_width) >> 1),
|
38
|
+
((pixbuf_height - crop_height) >> 1),
|
39
|
+
crop_width,
|
40
|
+
crop_height
|
41
|
+
].map(&:to_i)
|
42
|
+
end
|
43
|
+
|
44
|
+
def apply_crop(pixbuf, x_coord, y_coord, width, height, fill_col = 0xffffffff)
|
45
|
+
if x_coord.negative? ||
|
46
|
+
y_coord.negative? ||
|
47
|
+
((x_coord + width) > pixbuf.width) ||
|
48
|
+
((y_coord + height) > pixbuf.height)
|
49
|
+
|
50
|
+
base_pixbuf = GdkPixbuf::Pixbuf.new(
|
51
|
+
colorspace: GdkPixbuf::Colorspace::RGB,
|
52
|
+
has_alpha: false,
|
53
|
+
bits_per_sample: 8,
|
54
|
+
width: width,
|
55
|
+
height: height
|
56
|
+
)
|
57
|
+
base_pixbuf.fill!(fill_col)
|
58
|
+
|
59
|
+
offset_x = [x_coord, 0].max
|
60
|
+
offset_y = [y_coord, 0].max
|
61
|
+
copy_w = [width, pixbuf.width - offset_x].min
|
62
|
+
copy_h = [height, pixbuf.height - offset_y].min
|
63
|
+
|
64
|
+
paste_x = [x_coord, 0].min * -1
|
65
|
+
paste_y = [y_coord, 0].min * -1
|
66
|
+
|
67
|
+
copy_w = base_pixbuf.width - paste_x if copy_w + paste_x > base_pixbuf.width
|
68
|
+
copy_h = base_pixbuf.height - paste_y if copy_h + paste_y > base_pixbuf.height
|
69
|
+
|
70
|
+
base_pixbuf.composite!(
|
71
|
+
pixbuf,
|
72
|
+
dest_x: paste_x,
|
73
|
+
dest_y: paste_y,
|
74
|
+
dest_width: copy_w,
|
75
|
+
dest_height: copy_h,
|
76
|
+
offset_x: paste_x - offset_x,
|
77
|
+
offset_y: paste_y - offset_y,
|
78
|
+
scale_x: 1,
|
79
|
+
scale_y: 1,
|
80
|
+
interpolation_type: :hyper,
|
81
|
+
overall_alpha: 255
|
82
|
+
)
|
83
|
+
pixbuf = base_pixbuf
|
84
|
+
else
|
85
|
+
x_coord = x_coord.clamp(0, pixbuf.width)
|
86
|
+
y_coord = y_coord.clamp(0, pixbuf.height)
|
87
|
+
width = width.clamp(1, pixbuf.width - x_coord)
|
88
|
+
height = height.clamp(1, pixbuf.height - y_coord)
|
89
|
+
|
90
|
+
pixbuf = pixbuf.subpixbuf(x_coord, y_coord, width, height)
|
91
|
+
end
|
92
|
+
pixbuf
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Morandi
|
4
|
+
# Base Image Op class
|
5
|
+
# @!visibility private
|
6
|
+
class ImageOperation
|
7
|
+
class << self
|
8
|
+
def new_from_hash(hash)
|
9
|
+
op = allocate
|
10
|
+
hash.each_pair do |key, val|
|
11
|
+
op.respond_to?(key.intern) && op.instance_variable_set("@#{key}", val)
|
12
|
+
end
|
13
|
+
op
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def create_pixbuf_from_image_surface(type, width, height)
|
20
|
+
surface = Cairo::ImageSurface.new(type, width, height)
|
21
|
+
cr = Cairo::Context.new(surface)
|
22
|
+
|
23
|
+
yield(cr)
|
24
|
+
|
25
|
+
final_pb = surface.to_gdk_pixbuf
|
26
|
+
cr.destroy
|
27
|
+
surface.destroy
|
28
|
+
final_pb
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|