morandi 0.12.1 → 0.99.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Morandi
4
+ class Error < StandardError
5
+ end
6
+
7
+ class CorruptImageError < Error
8
+ end
9
+
10
+ class UnknownTypeError < Error
11
+ end
12
+ 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