morandi 0.12.1 → 0.99.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|