morandi 0.12.1 → 0.99.03
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 +20 -1
- data/README.md +6 -1
- 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/image_operation.rb +205 -0
- data/lib/morandi/image_processor.rb +180 -180
- data/lib/morandi/pixbuf_ext.rb +19 -0
- data/lib/morandi/profiled_pixbuf.rb +40 -61
- data/lib/morandi/redeye.rb +35 -47
- data/lib/morandi/version.rb +3 -1
- data/lib/morandi.rb +33 -11
- data/lib/morandi_native.so +0 -0
- metadata +52 -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,205 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'colorscore'
|
4
|
+
|
5
|
+
module Morandi
|
6
|
+
# Base Image Op class
|
7
|
+
# @!visibility private
|
8
|
+
class ImageOperation
|
9
|
+
class << self
|
10
|
+
def new_from_hash(hash)
|
11
|
+
op = allocate
|
12
|
+
hash.each_pair do |key, val|
|
13
|
+
op.respond_to?(key.intern) && op.instance_variable_set("@#{key}", val)
|
14
|
+
end
|
15
|
+
op
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def create_pixbuf_from_image_surface(type, width, height)
|
22
|
+
surface = Cairo::ImageSurface.new(type, width, height)
|
23
|
+
cr = Cairo::Context.new(surface)
|
24
|
+
|
25
|
+
yield(cr)
|
26
|
+
|
27
|
+
final_pb = surface.to_gdk_pixbuf
|
28
|
+
cr.destroy
|
29
|
+
surface.destroy
|
30
|
+
final_pb
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Straighten operation
|
35
|
+
# Does a small (ie. not 90,180,270 deg) rotation and zooms to avoid cropping
|
36
|
+
# @!visibility private
|
37
|
+
class Straighten < ImageOperation
|
38
|
+
attr_accessor :angle
|
39
|
+
|
40
|
+
def call(_image, pixbuf)
|
41
|
+
return pixbuf if angle.zero?
|
42
|
+
|
43
|
+
rotation_value_rad = angle * (Math::PI / 180)
|
44
|
+
|
45
|
+
ratio = pixbuf.width.to_f / pixbuf.height
|
46
|
+
rh = pixbuf.height / ((ratio * Math.sin(rotation_value_rad.abs)) + Math.cos(rotation_value_rad.abs))
|
47
|
+
scale = pixbuf.height / rh.to_f.abs
|
48
|
+
|
49
|
+
a_ratio = pixbuf.height.to_f / pixbuf.width
|
50
|
+
a_rh = pixbuf.width / ((a_ratio * Math.sin(rotation_value_rad.abs)) + Math.cos(rotation_value_rad.abs))
|
51
|
+
a_scale = pixbuf.width / a_rh.to_f.abs
|
52
|
+
|
53
|
+
scale = a_scale if a_scale > scale
|
54
|
+
|
55
|
+
create_pixbuf_from_image_surface(:rgb24, pixbuf.width, pixbuf.height) do |cr|
|
56
|
+
cr.translate(pixbuf.width / 2.0, pixbuf.height / 2.0)
|
57
|
+
cr.rotate(rotation_value_rad)
|
58
|
+
cr.scale(scale, scale)
|
59
|
+
cr.translate(pixbuf.width / -2.0, pixbuf.height / - 2.0)
|
60
|
+
cr.set_source_pixbuf(pixbuf)
|
61
|
+
|
62
|
+
cr.rectangle(0, 0, pixbuf.width, pixbuf.height)
|
63
|
+
cr.paint(1.0)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Image Border operation
|
69
|
+
# Supports retro (rounded) and square borders
|
70
|
+
# Background colour (ie. border colour) can be white, black, dominant (ie. from image)
|
71
|
+
# @!visibility private
|
72
|
+
class ImageBorder < ImageOperation
|
73
|
+
attr_accessor :style, :colour, :crop, :size, :print_size, :shrink, :border_size
|
74
|
+
|
75
|
+
def call(_image, pixbuf)
|
76
|
+
return pixbuf unless %w[square retro].include? @style
|
77
|
+
|
78
|
+
create_pixbuf_from_image_surface(:rgb24, pixbuf.width, pixbuf.height) do |cr|
|
79
|
+
if @crop && ((@crop[0]).negative? || (@crop[1]).negative?)
|
80
|
+
img_width = size[0]
|
81
|
+
img_height = size[1]
|
82
|
+
else
|
83
|
+
img_width = pixbuf.width
|
84
|
+
img_height = pixbuf.height
|
85
|
+
end
|
86
|
+
|
87
|
+
@border_scale = [img_width, img_height].max.to_f / print_size.max.to_i
|
88
|
+
|
89
|
+
draw_background(cr, img_height, img_width, pixbuf)
|
90
|
+
|
91
|
+
x = border_width
|
92
|
+
y = border_width
|
93
|
+
|
94
|
+
# This biggest impact will be on the smallest side, so to avoid white
|
95
|
+
# edges between photo and border scale by the longest changed side.
|
96
|
+
longest_side = [pixbuf.width, pixbuf.height].max.to_f
|
97
|
+
|
98
|
+
# Should be less than 1
|
99
|
+
pb_scale = (longest_side - (border_width * 2)) / longest_side
|
100
|
+
|
101
|
+
if @crop && ((@crop[0]).negative? || (@crop[1]).negative?)
|
102
|
+
x -= @crop[0]
|
103
|
+
y -= @crop[1]
|
104
|
+
end
|
105
|
+
|
106
|
+
draw_pixbuf(pixbuf, cr, img_height, img_width, pb_scale, x, y)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# Width is proportional to output size
|
113
|
+
def border_width
|
114
|
+
@border_size * @border_scale
|
115
|
+
end
|
116
|
+
|
117
|
+
def draw_pixbuf(pixbuf, cr, img_height, img_width, pb_scale, x, y)
|
118
|
+
case style
|
119
|
+
when 'retro'
|
120
|
+
Morandi::CairoExt.rounded_rectangle(cr, x, y,
|
121
|
+
img_width + x - (border_width * 2),
|
122
|
+
img_height + y - (border_width * 2), border_width)
|
123
|
+
when 'square'
|
124
|
+
cr.rectangle(x, y, img_width - (border_width * 2), img_height - (border_width * 2))
|
125
|
+
end
|
126
|
+
cr.clip
|
127
|
+
|
128
|
+
if @shrink
|
129
|
+
cr.translate(border_width, border_width)
|
130
|
+
cr.scale(pb_scale, pb_scale)
|
131
|
+
end
|
132
|
+
cr.set_source_pixbuf(pixbuf)
|
133
|
+
cr.rectangle(0, 0, pixbuf.width, pixbuf.height)
|
134
|
+
|
135
|
+
cr.paint(1.0)
|
136
|
+
end
|
137
|
+
|
138
|
+
def draw_background(cr, img_height, img_width, pixbuf)
|
139
|
+
cr.save do
|
140
|
+
cr.translate(-@crop[0], -@crop[1]) if @crop && ((@crop[0]).negative? || (@crop[1]).negative?)
|
141
|
+
|
142
|
+
cr.save do
|
143
|
+
cr.set_operator :source
|
144
|
+
cr.set_source_rgb 1, 1, 1
|
145
|
+
cr.paint
|
146
|
+
|
147
|
+
cr.rectangle(0, 0, img_width, img_height)
|
148
|
+
case colour
|
149
|
+
when 'dominant'
|
150
|
+
pixbuf.scale_max(400).save(fn = "/tmp/hist-#{$PROCESS_ID}.#{Time.now.to_i}", 'jpeg')
|
151
|
+
histogram = Colorscore::Histogram.new(fn)
|
152
|
+
FileUtils.rm_f(fn)
|
153
|
+
col = histogram.scores.first[1]
|
154
|
+
cr.set_source_rgb col.red / 256.0, col.green / 256.0, col.blue / 256.0
|
155
|
+
when 'retro'
|
156
|
+
cr.set_source_rgb 1, 1, 0.8
|
157
|
+
when 'black'
|
158
|
+
cr.set_source_rgb 0, 0, 0
|
159
|
+
else
|
160
|
+
cr.set_source_rgb 1, 1, 1
|
161
|
+
end
|
162
|
+
cr.fill
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Colourify Operation
|
169
|
+
# Apply tint to image with variable strength
|
170
|
+
# Supports filter, alpha
|
171
|
+
class Colourify < ImageOperation
|
172
|
+
attr_reader :filter
|
173
|
+
|
174
|
+
def alpha
|
175
|
+
@alpha || 255
|
176
|
+
end
|
177
|
+
|
178
|
+
def sepia(pixbuf)
|
179
|
+
MorandiNative::PixbufUtils.tint(pixbuf, 25, 5, -25, alpha)
|
180
|
+
end
|
181
|
+
|
182
|
+
def bluetone(pixbuf)
|
183
|
+
MorandiNative::PixbufUtils.tint(pixbuf, -10, 5, 25, alpha)
|
184
|
+
end
|
185
|
+
|
186
|
+
def null(pixbuf)
|
187
|
+
pixbuf
|
188
|
+
end
|
189
|
+
alias full null # WebKiosk
|
190
|
+
alias colour null # WebKiosk
|
191
|
+
|
192
|
+
def greyscale(pixbuf)
|
193
|
+
MorandiNative::PixbufUtils.tint(pixbuf, 0, 0, 0, alpha)
|
194
|
+
end
|
195
|
+
alias bw greyscale # WebKiosk
|
196
|
+
|
197
|
+
def call(_image, pixbuf)
|
198
|
+
if @filter && respond_to?(@filter)
|
199
|
+
__send__(@filter, pixbuf)
|
200
|
+
else
|
201
|
+
pixbuf # Default is nothing
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|