Просмотр исходного кода

tools: use PixbufView in sprite and font importers

Daniele Bartolini 1 год назад
Родитель
Сommit
4e90bbacc0
2 измененных файлов с 160 добавлено и 252 удалено
  1. 6 40
      tools/level_editor/font_import_dialog.vala
  2. 154 212
      tools/level_editor/sprite_import_dialog.vala

+ 6 - 40
tools/level_editor/font_import_dialog.vala

@@ -66,10 +66,9 @@ private void copy_alpha_to_argb32(Cairo.ImageSurface dst, Cairo.ImageSurface src
 
 public class FontImportDialog : Gtk.Dialog
 {
-	public Cairo.Surface _checker;
 	public FontAtlas* _font_atlas;
 	public Cairo.ImageSurface _atlas;
-	public Gtk.DrawingArea _drawing_area;
+	public PixbufView _drawing_area;
 	public Gtk.ScrolledWindow _scrolled_window;
 
 	public Gtk.Label _atlas_size;
@@ -111,6 +110,8 @@ public class FontImportDialog : Gtk.Dialog
 			);
 
 		_atlas_size.set_text(_atlas.get_width().to_string() + " × " + _atlas.get_height().to_string());
+		_drawing_area.set_pixbuf(Gdk.pixbuf_get_from_surface(_atlas, 0, 0, _atlas.get_width(), _atlas.get_height()));
+		_drawing_area._zoom = 1.0;
 	}
 
 	public GlyphData* glyph_data(int index)
@@ -134,44 +135,9 @@ public class FontImportDialog : Gtk.Dialog
 		this.title = "Import Font...";
 		this.set_icon_name(CROWN_EDITOR_ICON_NAME);
 
-		// Create checkered pattern
-		{
-			int width = 16;
-			int height = 16;
-			_checker = new Cairo.ImageSurface(Cairo.Format.RGB24, width, height);
-
-			Cairo.Context cr = new Cairo.Context(_checker);
-			cr.set_source_rgb(0.9, 0.9, 0.9);
-			cr.paint();
-			cr.set_source_rgb(0.7, 0.7, 0.7);
-			cr.rectangle(width / 2, 0, width / 2, height / 2);
-			cr.rectangle(0, height / 2, width / 2, height / 2);
-			cr.fill();
-		}
-
-		_drawing_area = new Gtk.DrawingArea();
-		_drawing_area.draw.connect((cr) => {
-				cr.set_source_rgb(0.1, 0.1, 0.1);
-				cr.paint();
-
-				cr.save();
-				cr.set_source_surface(_checker, 0, 0);
-				Cairo.Pattern pattern = cr.get_source();
-				pattern.set_filter(Cairo.Filter.NEAREST);
-				pattern.set_extend(Cairo.Extend.REPEAT);
-				cr.rectangle(0, 0, _atlas.get_width(), _atlas.get_height());
-				cr.clip();
-				cr.paint();
-				cr.restore();
-
-				cr.save();
-				cr.set_source_surface(_atlas, 0, 0);
-				cr.rectangle(0, 0, _atlas.get_width(), _atlas.get_height());
-				cr.paint();
-				cr.restore();
-
-				return Gdk.EVENT_STOP;
-			});
+		_drawing_area = new PixbufView();
+		_drawing_area._filter = Cairo.Filter.BILINEAR;
+		_drawing_area._extend = Cairo.Extend.NONE;
 
 		_scrolled_window = new Gtk.ScrolledWindow(null, null);
 		_scrolled_window.min_content_width = 640;

+ 154 - 212
tools/level_editor/sprite_import_dialog.vala

