.
1 /*
2 Copyright (C) 2012 2014 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using Cairo;
16 using Math;
17
18 namespace BirdFont {
19
20 public class CutBackgroundTool : Tool {
21
22 double x1 = 0;
23 double y1 = 0;
24 double x2 = 0;
25 double y2 = 0;
26
27 bool is_visible = false;
28 bool is_set = false;
29 bool is_done = false;
30
31 public signal void new_image (BackgroundImage file,
32 double coordinate_x, double coordinate_y, double width, double height);
33
34 public bool self_destination = true;
35
36 public CutBackgroundTool (string name, string? tool_tip = null) {
37 string tt;
38 tt = (tool_tip == null) ? t_("Crop background image") : (!) tool_tip;
39 base (name, tt);
40
41 select_action.connect((self) => {
42 });
43
44 deselect_action.connect ((self) => {
45 });
46
47 press_action.connect((self, b, x, y) => {
48 if (!is_over_rectangle (x, y) || !is_visible) {
49 x1 = x;
50 y1 = y;
51
52 x2 = x;
53 y2 = y;
54
55 is_visible = true;
56 is_set = false;
57 is_done = false;
58 }
59
60 if (is_set && is_visible && is_over_rectangle (x, y)) {
61 do_cut ();
62 is_set = false;
63 is_visible = false;
64 is_done = true;
65
66 x1 = 0;
67 y1 = 0;
68 x2 = 0;
69 y2 = 0;
70 }
71 });
72
73 release_action.connect((self, b, x, y) => {
74 if (!is_set && !is_done) {
75 x2 = x;
76 y2 = y;
77 is_set = true;
78 }
79
80 is_done = false;
81 });
82
83 move_action.connect((self, x, y) => {
84 if (is_visible && !is_set) {
85 x2 = x;
86 y2 = y;
87
88 GlyphCanvas.redraw ();
89 }
90 });
91
92 draw_action.connect ((self, cr, glyph) => {
93 if (is_visible && x1 - x2 != 0 && y1 - y2 != 0) {
94 // draw a border around the selection
95 cr.save ();
96 cr.set_line_width (2.0);
97 Theme.color_opacity (cr, "Foreground 1", 0.3);
98 cr.rectangle (fmin (x1, x2), fmin (y1, y2), fabs (x1 - x2), fabs (y1 - y2));
99 cr.stroke ();
100 cr.restore ();
101
102 // make the area outside of the selection grey
103 cr.save ();
104 cr.set_line_width (0);
105 Theme.color_opacity (cr, "Foreground 1", 0.075);
106 cr.rectangle (0, 0, glyph.allocation.width, fmin (y1, y2));
107 cr.rectangle (0, fmin (y1, y2), fmin (x1, x2), fabs (y1 - y2));
108 cr.rectangle (0, fmin (y1, y2) + fabs (y1 - y2), glyph.allocation.width, glyph.allocation.height - fabs (y1 - y2));
109 cr.rectangle (fmin (x1, x2) + fabs (x1 - x2), fmin (y1, y2), glyph.allocation.width - fmin (x1, x2) - fabs (x1 - x2), glyph.allocation.height);
110 cr.fill ();
111 cr.restore ();
112 }
113 });
114
115 new_image.connect ((file, x, y, w, h) => {
116 Glyph glyph;
117 TabBar tb;
118
119 if (self_destination) {
120 glyph = MainWindow.get_current_glyph ();
121 tb = MainWindow.get_tab_bar ();
122
123 glyph.store_undo_state ();
124
125 glyph.set_background_image (file);
126 tb.select_tab_name (glyph.get_name ());
127
128 glyph.set_background_visible (true);
129 }
130 });
131
132 }
133
134 bool is_over_rectangle (double x, double y) {
135 return fmin (x1, x2) + 1 < x < fmax (x1, x2) - 1 && fmin (y1, y2) + 1 < y < fmax (y1, y2) - 1;
136 }
137
138 public override double get_width () {
139 return fabs (x1 - x2);
140 }
141
142 public override double get_height () {
143 return fabs (y1 - y2);
144 }
145
146 void do_cut () {
147 double x, y;
148 int h, w;
149 double wc, hc;
150
151 Glyph g = MainWindow.get_current_glyph ();
152 BackgroundImage? b = g.get_background_image ();
153 BackgroundImage bg = (!) b;
154
155 ImageSurface img;
156
157 Surface sr;
158 Context cr;
159
160 double tx, ty, vx, vy;
161
162 if (b == null) {
163 return;
164 }
165
166 // Add margin
167 Surface sg = new Surface.similar (bg.get_img (), bg.get_img ().get_content (), bg.size_margin, bg.size_margin);
168 Context cg = new Context (sg);
169
170 wc = bg.get_margin_width ();
171 hc = bg.get_margin_height ();
172
173 Theme.color (cg, "Canvas Background");
174 cg.rectangle (0, 0, bg.size_margin, bg.size_margin);
175 cg.fill ();
176
177 cg.translate (bg.size_margin * 0.5, bg.size_margin * 0.5);
178 cg.rotate (bg.img_rotation);
179 cg.translate (-bg.size_margin * 0.5, -bg.size_margin * 0.5);
180
181 cg.set_source_surface (bg.get_img (), wc, hc);
182 cg.paint ();
183
184 // find start
185 tx = bg.img_offset_x - g.view_offset_x;
186 ty = bg.img_offset_y - g.view_offset_y;
187
188 ty *= g.view_zoom;
189 tx *= g.view_zoom;
190
191 vx = Glyph.path_coordinate_x (tx) - Glyph.path_coordinate_x (fmin (x1, x2));
192 vy = Glyph.path_coordinate_y (ty) - Glyph.path_coordinate_y (fmin (y1, y2));
193
194 x = (int) (vx / bg.img_scale_x);
195 y = (int) (-vy / bg.img_scale_y);
196
197 // do the cut
198 img = bg.get_img ();
199
200 w = (int) (get_width () / g.view_zoom);
201 h = (int) (get_height () / g.view_zoom);
202
203 sr = new Surface.similar (sg, img.get_content (), (int) (w / bg.img_scale_x), (int) (h / bg.img_scale_y));
204 cr = new Context (sr);
205
206 Theme.color (cr, "Canvas Background");
207 cr.rectangle (0, 0, w, h);
208 cr.fill ();
209
210 cr.set_source_surface (sg, x, y);
211 cr.paint ();
212
213 save_img (sr, g, bg);
214
215 cr.restore ();
216 }
217
218 void save_img (Surface sr, Glyph g, BackgroundImage original_bg) {
219 #if ANDROID
220 return; // FIXME: android
221 #else
222 BackgroundImage newbg;
223 Font f = BirdFont.get_current_font ();
224 File img_dir;
225 File img_file;
226 File img_file_next;
227 string fn;
228 double wc, hc;
229 double px, py, w, h;
230
231 img_dir = get_child (f.get_backgrounds_folder (), "parts");
232
233 if (!img_dir.query_exists ()) {
234 if (DirUtils.create_with_parents ((!) img_dir.get_path (), 755) != 0) {
235 warning (@"Can not create directory $((!) img_dir.get_path ())");
236 }
237 }
238
239 img_file = img_dir.get_child (@"NEW_BACKGROUND.png");
240 fn = (!) img_file.get_path ();
241
242 sr.write_to_png (fn);
243
244 newbg = new BackgroundImage (fn);
245
246 fn = newbg.get_sha1 () + ".png";
247
248 img_file_next = img_dir.get_child (fn);
249
250 try {
251 if (img_file_next.query_exists ()) {
252 img_file_next.delete ();
253 }
254
255 img_file.set_display_name (fn);
256 } catch (GLib.Error e) {
257 warning (e.message);
258 return;
259 }
260
261 newbg = new BackgroundImage ((!) f.get_backgrounds_folder ().get_child ("parts").get_child (fn).get_path ());
262
263 // set position for the new background
264 wc = Glyph.path_coordinate_x (0) - Glyph.path_coordinate_x (newbg.get_margin_width ());
265 hc = Glyph.path_coordinate_y (0) - Glyph.path_coordinate_y (newbg.get_margin_height ());
266
267 wc *= g.view_zoom;
268 hc *= g.view_zoom;
269
270 wc *= original_bg.img_scale_x;
271 hc *= original_bg.img_scale_y;
272
273 newbg.img_x = Glyph.path_coordinate_x (fmin (x1, x2)) + wc;
274 newbg.img_y = Glyph.path_coordinate_y (fmin (y1, y2)) + hc;
275
276 newbg.img_scale_x = original_bg.img_scale_x;
277 newbg.img_scale_y = original_bg.img_scale_y;
278
279 px = Glyph.path_coordinate_x (fmin (x1, x2));
280 py = Glyph.path_coordinate_y (fmin (y1, y2));
281 w = Glyph.path_coordinate_x (fmax (x1, x2)) - px;
282 h = Glyph.path_coordinate_y (fmax (y1, y2)) - py;
283
284 new_image (newbg, px, py, w, h);
285 #endif
286 }
287 }
288
289 }
290