Clone in a terminal:
Commit b59d831
Johan Mattsson - 2014-07-11T21:43:40+00:00
b59d83157b729b4a7260f10850131b86a406dd72johan.mattsson.m@gmail.com

Rename GlyphBackgroundImage to BackgroundImage

Changed files (11)

Diff

diff --git a/libbirdfont/BackgroundImage.vala b/libbirdfont/BackgroundImage.valanew file mode 100644index 0000000..e728113--- /dev/null+++ b/libbirdfont/BackgroundImage.vala@@ -0,0 +1,847 @@+/*+    Copyright (C) 2012, 2013, 2014 Johan Mattsson++    This library is free software; you can redistribute it and/or modify +    it under the terms of the GNU Lesser General Public License as +    published by the Free Software Foundation; either version 3 of the +    License, or (at your option) any later version.++    This library is distributed in the hope that it will be useful, but +    WITHOUT ANY WARRANTY; without even the implied warranty of +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +    Lesser General Public License for more details.+*/++using Cairo;+using Math;++namespace BirdFont {+	+public class BackgroundImage {+		+	public double img_x = 0;+	public double img_y = 0;+	+	public double img_scale_x = 1;+	public double img_scale_y = 1;+	public double img_rotation = 0;+	private int size = -1;++	public int active_handle = -1;+	public int selected_handle = -1;+	+	private ImageSurface? background_image = null;+	private ImageSurface? original_image = null;+	+	private string path;++	private double contrast = 1.0;+	private bool desaturate = false;+	private double threshold = 1.0;+	+	private bool background_image_is_processing = false;++	public signal void updated ();++	public double img_offset_x {+		get { return img_x + Glyph.xc (); }+		set { img_x = value - Glyph.xc (); }+	}++	public double img_offset_y {+		get { return Glyph.yc () - img_y; }+		set { img_y = Glyph.yc () - value; }+	}+	+	public int size_margin {+		get {+			if (unlikely (size == -1)) {+				size = (int) (Math.sqrt (Math.pow (get_img ().get_height (), 2) +					+ Math.pow (get_img ().get_width (), 2)) + 0.5);+			}+			+			return size;+		}+	}+		+	public BackgroundImage (string file_name) {+		path = file_name;+	}++	public BackgroundImage copy () {+		BackgroundImage bg = new BackgroundImage (path);+			+		bg.img_x = img_x;+		bg.img_y = img_y;++		bg.img_scale_x = img_scale_x;+		bg.img_scale_y = img_scale_y;+		bg.img_rotation = img_rotation;++		bg.contrast = contrast;+		bg.desaturate = desaturate;+		bg.threshold = threshold;++		return bg;+		+	}++	public double get_margin_width () {+		return ((size_margin - get_img ().get_width ()) / 2.0);+	}++	public double get_margin_height () {+		return ((size_margin - get_img ().get_height ()) / 2.0);+	}++	public void set_img_offset (double x, double y) {+		img_offset_x = x;+		img_offset_y = y;+	}++	public void set_position (double coordinate_x, double coordinate_y) {+		img_x = coordinate_x;+		img_y = coordinate_y;+	}+	+	public void load () {+		get_img ();+	}+			+	public ImageSurface get_img () {+		if (!path.has_suffix (".png")) {+			create_png ();+		}+		+		if (background_image == null) {+			background_image = new ImageSurface.from_png (path);+			original_image = new ImageSurface.from_png (path);+		}+		+		return (!) background_image;+	}++	public bool is_valid () {+		FileInfo file_info;+		File file = File.new_for_path (path);+	+		if (!file.query_exists ()) {+			return false;+		}+		+		try {+			file_info = file.query_info ("*", FileQueryInfoFlags.NONE);+			+			if (file_info.get_size () == 0) {+				return false;+			}+		} catch (GLib.Error e) {+			warning (e.message);+			return false;+		}+				+		return true;+	}++	public string get_png_base64 () {+		try {+			File file = File.new_for_path (path);+			FileInfo file_info = file.query_info ("*", FileQueryInfoFlags.NONE);+			uint8[] buffer = new uint8[file_info.get_size ()];+			FileInputStream file_stream;+			DataInputStream png_stream;+			+			if (!file.query_exists ()) {+				warning (@"Can't to save image $path, file does not exist.");+				return "";+			}+			+			if (is_null (buffer)) {+				warning (@"Can not allocate a buffer of $(file_info.get_size ()) bytes to store $path.");+				return "";+			}+			+			file_stream = file.read ();+			+			png_stream = new DataInputStream (file_stream);+			png_stream.read (buffer);+			+			return Base64.encode (buffer);+		} catch (GLib.Error e) {+			warning (e.message);+		}+		+		return "";++	}++	public void create_background_folders (Font font) {+		File dir;+		+		dir = BirdFont.get_settings_directory ();+		if (!dir.query_exists ()) {+			DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+		}+		+		dir = font.get_backgrounds_folder ();+		if (!dir.query_exists ()) {+			DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+		}++		dir = font.get_backgrounds_folder ().get_child ("parts");+		if (!dir.query_exists ()) {+			DirUtils.create ((!) dir.get_path (), 0xFFFFFF);+		}+	}++	public void copy_if_new (File destination) {+		if (!destination.query_exists ()) {+			copy_file (destination);+		}+	}++	public void copy_file (File destination) {+		File source;+		FileInfo info;+		+		try {+			if (destination.query_exists ()) {+				info = destination.query_info ("standard::*", FileQueryInfoFlags.NONE);+				if (info.get_file_type () == FileType.DIRECTORY) {+					warning (@"$((!) destination.get_path ()) is a directory.");+					return;+				}+			}+			+			if (!((!)destination.get_parent ()).query_exists ()) {+				warning (@"Directory for file $((!) destination.get_path ()) is not created.");+				return;+			}+			+			if (destination.query_exists ()) {+				warning (@"Image $((!) destination.get_path ()) is already created.");+				return;+			}+			+			source = File.new_for_path (path);+			source.copy (destination, FileCopyFlags.NONE);+		} catch (GLib.Error e) {+			warning (e.message);+		}+	}++	public string get_sha1 () {+		try {+			File file = File.new_for_path (path);+			FileInfo file_info;+			uint8[] buffer;+			FileInputStream file_stream;+			DataInputStream png_stream;+			+			if (!file.query_exists ()) {+				warning (@"Can't save $path file does not exist.");+				return "";+			}+			+			file_info = file.query_info ("*", FileQueryInfoFlags.NONE);+			+			if (file_info.get_size () == 0) {+				warning (@"length of image $path is zero");+				return "";+			}+			+			buffer = new uint8[file_info.get_size ()];+			file_stream = file.read ();+			png_stream = new DataInputStream (file_stream);++			png_stream.read (buffer);+			+			return Checksum.compute_for_data (ChecksumType.SHA1, buffer);+		} catch (GLib.Error e) {+			warning (e.message);+		}+		+		return "";+	}+	+	private void create_png () {+		string file_name = @"$path.png";+		Font font = BirdFont.get_current_font ();+		File folder = font.get_backgrounds_folder ();+		File original = File.new_for_path (file_name);+		File png_image = folder.get_child (@"full_$((!)original.get_basename ())");+		bool converted;+		+		if (png_image.query_exists ()) {+			path = (!) png_image.get_path ();+			return;+		}+		+		if (is_null (path)) {+			warning ("Background image path is null.");+			return;+		}+		+		converted = MainWindow.native_window.convert_to_png (path, (!) png_image.get_path ());+		+		if (!converted) {+			warning ("Failed to convert image: $(path)");+		}+		+		path = (!) png_image.get_path ();+	}+	+	public void set_img_rotation_from_coordinate (double x, double y) {+		double bcx, bcy;+		double a, b, c, length;++		img_center (out bcx, out bcy);++		a = bcx - x;+		b = bcy - y;+		c = a * a + b * b;+		+		if (c == 0) {+			return;+		}+		+		length = sqrt (fabs (c));	+		+		if (c < 0) {+			length = -length;+		}+	+		img_rotation =  (y < bcy) ? acos (a / length) + PI : -acos (a / length) + PI;+	}+	+	public void set_img_scale (double xs, double ys) {+		img_scale_x = xs;+		img_scale_y = ys;+	}++	public void reset_scale (Glyph g) {+		double w, h;++		w = g.get_width ();+		h = g.get_height ();++		img_scale_x = 1;			+		img_scale_y = 1;+		+		img_offset_x = g.get_line ("left").pos;+		img_offset_y = g.get_line ("top").pos;		+	}++	public void draw (Context cr, WidgetAllocation allocation, double view_offset_x, double view_offset_y, double view_zoom) {+		double x, y;+		double iw, ih;+		int h, w;+		double scale_x, scale_y;+		double oy, ox;+				+		Surface s;+		Context c;++		Surface sg;+		Context cg;++		Surface st;+		Context ct;++		double wc, hc;++		if (unlikely (get_img ().status () != Cairo.Status.SUCCESS)) {+			warning (@"Background image is invalid. (\"$path\")\n");+			MainWindow.get_current_glyph ().set_background_visible (false);+			return;+		}+		+		// add margin+		sg = new Surface.similar (get_img (), get_img ().get_content (), size_margin, size_margin);+		cg = new Context (sg);+		+		wc = get_margin_width ();+		hc = get_margin_height ();+		+		cg.set_source_rgba (1, 1, 1, 1);+		cg.rectangle (0, 0, size_margin, size_margin);+		cg.fill ();+		+		cg.set_source_surface (get_img (), wc, hc);+		cg.paint ();++		x = Glyph.reverse_path_coordinate_x (img_offset_x);+		y = Glyph.reverse_path_coordinate_y (img_offset_y);+		+		ih = (int) get_img ().get_height ();+		iw = (int) get_img ().get_width ();+		+		w = (int) iw;+		h = (int) ih;++		oy = size_margin;+		ox = size_margin;++		// rotate image+		s = new Surface.similar (sg, sg.get_content (), (int) (ox), (int) (oy));+		c = new Context (s);+	+		c.save ();++		c.set_source_rgba (1, 1, 1, 1);+		c.rectangle (0, 0, size_margin, size_margin);+		c.fill ();+			+   		c.translate (size_margin * 0.5, size_margin * 0.5);+		c.rotate (img_rotation);+		c.translate (-size_margin * 0.5, -size_margin * 0.5);++		c.set_source_surface (cg.get_target (), 0, 0);+		c.paint ();	+		c.restore ();++		// mask+		st = new Surface.similar (s, s.get_content (), allocation.width, allocation.height);+		ct = new Context (st);+		ct.save ();++		ct.set_source_rgba (1, 1, 1, 1);+		ct.rectangle (0, 0, allocation.width, allocation.height);+		ct.fill ();++		// scale both canvas and image at the same time+		scale_x = view_zoom * img_scale_x;+		scale_y = view_zoom * img_scale_y;+		+		ct.scale (scale_x, scale_y);+		ct.translate (-view_offset_x / img_scale_x, -view_offset_y / img_scale_y);+		+		ct.set_source_surface (s, img_offset_x / img_scale_x, img_offset_y / img_scale_y);++		ct.paint ();+		ct.restore ();+		+		// add it+		cr.save ();+		cr.set_source_surface (st, 0, 0);+		cr.paint ();+		cr.restore ();+	}+	+	public void handler_release (double nx, double ny) {		+		selected_handle = 0;+		handler_move (nx, ny);+	}+			+	public void handler_press (double nx, double ny) {		+		if (is_over_rotate (nx, ny)) {+			selected_handle = 2;+		} else if (is_over_resize (nx, ny)) {+			selected_handle = 1;+		} else {+			selected_handle = 0;+		}	+	}++	void img_center (out double x, out double y) {+		Glyph g = MainWindow.get_current_glyph ();+		+		x = img_offset_x - g.view_offset_x + (size_margin / 2) * img_scale_x;+		y = img_offset_y - g.view_offset_y + (size_margin / 2) * img_scale_y;++		y *= g.view_zoom;+		x *= g.view_zoom;+	}++	bool is_over_rotate (double nx, double ny) {+		double x, y, d;++		img_center (out x, out y);++		x += cos (img_rotation) * 75;+		y += sin (img_rotation) * 75;++		d = sqrt (pow (x - nx, 2) + pow (y - ny, 2));+		+		return d < 10;+	}++	bool is_over_resize (double nx, double ny) {+		Glyph g = MainWindow.get_current_glyph ();+		double x, y, size;+		bool inx, iny;++		size = 12 * g.view_zoom;++		x = img_offset_x - g.view_offset_x;+		y = img_offset_y - g.view_offset_y + (size_margin) * img_scale_y;+				+		y *= g.view_zoom;+		x *= g.view_zoom;+				+		inx = x - size <= nx <= x + size;+		iny = y - size <= ny <= y + size;+		+		return inx && iny;+	}++	public void handler_move (double nx, double ny) {+		int prev_handle = active_handle;++		if (is_over_rotate (nx, ny)) {+			active_handle = 2;+		} else if (is_over_resize (nx, ny)) {+			active_handle = 1;+		} else {+			active_handle = 0;+		}+		+		if (prev_handle != active_handle) {+			GlyphCanvas.redraw ();+		}+	}+	+	public void draw_handle (Context cr, Glyph g) {+		draw_resize_handle (cr, g);+		draw_rotate_handle (cr, g);+	}++	public void draw_resize_handle (Context cr, Glyph g) {+		double x, y;+		cr.save ();+		+		cr.set_source_rgba (1, 0, 0.3, 1);++		x = img_offset_x - g.view_offset_x;+		y = img_offset_y - g.view_offset_y + (size_margin) * img_scale_y;+				+		y *= g.view_zoom;+		x *= g.view_zoom;+				+		draw_handle_triangle (x, y, cr, g, 6);+		+		cr.restore ();+	}+		+	public void draw_rotate_handle (Context cr, Glyph g) {+		double x, y, hx, hy, x2, y2;+		+		double ivz = 1.0 / (g.view_zoom);+		+		cr.save ();+		+		cr.scale (g.view_zoom, g.view_zoom);+		+		if (selected_handle == 2) cr.set_source_rgba (1, 0, 0.3, 1);+		else if (active_handle == 2) cr.set_source_rgba (0, 0, 0.3, 1);+		else cr.set_source_rgba (0.7, 0.7, 0.8, 1);++		x = img_offset_x - g.view_offset_x + (size_margin / 2) * img_scale_x;+		y = img_offset_y - g.view_offset_y + (size_margin / 2) * img_scale_y;+				+		cr.rectangle (x, y, 5 * ivz, 5 * ivz);+		cr.fill ();++		hx = cos (img_rotation) * 75 * ivz;+		hy = sin (img_rotation) * 75 * ivz;+		+		x2 = x + hx;+		y2 = y + hy;++		cr.rectangle (x2, y2, 5 * ivz, 5 * ivz);+		cr.fill ();++		cr.set_line_width (ivz);+		cr.move_to (x + 2.5* ivz, y + 2.5* ivz);+		cr.line_to (x2 + 2.5* ivz, y2 + 2.5* ivz);+		cr.stroke ();+										+		cr.restore ();+	}++	void draw_handle_triangle (double x, double y, Context cr, Glyph g, int direction, double s = 1) +		requires (0 < direction < 8)+	{+		double ivz = 1.0 / (g.view_zoom);+		double size;+		+		cr.save ();+		cr.set_line_width (ivz);+		+		if (selected_handle == 1) cr.set_source_rgba (1, 0, 0.3, 1);+		else if (active_handle == 1) cr.set_source_rgba (0, 0, 0.3, 1);+		else cr.set_source_rgba (0.7, 0.7, 0.8, 1);	++		size = (8) * s;+		+		cr.scale (1, 1);+		cr.new_path ();+		+		// up + left+		if (direction == 1) {+			cr.move_to (x - size, y - size);+			cr.line_to (x + size, y - size);+			cr.line_to (x - size, y + size);	+		}+		+		if (direction == 6) {+			cr.move_to (x + size, y + size);	+			cr.line_to (x - size, y + size);+			cr.line_to (x - size, y - size);	+		}+		+		cr.close_path();+		cr.fill ();++		cr.restore ();+		+	}++	private static uchar ftu (double c) {+		if (c < 0) return 0;+		if (c > 255) return 255;+		return (uchar) c;+	}++	private static ImageSurface? process_background_colors (ImageSurface img, double contrast, bool desaturate, double threshold) {+		int i;+		unowned uchar[]? pix_buff_t;+		unowned uchar[] pix_buff;+		int len;+		uchar[] contrast_img;+		ImageSurface contrast_surface;+		double c, thres;+		uchar nc;++		int w = img.get_width();+		int h = img.get_height();++		if (unlikely (img.status () != Cairo.Status.SUCCESS)) {+			warning ("Err");+			return null;+		}+		+		len = 4 * w * h;+		+		pix_buff_t = img.get_data ();+		return_val_if_fail (pix_buff_t != null, null);+		pix_buff = pix_buff_t;+		+		contrast_img = new uchar[len];++		pix_buff_t = contrast_img;+		return_val_if_fail (pix_buff_t != null, null);+		+		contrast += 70;+		contrast /= 71;+		+		if (desaturate) {+			i = 0;+			+			while (i < len) {+				c = (pix_buff[i] + pix_buff[i+1] + pix_buff[i+2]) / 3.0;+				nc = ftu (Math.pow (c, contrast));++				contrast_img[i]   = nc;+				contrast_img[i+1] = nc; +				contrast_img[i+2] = nc;+				contrast_img[i+3] = 255;+				+				i += 4;+			}+		} else {+			i = 0;+			while (i < len) {+				contrast_img[i] = ftu (Math.pow (pix_buff[i], contrast) - Math.pow (100, contrast) + 100);+				contrast_img[i+1] = ftu (Math.pow (pix_buff[i+1], contrast) - Math.pow (100, contrast) + 100);+				contrast_img[i+2] = ftu (Math.pow (pix_buff[i+2], contrast) - Math.pow (100, contrast) + 100);+				contrast_img[i+3] = 255;+				+				i += 4;+			}+		}++		if (true) {+			thres = (threshold - 0.5) * 255;+			+			i = 0;+			while (i < len) {+				c = (pix_buff[i] + pix_buff[i+1] + pix_buff[i+2]) / 3.0;+				nc = (c > thres) ? 255 : 0;++				contrast_img[i]   = nc;+				contrast_img[i+1] = nc; +				contrast_img[i+2] = nc;+				contrast_img[i+3] = 255;+				+				i += 4;+			}+		}++		contrast_surface = new ImageSurface.for_data (contrast_img, img.get_format(), w, h, img.get_stride());++		if (unlikely (contrast_surface.status () != Cairo.Status.SUCCESS)) {+			warning ("Err");+			return null;+		}+		+		contrast_surface.mark_dirty	();+			+		return contrast_surface;	+	}++	async ImageSurface? do_process_background_colors () {+		SourceFunc callback = do_process_background_colors.callback;+		ImageSurface? output = null;+		double contrast = get_contrast ();+		bool desaturate = get_desaturate_background ();+		double threshold = this.threshold;++		if (original_image == null) {+			return null;+		}+		+		// TODO: Add multi threading+		output = process_background_colors ((!)original_image, contrast, desaturate, threshold);+		Idle.add ((owned) callback);++		return output;+	}++	public void update_background (double contrast, bool desaturate) {+		background_image_is_processing = true;+		+		do_process_background_colors.begin ((obj, res) => {+			ImageSurface? img;++			img = do_process_background_colors.end (res);+			+			if (img != null) {+				background_image = (!) img;+				updated ();+			} else {+				stderr.printf ("Error: No background image");	+			}+			+			// update image if new value was set while background task was running +			if (get_contrast () != contrast || get_desaturate_background () != desaturate) {++				do_process_background_colors.begin ((obj, res) => {+					img = do_process_background_colors.end (res);+					if (likely (img != null)) {+						background_image = (!) img;+						updated ();+					} else {+						stderr.printf ("Error: No background image");	+					}+				});+			}++			background_image_is_processing = false;+			updated ();+			+		});+	}++	public double get_contrast () {+		double bc;+		lock (contrast) {+			bc = contrast;+		}+		return bc;+	}+		+	public bool get_desaturate_background () {+		bool bw;+		lock (desaturate) {+			bw = desaturate;+		}+		return bw;+	}+	+	public void set_desaturate_background (bool bw) {+		lock (desaturate) {+			desaturate = bw;+		}+	+		if (background_image_is_processing) {+			return;+		}+		+		background_image_is_processing = true;+		+		update_background (get_contrast (), bw);+	}+	+	public void set_threshold (double t) {+		threshold = t;+	}+	+	public Path auto_trace () {+		ImageSurface img;+		int len, w, h, i;+		unowned uchar[] pix_buff;+		Path p = new Path ();+		double x, y;++		if (background_image == null) {+			return p;+		}++		img = (!) background_image;++		w = img.get_width();+		h = img.get_height();++		if (unlikely (img.status () != Cairo.Status.SUCCESS)) {+			warning ("Error");+			return p;+		}+		+		len = 4 * w * h;++		pix_buff = img.get_data ();++		i = 0;+		while (i < len - 4) {+			if (is_edge (pix_buff[i], pix_buff[i + 4])) {+				y = i / w;+				x = i - y;+				+				p.add (x, y);+				+				if (p.points.size > 10) {+					return p;+				}+			}+			+			i += 4;+		}+		+		return p;+	}+	+	private bool is_edge (uchar a, uchar b) {+		return (a == 255 && b == 0);+	}+	+	public void set_contrast (double contrast) {+		lock (this.contrast) {+			this.contrast = contrast;+		}+		+		if (background_image_is_processing) {+			return;+		}+		+		background_image_is_processing = true;+		+		update_background (contrast, get_desaturate_background ());+	}+	+	public void center_in_glyph () {+		Glyph g = MainWindow.get_current_glyph ();+	}+}+	+}diff --git a/libbirdfont/BackgroundSelection.vala b/libbirdfont/BackgroundSelection.valaindex 61e4e3a..59f2f93 100644--- a/libbirdfont/BackgroundSelection.vala+++ b/libbirdfont/BackgroundSelection.vala@@ -90,7 +90,7 @@ class BackgroundSelection : FontDisplay { 		ImageSurface img_surface; 		ImageSurface thumbnail_surface; 		Cairo.Status status;-		GlyphBackgroundImage background_image;+		BackgroundImage background_image; 		Context cr; 		double ratio; 		Font font = BirdFont.get_current_font ();@@ -105,7 +105,7 @@ class BackgroundSelection : FontDisplay { 		} 		 		if (!thumbnail_image.query_exists ()) {-			background_image = new GlyphBackgroundImage ((!) original.get_path ());+			background_image = new BackgroundImage ((!) original.get_path ()); 			img_surface = background_image.get_img (); 			thumbnail_surface = new ImageSurface (Format.ARGB32, 100, 100); 			ratio = Math.fmin (100.0 / img_surface.get_width (), 100.0 / img_surface.get_height ());@@ -189,7 +189,7 @@ class BackgroundSelection : FontDisplay { 	private void select_image () { 		Font font; 		string file;-		GlyphBackgroundImage bg;+		BackgroundImage bg; 		TabBar tb; 		Tool zoom_background; @@ -200,7 +200,7 @@ class BackgroundSelection : FontDisplay { 		} 		 		file = background_images.get (active_box - 1);-		bg = new GlyphBackgroundImage (file);+		bg = new BackgroundImage (file); 		tb = MainWindow.get_tab_bar (); 		 		bg.reset_scale (glyph);diff --git a/libbirdfont/BackgroundTool.vala b/libbirdfont/BackgroundTool.valaindex 80bcabf..be6dc2a 100644--- a/libbirdfont/BackgroundTool.vala+++ b/libbirdfont/BackgroundTool.vala@@ -32,7 +32,7 @@ public class BackgroundTool : Tool { 		 	bool move_bg; 	-	static GlyphBackgroundImage imported_background;+	static BackgroundImage imported_background; 	 	public BackgroundTool (string name) { 		base (name, t_("Move, resize and rotate background image"));@@ -40,7 +40,7 @@ public class BackgroundTool : Tool { 		top_limit = 0; 		bottom_limit = 0; -		imported_background = new GlyphBackgroundImage ("");+		imported_background = new BackgroundImage ("");  		select_action.connect((self) => { 		});@@ -50,8 +50,8 @@ public class BackgroundTool : Tool { 		 		press_action.connect((self, b, x, y) => { 			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? bg = g.get_background_image ();-			GlyphBackgroundImage background;+			BackgroundImage? bg = g.get_background_image ();+			BackgroundImage background; 			 			if (bg == null) { 				return;@@ -79,7 +79,7 @@ public class BackgroundTool : Tool {  		release_action.connect((self, b, x, y) => { 			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? bg = g.get_background_image ();+			BackgroundImage? bg = g.get_background_image (); 			 			if (bg == null) { 				return;@@ -99,7 +99,7 @@ public class BackgroundTool : Tool { 		 		draw_action.connect ((self, cairo_context, glyph) => { 			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? background_image = g.get_background_image ();+			BackgroundImage? background_image = g.get_background_image (); 			if (background_image == null) return; 			 			((!) background_image).draw_handle (cairo_context, glyph);@@ -108,8 +108,8 @@ public class BackgroundTool : Tool { 		 	void move (double x, double y) { 		Glyph g = MainWindow.get_current_glyph ();-		GlyphBackgroundImage? background_image = g.get_background_image ();-		GlyphBackgroundImage bg = (!) background_image;+		BackgroundImage? background_image = g.get_background_image ();+		BackgroundImage bg = (!) background_image; 		 		double xscale, yscale, dx, dy; @@ -169,7 +169,7 @@ public class BackgroundTool : Tool { 	}  	internal static void import_background_image () {-		GlyphBackgroundImage bg;+		BackgroundImage bg; 		string? fn; 		string path; 		@@ -177,7 +177,7 @@ public class BackgroundTool : Tool { 		 		if (fn != null) { 			path = (!) fn;-			bg = new GlyphBackgroundImage (path);+			bg = new BackgroundImage (path); 			imported_background = bg; 			MainWindow.native_window.load_background_image (); 		}diff --git a/libbirdfont/BirdFontFile.vala b/libbirdfont/BirdFontFile.valaindex 6f27974..3c23e10 100644--- a/libbirdfont/BirdFontFile.vala+++ b/libbirdfont/BirdFontFile.vala@@ -159,7 +159,7 @@ class BirdFontFile : GLib.Object { 			}); 		 			font.glyph_cache.for_each ((gc) => {-				GlyphBackgroundImage bg;+				BackgroundImage bg; 				 				try { 					string data;@@ -539,8 +539,8 @@ class BirdFontFile : GLib.Object { 	} 	 	private void write_glyph_background (Glyph g, DataOutputStream os) throws GLib.Error {-		GlyphBackgroundImage? bg;-		GlyphBackgroundImage background_image;+		BackgroundImage? bg;+		BackgroundImage background_image; 		double pos_x, pos_y, scale_x, scale_y, rotation; 		 		bg = g.get_background_image ();@@ -1488,8 +1488,8 @@ class BirdFontFile : GLib.Object { 	} 	 	private void parse_background_scale (Glyph g, Xml.Node* node) {-		GlyphBackgroundImage img;-		GlyphBackgroundImage? new_img = null;+		BackgroundImage img;+		BackgroundImage? new_img = null; 		 		string attr_name = ""; 		string attr_content;@@ -1507,7 +1507,7 @@ class BirdFontFile : GLib.Object { 					warning (@"Background file has not been created yet. $((!) img_file.get_path ())"); 				} 				-				new_img = new GlyphBackgroundImage ((!) img_file.get_path ());+				new_img = new BackgroundImage ((!) img_file.get_path ()); 				g.set_background_image ((!) new_img); 			} 		}diff --git a/libbirdfont/BirdFontPart.vala b/libbirdfont/BirdFontPart.valaindex daba4a5..bfe2a13 100644--- a/libbirdfont/BirdFontPart.vala+++ b/libbirdfont/BirdFontPart.vala@@ -162,7 +162,7 @@ class BirdFontPart : GLib.Object{ 		FileEnumerator e; 		string name; 		File image_dir;-		GlyphBackgroundImage bg;+		BackgroundImage bg; 		File found; 		File dest; 		@@ -188,7 +188,7 @@ class BirdFontPart : GLib.Object{ 				if (name.has_suffix (".png")) { 					found = image_dir.get_child (name); 					dest = font.get_backgrounds_folder ().get_child ("parts").get_child (name);-					bg = new GlyphBackgroundImage ((!) found.get_path ());+					bg = new BackgroundImage ((!) found.get_path ()); 					bg.create_background_folders (font); 					bg.copy_if_new (dest); 				}@@ -228,7 +228,7 @@ class BirdFontPart : GLib.Object{ 	void write_glyph_background_image (BirdFontFile bf, GlyphCollection gc, Glyph g) throws GLib.Error { 		string file_name; 		string dir_name;-		GlyphBackgroundImage bg;+		BackgroundImage bg; 		File file; 		 		if (g.get_background_image () != null) {diff --git a/libbirdfont/CutBackgroundTool.vala b/libbirdfont/CutBackgroundTool.valaindex d27d409..5ad4635 100644--- a/libbirdfont/CutBackgroundTool.vala+++ b/libbirdfont/CutBackgroundTool.vala@@ -28,7 +28,7 @@ class CutBackgroundTool : Tool { 	bool is_set = false; 	bool is_done = false; 		-	public signal void new_image (GlyphBackgroundImage file);+	public signal void new_image (BackgroundImage file); 	 	public CutBackgroundTool (string name) { 		base (name, t_("Crop background image"));@@ -139,8 +139,8 @@ class CutBackgroundTool : Tool { 		double wc, hc; 		 		Glyph g = MainWindow.get_current_glyph ();-		GlyphBackgroundImage? b = g.get_background_image ();-		GlyphBackgroundImage bg = (!) b;+		BackgroundImage? b = g.get_background_image ();+		BackgroundImage bg = (!) b; 		 		ImageSurface img; 		@@ -205,11 +205,11 @@ class CutBackgroundTool : Tool { 		cr.restore (); 	} -	void save_img (Surface sr, Glyph g, GlyphBackgroundImage original_bg) {+	void save_img (Surface sr, Glyph g, BackgroundImage original_bg) { #if ANDROID 	return; // FIXME: android #else-		GlyphBackgroundImage newbg;+		BackgroundImage newbg; 		Font f = BirdFont.get_current_font (); 		File img_dir; 		File img_file;@@ -228,7 +228,7 @@ class CutBackgroundTool : Tool { 		 		sr.write_to_png (fn); 		-		newbg = new GlyphBackgroundImage (fn);+		newbg = new BackgroundImage (fn); 		 		fn = newbg.get_sha1 () + ".png"; @@ -245,7 +245,7 @@ class CutBackgroundTool : Tool { 			return; 		} 		-		newbg = new GlyphBackgroundImage ((!) f.get_backgrounds_folder ().get_child ("parts").get_child (fn).get_path ());+		newbg = new BackgroundImage ((!) f.get_backgrounds_folder ().get_child ("parts").get_child (fn).get_path ()); 			 		// set position for the new background 		wc = Glyph.path_coordinate_x (0) - Glyph.path_coordinate_x (newbg.get_margin_width ());	diff --git a/libbirdfont/DrawingTools.vala b/libbirdfont/DrawingTools.valaindex 3de382d..5e00893 100644--- a/libbirdfont/DrawingTools.vala+++ b/libbirdfont/DrawingTools.vala@@ -510,7 +510,7 @@ public class DrawingTools : ToolCollection  { 		background_scale.select_action.connect((self) => { 			SpinButton sb = (SpinButton) self; 			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? img = g.get_background_image ();+			BackgroundImage? img = g.get_background_image (); 			double s = sb.get_value (); 			 			if (img != null) {@@ -571,8 +571,8 @@ public class DrawingTools : ToolCollection  { 		 		background_contrast.select_action.connect ((self) => {		 			Glyph g = MainWindow.get_current_glyph ();-			GlyphBackgroundImage? bg = g.get_background_image ();-			GlyphBackgroundImage b;+			BackgroundImage? bg = g.get_background_image ();+			BackgroundImage b; 			 			if (bg != null) { 				b = (!) bg;diff --git a/libbirdfont/Glyph.vala b/libbirdfont/Glyph.valaindex 70c6ac7..a52b2ea 100644--- a/libbirdfont/Glyph.vala+++ b/libbirdfont/Glyph.vala@@ -19,7 +19,7 @@ namespace BirdFont {  public class Glyph : FontDisplay { 	// Background image-	GlyphBackgroundImage? background_image = null;+	BackgroundImage? background_image = null; 	bool background_image_visible = true; 	 	// Glyph zoom level@@ -303,8 +303,8 @@ public class Glyph : FontDisplay { 		view_offset_y -= b - d; 	} -	public void set_background_image (GlyphBackgroundImage? b) {-		GlyphBackgroundImage bg;+	public void set_background_image (BackgroundImage? b) {+		BackgroundImage bg; 		 		if (b == null) { 			background_image = null;@@ -316,7 +316,7 @@ public class Glyph : FontDisplay { 		BirdFont.get_current_font ().touch (); 	} 	-	public GlyphBackgroundImage? get_background_image () {+	public BackgroundImage? get_background_image () { 		return (!) background_image; 	} 		diff --git a/libbirdfont/GlyphBackgroundImage.vala b/libbirdfont/GlyphBackgroundImage.valadeleted file mode 100644index 7f0361c..0000000--- a/libbirdfont/GlyphBackgroundImage.vala+++ /dev/null@@ -1,847 +0,0 @@-/*-    Copyright (C) 2012, 2013, 2014 Johan Mattsson--    This library is free software; you can redistribute it and/or modify -    it under the terms of the GNU Lesser General Public License as -    published by the Free Software Foundation; either version 3 of the -    License, or (at your option) any later version.--    This library is distributed in the hope that it will be useful, but -    WITHOUT ANY WARRANTY; without even the implied warranty of -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -    Lesser General Public License for more details.-*/--using Cairo;-using Math;--namespace BirdFont {-	-public class GlyphBackgroundImage {-		-	public double img_x = 0;-	public double img_y = 0;-	-	public double img_scale_x = 1;-	public double img_scale_y = 1;-	public double img_rotation = 0;-	private int size = -1;--	public int active_handle = -1;-	public int selected_handle = -1;-	-	private ImageSurface? background_image = null;-	private ImageSurface? original_image = null;-	-	private string path;--	private double contrast = 1.0;-	private bool desaturate = false;-	private double threshold = 1.0;-	-	private bool background_image_is_processing = false;--	public signal void updated ();--	public double img_offset_x {-		get { return img_x + Glyph.xc (); }-		set { img_x = value - Glyph.xc (); }-	}--	public double img_offset_y {-		get { return Glyph.yc () - img_y; }-		set { img_y = Glyph.yc () - value; }-	}-	-	public int size_margin {-		get {-			if (unlikely (size == -1)) {-				size = (int) (Math.sqrt (Math.pow (get_img ().get_height (), 2) -					+ Math.pow (get_img ().get_width (), 2)) + 0.5);-			}-			-			return size;-		}-	}-		-	public GlyphBackgroundImage (string file_name) {-		path = file_name;-	}--	public GlyphBackgroundImage copy () {-		GlyphBackgroundImage bg = new GlyphBackgroundImage (path);-			-		bg.img_x = img_x;-		bg.img_y = img_y;--		bg.img_scale_x = img_scale_x;-		bg.img_scale_y = img_scale_y;-		bg.img_rotation = img_rotation;--		bg.contrast = contrast;-		bg.desaturate = desaturate;-		bg.threshold = threshold;--		return bg;-		-	}--	public double get_margin_width () {-		return ((size_margin - get_img ().get_width ()) / 2.0);-	}--	public double get_margin_height () {-		return ((size_margin - get_img ().get_height ()) / 2.0);-	}--	public void set_img_offset (double x, double y) {-		img_offset_x = x;-		img_offset_y = y;-	}--	public void set_position (double coordinate_x, double coordinate_y) {-		img_x = coordinate_x;-		img_y = coordinate_y;-	}-	-	public void load () {-		get_img ();-	}-			-	public ImageSurface get_img () {-		if (!path.has_suffix (".png")) {-			create_png ();-		}-		-		if (background_image == null) {-			background_image = new ImageSurface.from_png (path);-			original_image = new ImageSurface.from_png (path);-		}-		-		return (!) background_image;-	}--	public bool is_valid () {-		FileInfo file_info;-		File file = File.new_for_path (path);-	-		if (!file.query_exists ()) {-			return false;-		}-		-		try {-			file_info = file.query_info ("*", FileQueryInfoFlags.NONE);-			-			if (file_info.get_size () == 0) {-				return false;-			}-		} catch (GLib.Error e) {-			warning (e.message);-			return false;-		}-				-		return true;-	}--	public string get_png_base64 () {-		try {-			File file = File.new_for_path (path);-			FileInfo file_info = file.query_info ("*", FileQueryInfoFlags.NONE);-			uint8[] buffer = new uint8[file_info.get_size ()];-			FileInputStream file_stream;-			DataInputStream png_stream;-			-			if (!file.query_exists ()) {-				warning (@"Can't to save image $path, file does not exist.");-				return "";-			}-			-			if (is_null (buffer)) {-				warning (@"Can not allocate a buffer of $(file_info.get_size ()) bytes to store $path.");-				return "";-			}-			-			file_stream = file.read ();-			-			png_stream = new DataInputStream (file_stream);-			png_stream.read (buffer);-			-			return Base64.encode (buffer);-		} catch (GLib.Error e) {-			warning (e.message);-		}-		-		return "";--	}--	public void create_background_folders (Font font) {-		File dir;-		-		dir = BirdFont.get_settings_directory ();-		if (!dir.query_exists ()) {-			DirUtils.create ((!) dir.get_path (), 0xFFFFFF);-		}-		-		dir = font.get_backgrounds_folder ();-		if (!dir.query_exists ()) {-			DirUtils.create ((!) dir.get_path (), 0xFFFFFF);-		}--		dir = font.get_backgrounds_folder ().get_child ("parts");-		if (!dir.query_exists ()) {-			DirUtils.create ((!) dir.get_path (), 0xFFFFFF);-		}-	}--	public void copy_if_new (File destination) {-		if (!destination.query_exists ()) {-			copy_file (destination);-		}-	}--	public void copy_file (File destination) {-		File source;-		FileInfo info;-		-		try {-			if (destination.query_exists ()) {-				info = destination.query_info ("standard::*", FileQueryInfoFlags.NONE);-				if (info.get_file_type () == FileType.DIRECTORY) {-					warning (@"$((!) destination.get_path ()) is a directory.");-					return;-				}-			}-			-			if (!((!)destination.get_parent ()).query_exists ()) {-				warning (@"Directory for file $((!) destination.get_path ()) is not created.");-				return;-			}-			-			if (destination.query_exists ()) {-				warning (@"Image $((!) destination.get_path ()) is already created.");-				return;-			}-			-			source = File.new_for_path (path);-			source.copy (destination, FileCopyFlags.NONE);-		} catch (GLib.Error e) {-			warning (e.message);-		}-	}--	public string get_sha1 () {-		try {-			File file = File.new_for_path (path);-			FileInfo file_info;-			uint8[] buffer;-			FileInputStream file_stream;-			DataInputStream png_stream;-			-			if (!file.query_exists ()) {-				warning (@"Can't save $path file does not exist.");-				return "";-			}-			-			file_info = file.query_info ("*", FileQueryInfoFlags.NONE);-			-			if (file_info.get_size () == 0) {-				warning (@"length of image $path is zero");-				return "";-			}-			-			buffer = new uint8[file_info.get_size ()];-			file_stream = file.read ();-			png_stream = new DataInputStream (file_stream);--			png_stream.read (buffer);-			-			return Checksum.compute_for_data (ChecksumType.SHA1, buffer);-		} catch (GLib.Error e) {-			warning (e.message);-		}-		-		return "";-	}-	-	private void create_png () {-		string file_name = @"$path.png";-		Font font = BirdFont.get_current_font ();-		File folder = font.get_backgrounds_folder ();-		File original = File.new_for_path (file_name);-		File png_image = folder.get_child (@"full_$((!)original.get_basename ())");-		bool converted;-		-		if (png_image.query_exists ()) {-			path = (!) png_image.get_path ();-			return;-		}-		-		if (is_null (path)) {-			warning ("Background image path is null.");-			return;-		}-		-		converted = MainWindow.native_window.convert_to_png (path, (!) png_image.get_path ());-		-		if (!converted) {-			warning ("Failed to convert image: $(path)");-		}-		-		path = (!) png_image.get_path ();-	}-	-	public void set_img_rotation_from_coordinate (double x, double y) {-		double bcx, bcy;-		double a, b, c, length;--		img_center (out bcx, out bcy);--		a = bcx - x;-		b = bcy - y;-		c = a * a + b * b;-		-		if (c == 0) {-			return;-		}-		-		length = sqrt (fabs (c));	-		-		if (c < 0) {-			length = -length;-		}-	-		img_rotation =  (y < bcy) ? acos (a / length) + PI : -acos (a / length) + PI;-	}-	-	public void set_img_scale (double xs, double ys) {-		img_scale_x = xs;-		img_scale_y = ys;-	}--	public void reset_scale (Glyph g) {-		double w, h;--		w = g.get_width ();-		h = g.get_height ();--		img_scale_x = 1;			-		img_scale_y = 1;-		-		img_offset_x = g.get_line ("left").pos;-		img_offset_y = g.get_line ("top").pos;		-	}--	public void draw (Context cr, WidgetAllocation allocation, double view_offset_x, double view_offset_y, double view_zoom) {-		double x, y;-		double iw, ih;-		int h, w;-		double scale_x, scale_y;-		double oy, ox;-				-		Surface s;-		Context c;--		Surface sg;-		Context cg;--		Surface st;-		Context ct;--		double wc, hc;--		if (unlikely (get_img ().status () != Cairo.Status.SUCCESS)) {-			warning (@"Background image is invalid. (\"$path\")\n");-			MainWindow.get_current_glyph ().set_background_visible (false);-			return;-		}-		-		// add margin-		sg = new Surface.similar (get_img (), get_img ().get_content (), size_margin, size_margin);-		cg = new Context (sg);-		-		wc = get_margin_width ();-		hc = get_margin_height ();-		-		cg.set_source_rgba (1, 1, 1, 1);-		cg.rectangle (0, 0, size_margin, size_margin);-		cg.fill ();-		-		cg.set_source_surface (get_img (), wc, hc);-		cg.paint ();--		x = Glyph.reverse_path_coordinate_x (img_offset_x);-		y = Glyph.reverse_path_coordinate_y (img_offset_y);-		-		ih = (int) get_img ().get_height ();-		iw = (int) get_img ().get_width ();-		-		w = (int) iw;-		h = (int) ih;--		oy = size_margin;-		ox = size_margin;--		// rotate image-		s = new Surface.similar (sg, sg.get_content (), (int) (ox), (int) (oy));-		c = new Context (s);-	-		c.save ();--		c.set_source_rgba (1, 1, 1, 1);-		c.rectangle (0, 0, size_margin, size_margin);-		c.fill ();-			-   		c.translate (size_margin * 0.5, size_margin * 0.5);-		c.rotate (img_rotation);-		c.translate (-size_margin * 0.5, -size_margin * 0.5);--		c.set_source_surface (cg.get_target (), 0, 0);-		c.paint ();	-		c.restore ();--		// mask-		st = new Surface.similar (s, s.get_content (), allocation.width, allocation.height);-		ct = new Context (st);-		ct.save ();--		ct.set_source_rgba (1, 1, 1, 1);-		ct.rectangle (0, 0, allocation.width, allocation.height);-		ct.fill ();--		// scale both canvas and image at the same time-		scale_x = view_zoom * img_scale_x;-		scale_y = view_zoom * img_scale_y;-		-		ct.scale (scale_x, scale_y);-		ct.translate (-view_offset_x / img_scale_x, -view_offset_y / img_scale_y);-		-		ct.set_source_surface (s, img_offset_x / img_scale_x, img_offset_y / img_scale_y);--		ct.paint ();-		ct.restore ();-		-		// add it-		cr.save ();-		cr.set_source_surface (st, 0, 0);-		cr.paint ();-		cr.restore ();-	}-	-	public void handler_release (double nx, double ny) {		-		selected_handle = 0;-		handler_move (nx, ny);-	}-			-	public void handler_press (double nx, double ny) {		-		if (is_over_rotate (nx, ny)) {-			selected_handle = 2;-		} else if (is_over_resize (nx, ny)) {-			selected_handle = 1;-		} else {-			selected_handle = 0;-		}	-	}--	void img_center (out double x, out double y) {-		Glyph g = MainWindow.get_current_glyph ();-		-		x = img_offset_x - g.view_offset_x + (size_margin / 2) * img_scale_x;-		y = img_offset_y - g.view_offset_y + (size_margin / 2) * img_scale_y;--		y *= g.view_zoom;-		x *= g.view_zoom;-	}--	bool is_over_rotate (double nx, double ny) {-		double x, y, d;--		img_center (out x, out y);--		x += cos (img_rotation) * 75;-		y += sin (img_rotation) * 75;--		d = sqrt (pow (x - nx, 2) + pow (y - ny, 2));-		-		return d < 10;-	}--	bool is_over_resize (double nx, double ny) {-		Glyph g = MainWindow.get_current_glyph ();-		double x, y, size;-		bool inx, iny;--		size = 12 * g.view_zoom;--		x = img_offset_x - g.view_offset_x;-		y = img_offset_y - g.view_offset_y + (size_margin) * img_scale_y;-				-		y *= g.view_zoom;-		x *= g.view_zoom;-				-		inx = x - size <= nx <= x + size;-		iny = y - size <= ny <= y + size;-		-		return inx && iny;-	}--	public void handler_move (double nx, double ny) {-		int prev_handle = active_handle;--		if (is_over_rotate (nx, ny)) {-			active_handle = 2;-		} else if (is_over_resize (nx, ny)) {-			active_handle = 1;-		} else {-			active_handle = 0;-		}-		-		if (prev_handle != active_handle) {-			GlyphCanvas.redraw ();-		}-	}-	-	public void draw_handle (Context cr, Glyph g) {-		draw_resize_handle (cr, g);-		draw_rotate_handle (cr, g);-	}--	public void draw_resize_handle (Context cr, Glyph g) {-		double x, y;-		cr.save ();-		-		cr.set_source_rgba (1, 0, 0.3, 1);--		x = img_offset_x - g.view_offset_x;-		y = img_offset_y - g.view_offset_y + (size_margin) * img_scale_y;-				-		y *= g.view_zoom;-		x *= g.view_zoom;-				-		draw_handle_triangle (x, y, cr, g, 6);-		-		cr.restore ();-	}-		-	public void draw_rotate_handle (Context cr, Glyph g) {-		double x, y, hx, hy, x2, y2;-		-		double ivz = 1.0 / (g.view_zoom);-		-		cr.save ();-		-		cr.scale (g.view_zoom, g.view_zoom);-		-		if (selected_handle == 2) cr.set_source_rgba (1, 0, 0.3, 1);-		else if (active_handle == 2) cr.set_source_rgba (0, 0, 0.3, 1);-		else cr.set_source_rgba (0.7, 0.7, 0.8, 1);--		x = img_offset_x - g.view_offset_x + (size_margin / 2) * img_scale_x;-		y = img_offset_y - g.view_offset_y + (size_margin / 2) * img_scale_y;-				-		cr.rectangle (x, y, 5 * ivz, 5 * ivz);-		cr.fill ();--		hx = cos (img_rotation) * 75 * ivz;-		hy = sin (img_rotation) * 75 * ivz;-		-		x2 = x + hx;-		y2 = y + hy;--		cr.rectangle (x2, y2, 5 * ivz, 5 * ivz);-		cr.fill ();--		cr.set_line_width (ivz);-		cr.move_to (x + 2.5* ivz, y + 2.5* ivz);-		cr.line_to (x2 + 2.5* ivz, y2 + 2.5* ivz);-		cr.stroke ();-										-		cr.restore ();-	}--	void draw_handle_triangle (double x, double y, Context cr, Glyph g, int direction, double s = 1) -		requires (0 < direction < 8)-	{-		double ivz = 1.0 / (g.view_zoom);-		double size;-		-		cr.save ();-		cr.set_line_width (ivz);-		-		if (selected_handle == 1) cr.set_source_rgba (1, 0, 0.3, 1);-		else if (active_handle == 1) cr.set_source_rgba (0, 0, 0.3, 1);-		else cr.set_source_rgba (0.7, 0.7, 0.8, 1);	--		size = (8) * s;-		-		cr.scale (1, 1);-		cr.new_path ();-		-		// up + left-		if (direction == 1) {-			cr.move_to (x - size, y - size);-			cr.line_to (x + size, y - size);-			cr.line_to (x - size, y + size);	-		}-		-		if (direction == 6) {-			cr.move_to (x + size, y + size);	-			cr.line_to (x - size, y + size);-			cr.line_to (x - size, y - size);	-		}-		-		cr.close_path();-		cr.fill ();--		cr.restore ();-		-	}--	private static uchar ftu (double c) {-		if (c < 0) return 0;-		if (c > 255) return 255;-		return (uchar) c;-	}--	private static ImageSurface? process_background_colors (ImageSurface img, double contrast, bool desaturate, double threshold) {-		int i;-		unowned uchar[]? pix_buff_t;-		unowned uchar[] pix_buff;-		int len;-		uchar[] contrast_img;-		ImageSurface contrast_surface;-		double c, thres;-		uchar nc;--		int w = img.get_width();-		int h = img.get_height();--		if (unlikely (img.status () != Cairo.Status.SUCCESS)) {-			warning ("Err");-			return null;-		}-		-		len = 4 * w * h;-		-		pix_buff_t = img.get_data ();-		return_val_if_fail (pix_buff_t != null, null);-		pix_buff = pix_buff_t;-		-		contrast_img = new uchar[len];--		pix_buff_t = contrast_img;-		return_val_if_fail (pix_buff_t != null, null);-		-		contrast += 70;-		contrast /= 71;-		-		if (desaturate) {-			i = 0;-			-			while (i < len) {-				c = (pix_buff[i] + pix_buff[i+1] + pix_buff[i+2]) / 3.0;-				nc = ftu (Math.pow (c, contrast));--				contrast_img[i]   = nc;-				contrast_img[i+1] = nc; -				contrast_img[i+2] = nc;-				contrast_img[i+3] = 255;-				-				i += 4;-			}-		} else {-			i = 0;-			while (i < len) {-				contrast_img[i] = ftu (Math.pow (pix_buff[i], contrast) - Math.pow (100, contrast) + 100);-				contrast_img[i+1] = ftu (Math.pow (pix_buff[i+1], contrast) - Math.pow (100, contrast) + 100);-				contrast_img[i+2] = ftu (Math.pow (pix_buff[i+2], contrast) - Math.pow (100, contrast) + 100);-				contrast_img[i+3] = 255;-				-				i += 4;-			}-		}--		if (true) {-			thres = (threshold - 0.5) * 255;-			-			i = 0;-			while (i < len) {-				c = (pix_buff[i] + pix_buff[i+1] + pix_buff[i+2]) / 3.0;-				nc = (c > thres) ? 255 : 0;--				contrast_img[i]   = nc;-				contrast_img[i+1] = nc; -				contrast_img[i+2] = nc;-				contrast_img[i+3] = 255;-				-				i += 4;-			}-		}--		contrast_surface = new ImageSurface.for_data (contrast_img, img.get_format(), w, h, img.get_stride());--		if (unlikely (contrast_surface.status () != Cairo.Status.SUCCESS)) {-			warning ("Err");-			return null;-		}-		-		contrast_surface.mark_dirty	();-			-		return contrast_surface;	-	}--	async ImageSurface? do_process_background_colors () {-		SourceFunc callback = do_process_background_colors.callback;-		ImageSurface? output = null;-		double contrast = get_contrast ();-		bool desaturate = get_desaturate_background ();-		double threshold = this.threshold;--		if (original_image == null) {-			return null;-		}-		-		// TODO: Add multi threading-		output = process_background_colors ((!)original_image, contrast, desaturate, threshold);-		Idle.add ((owned) callback);--		return output;-	}--	public void update_background (double contrast, bool desaturate) {-		background_image_is_processing = true;-		-		do_process_background_colors.begin ((obj, res) => {-			ImageSurface? img;--			img = do_process_background_colors.end (res);-			-			if (img != null) {-				background_image = (!) img;-				updated ();-			} else {-				stderr.printf ("Error: No background image");	-			}-			-			// update image if new value was set while background task was running -			if (get_contrast () != contrast || get_desaturate_background () != desaturate) {--				do_process_background_colors.begin ((obj, res) => {-					img = do_process_background_colors.end (res);-					if (likely (img != null)) {-						background_image = (!) img;-						updated ();-					} else {-						stderr.printf ("Error: No background image");	-					}-				});-			}--			background_image_is_processing = false;-			updated ();-			-		});-	}--	public double get_contrast () {-		double bc;-		lock (contrast) {-			bc = contrast;-		}-		return bc;-	}-		-	public bool get_desaturate_background () {-		bool bw;-		lock (desaturate) {-			bw = desaturate;-		}-		return bw;-	}-	-	public void set_desaturate_background (bool bw) {-		lock (desaturate) {-			desaturate = bw;-		}-	-		if (background_image_is_processing) {-			return;-		}-		-		background_image_is_processing = true;-		-		update_background (get_contrast (), bw);-	}-	-	public void set_threshold (double t) {-		threshold = t;-	}-	-	public Path auto_trace () {-		ImageSurface img;-		int len, w, h, i;-		unowned uchar[] pix_buff;-		Path p = new Path ();-		double x, y;--		if (background_image == null) {-			return p;-		}--		img = (!) background_image;--		w = img.get_width();-		h = img.get_height();--		if (unlikely (img.status () != Cairo.Status.SUCCESS)) {-			warning ("Error");-			return p;-		}-		-		len = 4 * w * h;--		pix_buff = img.get_data ();--		i = 0;-		while (i < len - 4) {-			if (is_edge (pix_buff[i], pix_buff[i + 4])) {-				y = i / w;-				x = i - y;-				-				p.add (x, y);-				-				if (p.points.size > 10) {-					return p;-				}-			}-			-			i += 4;-		}-		-		return p;-	}-	-	private bool is_edge (uchar a, uchar b) {-		return (a == 255 && b == 0);-	}-	-	public void set_contrast (double contrast) {-		lock (this.contrast) {-			this.contrast = contrast;-		}-		-		if (background_image_is_processing) {-			return;-		}-		-		background_image_is_processing = true;-		-		update_background (contrast, get_desaturate_background ());-	}-	-	public void center_in_glyph () {-		Glyph g = MainWindow.get_current_glyph ();-	}-}-	-}diff --git a/libbirdfont/TestCases.vala b/libbirdfont/TestCases.valaindex 9056c70..c704e14 100644--- a/libbirdfont/TestCases.vala+++ b/libbirdfont/TestCases.vala@@ -514,7 +514,7 @@ class TestCases { 	}  	public static void test_background_coordinates () {-		GlyphBackgroundImage bg = new GlyphBackgroundImage ("");+		BackgroundImage bg = new BackgroundImage (""); 		 		bg.set_position (100, 100); 		bg.set_img_offset (bg.img_offset_x, bg.img_offset_y);diff --git a/libbirdfont/ZoomTool.vala b/libbirdfont/ZoomTool.valaindex d45872b..2551fa5 100644--- a/libbirdfont/ZoomTool.vala+++ b/libbirdfont/ZoomTool.vala@@ -70,7 +70,7 @@ class ZoomTool : Tool { 	}  	public void zoom_full_background_image () {-		GlyphBackgroundImage bg;+		BackgroundImage bg; 		Glyph g = MainWindow.get_current_glyph (); 		int x, y; 		

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112