@@ -94,12 +94,7 @@ void sprite_cell_from_index(out int r, out int c, int num_cols, int index)
 
 public class SpriteImportDialog : Gtk.Dialog
 {
-	public Cairo.Surface _checker;
 	public Gdk.Pixbuf _pixbuf;
-	public Cairo.Pattern _pixbuf_pattern;
-	public Gtk.DrawingArea _slices;
-	public Gtk.ScrolledWindow _scrolled_window;
-	public Gtk.DrawingArea _preview;
 
 	public EntryResourceBasename _unit_name;
 	public Gtk.Label resolution;
@@ -130,19 +125,16 @@ public class SpriteImportDialog : Gtk.Dialog
 	public EntryDouble mass;
 	public Gtk.CheckButton lock_rotation_y;
 
-	public Gtk.Notebook _notebook;
-
 	public Gtk.Button _previous_frame;
 	public Gtk.Button _next_frame;
 	public EntryDouble _current_frame;
 	public Gtk.Box _frame_selector_box;
 	public Gtk.Overlay _preview_overlay;
-	public Gtk.EventControllerScroll _preview_controller_scroll;
-	public Gtk.EventControllerScroll _slices_controller_scroll;
+	public PixbufView _slices;
+	public PixbufView _preview;
+	public Gtk.ScrolledWindow _scrolled_window;
 
-	public double _preview_zoom;
-	public double _slices_zoom;
-	public double _zoom_speed;
+	public Gtk.Notebook _notebook;
 
 	// Widgets
 	public SpriteImportDialog(string image_path, string unit_name)
@@ -157,190 +149,6 @@ public class SpriteImportDialog : Gtk.Dialog
 			loge(e.message);
 		}
 
