The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

CutBackgroundTool.vala in /libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git
Circle boundaries heads/master
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