-		_pixbuf_pattern = new Cairo.Pattern.for_surface(Gdk.cairo_surface_create_from_pixbuf(_pixbuf, 1, null));
-		_pixbuf_pattern.set_filter(Cairo.Filter.NEAREST);
-
-		// Create checkered pattern
-		{
-			int width = 16;
-			int height = 16;
-			_checker = new Cairo.ImageSurface(Cairo.Format.RGB24, width, height);
-
-			Cairo.Context cr = new Cairo.Context(_checker);
-			cr.set_source_rgb(0.9, 0.9, 0.9);
-			cr.paint();
-			cr.set_source_rgb(0.7, 0.7, 0.7);
-			cr.rectangle(width / 2, 0, width / 2, height / 2);
-			cr.rectangle(0, height / 2, width / 2, height / 2);
-			cr.fill();
-		}
-
-		_slices = new Gtk.DrawingArea();
-		_slices.set_size_request(_pixbuf.width, _pixbuf.height);
-
-		_slices.draw.connect((cr) => {
-				int allocated_width = _preview.get_allocated_width();
-				int allocated_height = _preview.get_allocated_height();
-				double original_line_width = cr.get_line_width();
-
-				cr.set_source_rgb(0.1, 0.1, 0.1);
-				cr.paint();
-
-				cr.translate(allocated_width * 0.5, allocated_height * 0.5);
-				cr.scale(_slices_zoom, _slices_zoom);
-				cr.set_line_width(original_line_width / _slices_zoom);
-				cr.translate(-_pixbuf.width * 0.5, -_pixbuf.height * 0.5);
-
-				cr.save();
-				cr.set_source_surface(_checker, 0, 0);
-				Cairo.Pattern pattern = cr.get_source();
-				pattern.set_filter(Cairo.Filter.NEAREST);
-				pattern.set_extend(Cairo.Extend.REPEAT);
-				cr.rectangle(0, 0, _pixbuf.width, _pixbuf.height);
-				cr.clip();
-				cr.paint();
-				cr.restore();
-
-				cr.set_source(_pixbuf_pattern);
-				cr.paint();
-
-				int num_v = (int)cells.value.y;
-				int num_h = (int)cells.value.x;
-
-				for (int h = 0; h < num_v; ++h) {
-					for (int w = 0; w < num_h; ++w) {
-						Vector2 sc = sprite_cell_xy(h
-							, w
-							, (int)offset.value.x
-							, (int)offset.value.y
-							, (int)cell.value.x
-							, (int)cell.value.y
-							, (int)spacing.value.x
-							, (int)spacing.value.y
-							);
-
-						int x0 = (int)sc.x;
-						int y0 = (int)sc.y;
-						int x1 = x0 + (int)cell.value.x;
-						int y1 = y0;
-						int x2 = x1;
-						int y2 = y0 + (int)cell.value.y;
-						int x3 = x0;
-						int y3 = y2;
-						// https://www.cairographics.org/FAQ/#sharp_lines
-						cr.move_to((double)x0,       (double)y0 + 0.5);
-						cr.line_to((double)x1,       (double)y1 + 0.5);
-						cr.move_to((double)x1 + 0.5, (double)y1);
-						cr.line_to((double)x2 + 0.5, (double)y2);
-						cr.move_to((double)x2,       (double)y2 + 0.5);
-						cr.line_to((double)x3,       (double)y3 + 0.5);
-						cr.move_to((double)x3 + 0.5, (double)y3);
-						cr.line_to((double)x0 + 0.5, (double)y0);
-
-						cr.set_source_rgba(0.9, 0.1, 0.1, 0.9);
-						cr.stroke();
-					}
-				}
-
-				return Gdk.EVENT_STOP;
-			});
-
-		_scrolled_window = new Gtk.ScrolledWindow(null, null);
-		_scrolled_window.min_content_width = 640;
-		_scrolled_window.min_content_height = 640;
-		_scrolled_window.add(_slices);
-
-		_preview = new Gtk.DrawingArea();
-		_preview.set_size_request(128, 128);
-
-		_preview.draw.connect((cr) => {
-				int allocated_width = _preview.get_allocated_width();
-				int allocated_height = _preview.get_allocated_height();
-				double original_line_width = cr.get_line_width();
-
-				cr.set_source_rgb(0.1, 0.1, 0.1);
-				cr.paint();
-
-				int r, c;
-				sprite_cell_from_index(out r, out c, (int)cells.value.x, (int)_current_frame.value);
-
-				Vector2 sc = sprite_cell_xy(r
-					, c
-					, (int)offset.value.x
-					, (int)offset.value.y
-					, (int)cell.value.x
-					, (int)cell.value.y
-					, (int)spacing.value.x
-					, (int)spacing.value.y
-					);
-
-				int x0 = (int)sc.x;
-				int y0 = (int)sc.y;
-
-				cr.translate(allocated_width * 0.5, allocated_height * 0.5);
-				cr.scale(_preview_zoom, _preview_zoom);
-				cr.set_line_width(original_line_width / _preview_zoom);
-				cr.translate(-cell.value.x * 0.5, -cell.value.y * 0.5);
-
-				// Draw checkered background.
-				cr.save();
-				cr.set_source_surface(_checker, 0, 0);
-				Cairo.Pattern pattern = cr.get_source();
-				pattern.set_filter(Cairo.Filter.NEAREST);
-				pattern.set_extend(Cairo.Extend.REPEAT);
-				cr.rectangle(0, 0, (int)cell.value.x, (int)cell.value.y);
-				cr.clip();
-				cr.new_path(); // path not consumed by clip()
-				cr.paint();
-				cr.restore();
-
-				// Draw sprite frame.
-				Gdk.Pixbuf frame_pixbuf = new Gdk.Pixbuf.subpixbuf(_pixbuf, x0, y0, (int)cell.value.x, (int)cell.value.y);
-				Cairo.Surface frame_surface = Gdk.cairo_surface_create_from_pixbuf(frame_pixbuf, 1, null);
-				Cairo.Pattern frame_pattern = new Cairo.Pattern.for_surface(frame_surface);
-				frame_pattern.set_filter(Cairo.Filter.NEAREST);
-				cr.save();
-				cr.set_source(frame_pattern);
-				cr.paint();
-				cr.clip();
-				cr.new_path(); // path not consumed by clip()
-				cr.paint();
-				cr.restore();
-
-				// Draw collider.
-				if (shape.visible_child_name == "square_collider") {
-					cr.rectangle(collision_xy.value.x, collision_xy.value.y, collision_wh.value.x, collision_wh.value.y);
-					cr.set_source_rgba(collider_color.red, collider_color.green, collider_color.blue, collider_color.alpha);
-					cr.stroke();
-				} else if (shape.visible_child_name == "circle_collider") {
-					cr.arc(circle_collision_center.value.x, circle_collision_center.value.y, circle_collision_radius.value, 0, 2*Math.PI);
-					cr.set_source_rgba(collider_color.red, collider_color.green, collider_color.blue, collider_color.alpha);
-					cr.stroke();
-				} else if (shape.visible_child_name == "capsule_collider") {
-					double x = capsule_collision_center.value.x;
-					double y = capsule_collision_center.value.y;
-					double radius = capsule_collision_radius.value;
-					double height = capsule_collision_height.value - 2*radius;
-					cr.arc(x - height/2, y, radius, Math.PI/2, 3*Math.PI/2);
-					cr.rectangle(x - height/2, y - radius, height, 2*radius);
-					cr.arc(x + height/2, y, radius, 3*Math.PI/2, Math.PI/2);
-					cr.set_source_rgba(collider_color.red, collider_color.green, collider_color.blue, collider_color.alpha);
-					cr.stroke();
-				}
-
-				// Draw pivot.
-				// Pivot is relative to the top-left corner of the cell.
-				Vector2 pivot = sprite_cell_pivot_xy((int)cell.value.x
-					, (int)cell.value.y
-					, (int)pivot.active
-					);
-				cr.arc(pivot.x, pivot.y, 5.0, 0, 2*Math.PI);
-				cr.set_source_rgba(0.1, 0.1, 0.9, 0.6);
-				cr.fill();
-
-				return Gdk.EVENT_STOP;
-			});
-
 		_unit_name = new EntryResourceBasename(unit_name);
 
 		resolution = new Gtk.Label(_pixbuf.width.to_string() + " × " + _pixbuf.height.to_string());
@@ -382,6 +190,7 @@ public class SpriteImportDialog : Gtk.Dialog
 				_current_frame.set_max(cells.value.x * cells.value.y - 1);
 
 				_slices.queue_draw();
+				set_preview_frame();
 				_preview.queue_draw();
 			});
 
@@ -396,16 +205,19 @@ public class SpriteImportDialog : Gtk.Dialog
 				circle_collision_center.value = Vector2(cell.value.x/2.0, cell.value.y/2.0);
 				capsule_collision_center.value = Vector2(cell.value.x/2.0, cell.value.y/2.0);
 				_slices.queue_draw();
+				set_preview_frame();
 				_preview.queue_draw();
 			});
 
 		offset.value_changed.connect(() => {
 				_slices.queue_draw();
+				set_preview_frame();
 				_preview.queue_draw();
 			});
 
 		spacing.value_changed.connect(() => {
 				_slices.queue_draw();
+				set_preview_frame();
 				_preview.queue_draw();
 			});
 
@@ -532,13 +344,18 @@ public class SpriteImportDialog : Gtk.Dialog
 		_previous_frame = new Gtk.Button.from_icon_name("go-previous-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
 		_previous_frame.clicked.connect(() => {
 				_current_frame.value -= 1;
+				set_preview_frame();
+				_preview.queue_draw();
 			});
 		_next_frame = new Gtk.Button.from_icon_name("go-next-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
 		_next_frame.clicked.connect(() => {
 				_current_frame.value += 1;
+				set_preview_frame();
+				_preview.queue_draw();
 			});
 		_current_frame = new EntryDouble(0.0, 0.0, cells.value.x * cells.value.y - 1);
 		_current_frame.value_changed.connect(() => {
+				set_preview_frame();
 				_preview.queue_draw();
 			});
 		_frame_selector_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
@@ -549,30 +366,124 @@ public class SpriteImportDialog : Gtk.Dialog
 		_frame_selector_box.pack_start(_previous_frame);
 		_frame_selector_box.pack_start(_current_frame);
 		_frame_selector_box.pack_end(_next_frame);
+
+		_slices = new PixbufView();
+		_slices.set_size_request(_pixbuf.width, _pixbuf.height);
+		_slices.set_pixbuf(_pixbuf);
+
+		_slices.draw.connect((cr) => {
+				int allocated_width = _preview.get_allocated_width();
+				int allocated_height = _preview.get_allocated_height();
+				double original_line_width = cr.get_line_width();
+
+				cr.translate(allocated_width * 0.5, allocated_height * 0.5);
+				cr.scale(_slices._zoom, _slices._zoom);
+				cr.set_line_width(original_line_width / _slices._zoom);
+				cr.translate(-_pixbuf.width * 0.5, -_pixbuf.height * 0.5);
+
+				int num_v = (int)cells.value.y;
+				int num_h = (int)cells.value.x;
+
+				for (int h = 0; h < num_v; ++h) {
+					for (int w = 0; w < num_h; ++w) {
+						Vector2 sc = sprite_cell_xy(h
+							, w
+							, (int)offset.value.x
+							, (int)offset.value.y
+							, (int)cell.value.x
+							, (int)cell.value.y
+							, (int)spacing.value.x
+							, (int)spacing.value.y
+							);
+
+						int x0 = (int)sc.x;
+						int y0 = (int)sc.y;
+						int x1 = x0 + (int)cell.value.x;
+						int y1 = y0;
+						int x2 = x1;
+						int y2 = y0 + (int)cell.value.y;
+						int x3 = x0;
+						int y3 = y2;
+						// https://www.cairographics.org/FAQ/#sharp_lines
+						cr.move_to((double)x0,       (double)y0 + 0.5);
+						cr.line_to((double)x1,       (double)y1 + 0.5);
+						cr.move_to((double)x1 + 0.5, (double)y1);
+						cr.line_to((double)x2 + 0.5, (double)y2);
+						cr.move_to((double)x2,       (double)y2 + 0.5);
+						cr.line_to((double)x3,       (double)y3 + 0.5);
+						cr.move_to((double)x3 + 0.5, (double)y3);
+						cr.line_to((double)x0 + 0.5, (double)y0);
+
+						cr.set_source_rgba(0.9, 0.1, 0.1, 0.9);
+						cr.stroke();
+					}
+				}
+
+				return Gdk.EVENT_STOP;
+			});
+
+		_preview = new PixbufView();
+		_preview._zoom = 4.0;
+		_preview.set_size_request(128, 128);
+		set_preview_frame();
+
+		_preview.draw.connect((cr) => {
+				int allocated_width = _preview.get_allocated_width();
+				int allocated_height = _preview.get_allocated_height();
+				double original_line_width = cr.get_line_width();
+
+				cr.translate(allocated_width * 0.5, allocated_height * 0.5);
+				cr.scale(_preview._zoom, _preview._zoom);
+				cr.set_line_width(original_line_width / _preview._zoom);
+				cr.translate(-cell.value.x * 0.5, -cell.value.y * 0.5);
+
+				// Draw collider.
+				if (shape.visible_child_name == "square_collider") {
+					cr.rectangle(collision_xy.value.x, collision_xy.value.y, collision_wh.value.x, collision_wh.value.y);
+					cr.set_source_rgba(collider_color.red, collider_color.green, collider_color.blue, collider_color.alpha);
+					cr.stroke();
+				} else if (shape.visible_child_name == "circle_collider") {
+					cr.arc(circle_collision_center.value.x, circle_collision_center.value.y, circle_collision_radius.value, 0, 2*Math.PI);
+					cr.set_source_rgba(collider_color.red, collider_color.green, collider_color.blue, collider_color.alpha);
+					cr.stroke();
+				} else if (shape.visible_child_name == "capsule_collider") {
+					double x = capsule_collision_center.value.x;
+					double y = capsule_collision_center.value.y;
+					double radius = capsule_collision_radius.value;
+					double height = capsule_collision_height.value - 2*radius;
+					cr.arc(x - height/2, y, radius, Math.PI/2, 3*Math.PI/2);
+					cr.rectangle(x - height/2, y - radius, height, 2*radius);
+					cr.arc(x + height/2, y, radius, 3*Math.PI/2, Math.PI/2);
+					cr.set_source_rgba(collider_color.red, collider_color.green, collider_color.blue, collider_color.alpha);
+					cr.stroke();
+				}
+
+				// Draw pivot.
+				// Pivot is relative to the top-left corner of the cell.
+				Vector2 pivot = sprite_cell_pivot_xy((int)cell.value.x
+					, (int)cell.value.y
+					, (int)pivot.active
+					);
+				cr.arc(pivot.x, pivot.y, 5.0, 0, 2*Math.PI);
+				cr.set_source_rgba(0.1, 0.1, 0.9, 0.6);
+				cr.fill();
+
+				return Gdk.EVENT_STOP;
+			});
+
 		_preview_overlay = new Gtk.Overlay();
 		_preview_overlay.add(_preview);
 		_preview_overlay.add_overlay(_frame_selector_box);
 
+		_scrolled_window = new Gtk.ScrolledWindow(null, null);
+		_scrolled_window.min_content_width = 640;
+		_scrolled_window.min_content_height = 640;
+		_scrolled_window.add(_slices);
+
 		_notebook = new Gtk.Notebook();
 		_notebook.append_page(_preview_overlay, new Gtk.Label("Preview"));
 		_notebook.append_page(_scrolled_window, new Gtk.Label("Slices"));
 
-		_preview_controller_scroll = new Gtk.EventControllerScroll(_preview, Gtk.EventControllerScrollFlags.VERTICAL);
-		_preview_controller_scroll.scroll.connect((dx, dy) => {
-				_preview_zoom = double.min(10.0, double.max(0.25, _preview_zoom - dy * _zoom_speed));
-				_preview.queue_draw();
-			});
-
-		_slices_controller_scroll = new Gtk.EventControllerScroll(_slices, Gtk.EventControllerScrollFlags.VERTICAL);
-		_slices_controller_scroll.scroll.connect((dx, dy) => {
-				_slices_zoom = double.min(10.0, double.max(0.25, _slices_zoom - dy * _zoom_speed));
-				_slices.queue_draw();
-			});
-
-		_preview_zoom = 4.0;
-		_slices_zoom = 1.0;
-		_zoom_speed = 0.2;
-
 		Gtk.Paned pane;
 		pane = new Gtk.Paned(Gtk.Orientation.HORIZONTAL);
 		pane.pack1(_notebook, false, false);
@@ -603,6 +514,37 @@ public class SpriteImportDialog : Gtk.Dialog
 		}
 	}
 
+	public void set_preview_frame()
+	{
+		assert(cells != null);
+		assert(_current_frame != null);
+		assert(offset != null);
+		assert(cell != null);
+		assert(spacing != null);
+
+		int r, c;
+		sprite_cell_from_index(out r, out c, (int)cells.value.x, (int)_current_frame.value);
+
+		Vector2 sc = sprite_cell_xy(r
+			, c
+			, (int)offset.value.x
+			, (int)offset.value.y
+			, (int)cell.value.x
+			, (int)cell.value.y
+			, (int)spacing.value.x
+			, (int)spacing.value.y
+			);
+
+		Gdk.Pixbuf frame_pixbuf = new Gdk.Pixbuf.subpixbuf(_pixbuf
+			, (int)sc.x
+			, (int)sc.y
+			, (int)cell.value.x
+			, (int)cell.value.y
+			);
+
+		_preview.set_pixbuf(frame_pixbuf);
+	}
+
 	public void load(Hashtable importer_settings)
 	{
 		// Load settings