From b2ed43d22de03d0f504dd2e5b8b840af5e992950 Mon Sep 17 00:00:00 2001 From: Jesse van den Kieboom Date: Sun, 10 Jan 2016 13:24:33 +0100 Subject: [PATCH] Add diff renderer for images --- libgitg/Makefile.am | 7 + libgitg/gitg-diff-image-composite.vala | 91 ++++++ libgitg/gitg-diff-image-difference.vala | 65 ++++ libgitg/gitg-diff-image-overlay.vala | 80 +++++ libgitg/gitg-diff-image-side-by-side.vala | 290 ++++++++++++++++++ libgitg/gitg-diff-image-slider.vala | 94 ++++++ libgitg/gitg-diff-image-surface-cache.vala | 29 ++ .../gitg-diff-view-file-renderer-image.vala | 152 +++++++++ libgitg/gitg-diff-view-file.vala | 5 + libgitg/gitg-diff-view.vala | 51 ++- libgitg/resources/resources.xml | 1 + .../ui/gitg-diff-view-file-renderer-image.ui | 123 ++++++++ libgitg/resources/ui/libgitg-style.css | 8 + 13 files changed, 990 insertions(+), 6 deletions(-) create mode 100644 libgitg/gitg-diff-image-composite.vala create mode 100644 libgitg/gitg-diff-image-difference.vala create mode 100644 libgitg/gitg-diff-image-overlay.vala create mode 100644 libgitg/gitg-diff-image-side-by-side.vala create mode 100644 libgitg/gitg-diff-image-slider.vala create mode 100644 libgitg/gitg-diff-image-surface-cache.vala create mode 100644 libgitg/gitg-diff-view-file-renderer-image.vala create mode 100644 libgitg/resources/ui/gitg-diff-view-file-renderer-image.ui diff --git a/libgitg/Makefile.am b/libgitg/Makefile.am index 245d103f..73981cfe 100644 --- a/libgitg/Makefile.am +++ b/libgitg/Makefile.am @@ -67,6 +67,12 @@ libgitg_libgitg_1_0_la_VALASOURCES = \ libgitg/gitg-credentials-manager.vala \ libgitg/gitg-date.vala \ libgitg/gitg-diff-stat.vala \ + libgitg/gitg-diff-image-composite.vala \ + libgitg/gitg-diff-image-difference.vala \ + libgitg/gitg-diff-image-overlay.vala \ + libgitg/gitg-diff-image-side-by-side.vala \ + libgitg/gitg-diff-image-slider.vala \ + libgitg/gitg-diff-image-surface-cache.vala \ libgitg/gitg-diff-view.vala \ libgitg/gitg-diff-view-file.vala \ libgitg/gitg-diff-view-file-info.vala \ @@ -74,6 +80,7 @@ libgitg_libgitg_1_0_la_VALASOURCES = \ libgitg/gitg-diff-view-file-renderer.vala \ libgitg/gitg-diff-view-file-renderer-binary.vala \ libgitg/gitg-diff-view-file-renderer-text.vala \ + libgitg/gitg-diff-view-file-renderer-image.vala \ libgitg/gitg-diff-view-lines-renderer.vala \ libgitg/gitg-diff-selectable.vala \ libgitg/gitg-diff-view-commit-details.vala \ diff --git a/libgitg/gitg-diff-image-composite.vala b/libgitg/gitg-diff-image-composite.vala new file mode 100644 index 00000000..0a7c2c3f --- /dev/null +++ b/libgitg/gitg-diff-image-composite.vala @@ -0,0 +1,91 @@ +/* + * This file is part of gitg + * + * Copyright (C) 2016 - Jesse van den Kieboom + * + * gitg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * gitg 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gitg. If not, see . + */ + +class Gitg.DiffImageComposite : Gtk.DrawingArea +{ + public Gitg.DiffImageSurfaceCache cache { get; set; } + + private void get_natural_size(out int image_width, out int image_height) + { + var pixbuf = cache.old_pixbuf; + var window = get_window(); + + double xscale = 1, yscale = 1; + + if (window != null) + { + cache.get_old_surface(get_window()).get_device_scale(out xscale, out yscale); + } + + image_width = (int)(pixbuf.get_width() / xscale); + image_height = (int)(pixbuf.get_height() / yscale); + } + + protected void get_sizing(int width, out int image_width, out int image_height) + { + get_natural_size(out image_width, out image_height); + + // Scale down to fit in width + if (image_width > width) + { + image_height *= width / image_width; + image_width = width; + } + } + + protected override void get_preferred_width(out int minimum_width, out int natural_width) + { + int natural_height; + + get_natural_size(out natural_width, out natural_height); + minimum_width = 1; + } + + protected override void get_preferred_height_for_width(int width, out int minimum_height, out int natural_height) + { + int image_width, image_height; + + get_sizing(width, out image_width, out image_height); + + minimum_height = image_height; + natural_height = image_height; + } + + protected override Gtk.SizeRequestMode get_request_mode() + { + return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH; + } + + protected override bool draw(Cairo.Context cr) + { + Gtk.Allocation alloc; + get_allocation(out alloc); + + var ctx = get_style_context(); + + ctx.render_background(cr, alloc.x, alloc.y, alloc.width, alloc.height); + return true; + } + + protected override void realize() + { + base.realize(); + queue_resize(); + } +} diff --git a/libgitg/gitg-diff-image-difference.vala b/libgitg/gitg-diff-image-difference.vala new file mode 100644 index 00000000..b74bfdac --- /dev/null +++ b/libgitg/gitg-diff-image-difference.vala @@ -0,0 +1,65 @@ +/* + * This file is part of gitg + * + * Copyright (C) 2016 - Jesse van den Kieboom + * + * gitg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * gitg 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gitg. If not, see . + */ + +class Gitg.DiffImageDifference : DiffImageComposite +{ + protected override bool draw(Cairo.Context cr) + { + base.draw(cr); + + var window = get_window(); + + Gtk.Allocation alloc; + get_allocation(out alloc); + + int image_width, image_height; + get_sizing(alloc.width, out image_width, out image_height); + + var old_surface = cache.get_old_surface(window); + var new_surface = cache.get_new_surface(window); + + int x = (alloc.width - image_width) / 2; + int y = 0; + + if (old_surface != null) + { + cr.set_source_surface(old_surface, x, y); + cr.paint(); + } + + if (new_surface != null) + { + cr.save(); + { + cr.set_operator(Cairo.Operator.DIFFERENCE); + cr.set_source_surface(new_surface, x, y); + cr.paint(); + } + cr.restore(); + } + + return true; + } + + protected override void realize() + { + base.realize(); + queue_resize(); + } +} diff --git a/libgitg/gitg-diff-image-overlay.vala b/libgitg/gitg-diff-image-overlay.vala new file mode 100644 index 00000000..b74218ba --- /dev/null +++ b/libgitg/gitg-diff-image-overlay.vala @@ -0,0 +1,80 @@ +/* + * This file is part of gitg + * + * Copyright (C) 2016 - Jesse van den Kieboom + * + * gitg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * gitg 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gitg. If not, see . + */ + +class Gitg.DiffImageOverlay : DiffImageComposite +{ + private double d_alpha; + + public double alpha + { + get { return d_alpha; } + + set + { + var newalpha = double.max(0, double.min(value, 1)); + + if (newalpha != d_alpha) + { + d_alpha = newalpha; + queue_draw(); + } + } + + default = 0.5; + } + + protected override bool draw(Cairo.Context cr) + { + base.draw(cr); + + var window = get_window(); + + Gtk.Allocation alloc; + get_allocation(out alloc); + + int image_width, image_height; + get_sizing(alloc.width, out image_width, out image_height); + + var old_surface = cache.get_old_surface(window); + var new_surface = cache.get_new_surface(window); + + int x = (alloc.width - image_width) / 2; + int y = 0; + + if (old_surface != null && d_alpha != 1) + { + cr.set_source_surface(old_surface, x, y); + cr.paint_with_alpha(1 - d_alpha); + } + + if (new_surface != null && d_alpha != 0) + { + cr.set_source_surface(new_surface, x, y); + cr.paint_with_alpha(d_alpha); + } + + return true; + } + + protected override void realize() + { + base.realize(); + queue_resize(); + } +} diff --git a/libgitg/gitg-diff-image-side-by-side.vala b/libgitg/gitg-diff-image-side-by-side.vala new file mode 100644 index 00000000..4e152408 --- /dev/null +++ b/libgitg/gitg-diff-image-side-by-side.vala @@ -0,0 +1,290 @@ +/* + * This file is part of gitg + * + * Copyright (C) 2016 - Jesse van den Kieboom + * + * gitg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * gitg 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gitg. If not, see . + */ + +class Gitg.DiffImageSideBySide : Gtk.DrawingArea +{ + private Pango.Layout d_old_size_layout; + private Pango.Layout d_new_size_layout; + + private static const int TEXT_SPACING = 6; + + private Pango.Layout? old_size_layout + { + get + { + if (d_old_size_layout == null && cache.old_pixbuf != null) + { + string message; + + if (cache.new_pixbuf != null) + { + // Translators: this label is displayed below the image diff, %s + // is substituted with the size of the image + message = _("before (%s)"); + } + else + { + // Translators: this label is displayed below the image diff, %s + // is substituted with the size of the image + message = _("removed (%s)"); + } + + d_old_size_layout = create_pango_layout(message.printf(@"$(cache.old_pixbuf.get_width()) × $(cache.old_pixbuf.get_height())")); + } + + return d_old_size_layout; + } + } + + private Pango.Layout? new_size_layout + { + get + { + if (d_new_size_layout == null && cache.new_pixbuf != null) + { + string message; + + if (cache.old_pixbuf != null) + { + // Translators: this label is displayed below the image diff, %s + // is substituted with the size of the image + message = _("after (%s)"); + } + else + { + // Translators: this label is displayed below the image diff, %s + // is substituted with the size of the image + message = _("added (%s)"); + } + + d_new_size_layout = create_pango_layout(message.printf(@"$(cache.new_pixbuf.get_width()) × $(cache.new_pixbuf.get_height())")); + } + + return d_new_size_layout; + } + } + + public Gitg.DiffImageSurfaceCache cache { get; set; } + public int spacing { get; set; } + + private struct Size + { + public int width; + + public int image_width; + public int image_height; + } + + private struct Sizing + { + public Size old_size; + public Size new_size; + } + + private Sizing get_sizing(int width) + { + double ow = 0, oh = 0, nw = 0, nh = 0; + + var old_pixbuf = cache.old_pixbuf; + var new_pixbuf = cache.new_pixbuf; + + var window = get_window(); + + if (old_pixbuf != null) + { + double xscale = 1, yscale = 1; + + if (window != null) + { + cache.get_old_surface(get_window()).get_device_scale(out xscale, out yscale); + } + + ow = (double)old_pixbuf.get_width() / xscale; + oh = (double)old_pixbuf.get_height() / yscale; + } + + if (new_pixbuf != null) + { + double xscale = 1, yscale = 1; + + if (window != null) + { + cache.get_new_surface(get_window()).get_device_scale(out xscale, out yscale); + } + + nw = (double)new_pixbuf.get_width() / xscale; + nh = (double)new_pixbuf.get_height() / yscale; + } + + var tw = ow + nw; + + width -= spacing; + + double osw = 0, nsw = 0; + + if (tw != 0) + { + if (ow != 0) + { + osw = width * (ow / tw); + } + + if (nw != 0) + { + nsw = width * (nw / tw); + } + } + + var oswi = double.min(osw, ow); + var nswi = double.min(nsw, nw); + + double oshi = 0, nshi = 0; + + if (ow != 0) + { + oshi = oswi / ow * oh; + } + + if (nw != 0) + { + nshi = nswi / nw * nh; + } + + return Sizing() { + old_size = Size() { + width = (int)osw, + + image_width = (int)oswi, + image_height = (int)oshi + }, + + new_size = Size() { + width = (int)nsw, + + image_width = (int)nswi, + image_height = (int)nshi + } + }; + } + + protected override void style_updated() + { + d_old_size_layout = null; + d_new_size_layout = null; + } + + protected override void get_preferred_height_for_width(int width, out int minimum_height, out int natural_height) + { + var sizing = get_sizing(width); + var h = double.max(sizing.old_size.image_height, sizing.new_size.image_height); + + var ol = old_size_layout; + var nl = new_size_layout; + + int osw = 0, osh = 0, nsw = 0, nsh = 0; + + if (ol != null) + { + ol.get_pixel_size(out osw, out osh); + } + + if (nl != null) + { + nl.get_pixel_size(out nsw, out nsh); + } + + h += TEXT_SPACING + int.max(osh, nsh); + + minimum_height = (int)h; + natural_height = (int)h; + } + + protected override Gtk.SizeRequestMode get_request_mode() + { + return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH; + } + + protected override bool draw(Cairo.Context cr) + { + var window = get_window(); + + Gtk.Allocation alloc; + get_allocation(out alloc); + + var sizing = get_sizing(alloc.width); + + var old_surface = cache.get_old_surface(window); + var new_surface = cache.get_new_surface(window); + + var ctx = get_style_context(); + + ctx.render_background(cr, alloc.x, alloc.y, alloc.width, alloc.height); + + double max_height = double.max(sizing.old_size.image_height, sizing.new_size.image_height); + + if (old_surface != null) + { + var x = (sizing.old_size.width - sizing.old_size.image_width) * 2 / 3; + var y = (max_height - sizing.old_size.image_height) / 2; + + cr.set_source_surface(old_surface, x, y); + cr.paint(); + + Pango.Rectangle rect; + + old_size_layout.get_pixel_extents(null, out rect); + + ctx.render_layout(cr, + x + rect.x + (sizing.old_size.image_width - rect.width) / 2, + rect.y + max_height + TEXT_SPACING, + old_size_layout); + } + + if (new_surface != null) + { + var x = (sizing.new_size.width - sizing.new_size.image_width) * 1 / 3; + var y = (max_height - sizing.new_size.image_height) / 2; + + if (cache.old_pixbuf != null) + { + x += sizing.old_size.width + spacing; + } + + cr.set_source_surface(new_surface, x, y); + cr.paint(); + + Pango.Rectangle rect; + + new_size_layout.get_pixel_extents(null, out rect); + + ctx.render_layout(cr, + x + rect.x + (sizing.new_size.image_width - rect.width) / 2, + rect.y + max_height + TEXT_SPACING, + new_size_layout); + } + + return true; + } + + protected override void realize() + { + base.realize(); + queue_resize(); + } +} diff --git a/libgitg/gitg-diff-image-slider.vala b/libgitg/gitg-diff-image-slider.vala new file mode 100644 index 00000000..4179f1d4 --- /dev/null +++ b/libgitg/gitg-diff-image-slider.vala @@ -0,0 +1,94 @@ +/* + * This file is part of gitg + * + * Copyright (C) 2016 - Jesse van den Kieboom + * + * gitg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * gitg 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gitg. If not, see . + */ + +class Gitg.DiffImageSlider : DiffImageComposite +{ + private double d_position; + + public double position + { + get { return d_position; } + + set + { + var newpos = double.max(0, double.min(value, 1)); + + if (newpos != d_position) + { + d_position = newpos; + queue_draw(); + } + } + + default = 0.5; + } + + protected override bool draw(Cairo.Context cr) + { + base.draw(cr); + + var window = get_window(); + + Gtk.Allocation alloc; + get_allocation(out alloc); + + int image_width, image_height; + get_sizing(alloc.width, out image_width, out image_height); + + var old_surface = cache.get_old_surface(window); + var new_surface = cache.get_new_surface(window); + + int x = (alloc.width - image_width) / 2; + int y = 0; + + int pos = (int)(image_width * position); + + if (old_surface != null) + { + cr.save(); + { + cr.rectangle(x, y, pos, image_height); + cr.clip(); + cr.set_source_surface(old_surface, x, y); + cr.paint(); + } + cr.restore(); + } + + if (new_surface != null) + { + cr.save(); + { + cr.rectangle(x + pos, y, image_width - pos, image_height); + cr.clip(); + cr.set_source_surface(new_surface, x, y); + cr.paint(); + } + cr.restore(); + } + + return true; + } + + protected override void realize() + { + base.realize(); + queue_resize(); + } +} diff --git a/libgitg/gitg-diff-image-surface-cache.vala b/libgitg/gitg-diff-image-surface-cache.vala new file mode 100644 index 00000000..f9eff5aa --- /dev/null +++ b/libgitg/gitg-diff-image-surface-cache.vala @@ -0,0 +1,29 @@ +/* + * This file is part of gitg + * + * Copyright (C) 2016 - Jesse van den Kieboom + * + * gitg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * gitg 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gitg. If not, see . + */ + +interface Gitg.DiffImageSurfaceCache : Object +{ + public abstract Gdk.Pixbuf? old_pixbuf { get; construct set; } + public abstract Gdk.Pixbuf? new_pixbuf { get; construct set; } + + public abstract Gdk.Window window { get; construct set; } + + public abstract Cairo.Surface? get_old_surface(Gdk.Window window); + public abstract Cairo.Surface? get_new_surface(Gdk.Window window); +} diff --git a/libgitg/gitg-diff-view-file-renderer-image.vala b/libgitg/gitg-diff-view-file-renderer-image.vala new file mode 100644 index 00000000..018efe00 --- /dev/null +++ b/libgitg/gitg-diff-view-file-renderer-image.vala @@ -0,0 +1,152 @@ +/* + * This file is part of gitg + * + * Copyright (C) 2016 - Jesse van den Kieboom + * + * gitg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * gitg 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gitg. If not, see . + */ + +[GtkTemplate (ui = "/org/gnome/gitg/ui/gitg-diff-view-file-renderer-image.ui")] +class Gitg.DiffViewFileRendererImage : Gtk.Grid, DiffViewFileRenderer +{ + public Ggit.DiffDelta? delta { get; construct set; } + public Repository repository { get; construct set; } + + [GtkChild( name = "diff_image_side_by_side" )] + private Gitg.DiffImageSideBySide d_diff_image_side_by_side; + + [GtkChild( name = "diff_image_slider" )] + private Gitg.DiffImageSlider d_diff_image_slider; + + [GtkChild( name = "scale_slider_adjustment" )] + private Gtk.Adjustment d_scale_slider_adjustment; + + [GtkChild( name = "diff_image_overlay" )] + private Gitg.DiffImageOverlay d_diff_image_overlay; + + [GtkChild( name = "scale_overlay_adjustment" )] + private Gtk.Adjustment d_scale_overlay_adjustment; + + [GtkChild( name = "diff_image_difference" )] + private Gitg.DiffImageDifference d_diff_image_difference; + + [GtkChild( name = "stack_switcher" )] + private Gtk.StackSwitcher d_stack_switcher; + + private SurfaceCache d_cache; + + public DiffViewFileRendererImage(Repository repository, Ggit.DiffDelta delta) + { + Object(repository: repository, delta: delta); + } + + construct + { + d_cache = new SurfaceCache(pixbuf_for_file(delta.get_old_file()), + pixbuf_for_file(delta.get_new_file())); + + d_diff_image_side_by_side.cache = d_cache; + d_diff_image_slider.cache = d_cache; + d_diff_image_overlay.cache = d_cache; + d_diff_image_difference.cache = d_cache; + + if (d_cache.old_pixbuf == null || d_cache.new_pixbuf == null || + d_cache.old_pixbuf.get_width() != d_cache.new_pixbuf.get_width() || + d_cache.old_pixbuf.get_height() != d_cache.new_pixbuf.get_height()) + { + d_stack_switcher.sensitive = false; + } + + d_scale_slider_adjustment.bind_property("value", d_diff_image_slider, "position", BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE); + d_scale_overlay_adjustment.bind_property("value", d_diff_image_overlay, "alpha", BindingFlags.DEFAULT | BindingFlags.SYNC_CREATE); + } + + private Gdk.Pixbuf? pixbuf_for_file(Ggit.DiffFile file) + { + if ((file.get_flags() & Ggit.DiffFlag.VALID_ID) == 0 || file.get_oid().is_zero()) + { + return null; + } + + Ggit.Blob blob; + + try + { + blob = repository.lookup(file.get_oid()); + } + catch (Error e) + { + stderr.printf(@"ERROR: failed to load image blob: $(e.message)\n"); + return null; + } + + var stream = new MemoryInputStream.from_data(blob.get_raw_content(), null); + + try + { + return new Gdk.Pixbuf.from_stream(stream); + } + catch (Error e) + { + stderr.printf(@"ERROR: failed to create pixbuf: $(e.message)\n"); + return null; + } + } + + public void add_hunk(Ggit.DiffHunk hunk, Gee.ArrayList lines) + { + } + + private class SurfaceCache : Object, Gitg.DiffImageSurfaceCache { + private Cairo.Surface? d_old_surface; + private Cairo.Surface? d_new_surface; + + public Gdk.Pixbuf? old_pixbuf { get; construct set; } + public Gdk.Pixbuf? new_pixbuf { get; construct set; } + + public Gdk.Window window { get; construct set; } + + public SurfaceCache(Gdk.Pixbuf? old_pixbuf, Gdk.Pixbuf? new_pixbuf) + { + Object(old_pixbuf: old_pixbuf, new_pixbuf: new_pixbuf); + } + + public Cairo.Surface? get_old_surface(Gdk.Window window) + { + return get_cached_surface(window, old_pixbuf, ref d_old_surface); + } + + public Cairo.Surface? get_new_surface(Gdk.Window window) + { + return get_cached_surface(window, new_pixbuf, ref d_new_surface); + } + + private Cairo.Surface? get_cached_surface(Gdk.Window window, Gdk.Pixbuf? pixbuf, ref Cairo.Surface? cached) + { + if (pixbuf == null) + { + return null; + } + + if (cached == null) + { + cached = Gdk.cairo_surface_create_from_pixbuf(pixbuf, 0, window); + } + + return cached; + } + } +} + +// ex:ts=4 noet diff --git a/libgitg/gitg-diff-view-file.vala b/libgitg/gitg-diff-view-file.vala index 1d4dc002..afec0608 100644 --- a/libgitg/gitg-diff-view-file.vala +++ b/libgitg/gitg-diff-view-file.vala @@ -128,6 +128,11 @@ class Gitg.DiffViewFile : Gtk.Grid public DiffViewFile.image(Repository? repository, Ggit.DiffDelta delta) { this(repository, delta); + + this.renderer = new DiffViewFileRendererImage(repository, delta); + this.renderer.show(); + + d_diff_stat_file.hide(); } protected override void constructed() diff --git a/libgitg/gitg-diff-view.vala b/libgitg/gitg-diff-view.vala index fedde41f..28657755 100644 --- a/libgitg/gitg-diff-view.vala +++ b/libgitg/gitg-diff-view.vala @@ -52,6 +52,8 @@ public class Gitg.DiffView : Gtk.Grid private uint d_reveal_options_timeout; private uint d_unreveal_options_timeout; + private static Gee.HashSet s_image_mime_types; + public Ggit.DiffOptions options { get @@ -224,6 +226,16 @@ public class Gitg.DiffView : Gtk.Grid static construct { + s_image_mime_types = new Gee.HashSet(); + + foreach (var format in Gdk.Pixbuf.get_formats()) + { + foreach (var mime_type in format.get_mime_types()) + { + s_image_mime_types.add(mime_type); + } + } + try { s_message_regexp = new Regex(".*[\\R\\s]*(?P(?:.|\\R)*?)\\s*$"); @@ -336,13 +348,13 @@ public class Gitg.DiffView : Gtk.Grid } } - private string? primary_path(Gitg.DiffViewFile f) + private string? primary_path(Ggit.DiffDelta delta) { - var path = f.delta.get_old_file().get_path(); + var path = delta.get_old_file().get_path(); if (path == null) { - path = f.delta.get_new_file().get_path(); + path = delta.get_new_file().get_path(); } return path; @@ -479,7 +491,34 @@ public class Gitg.DiffView : Gtk.Grid current_is_binary = true; } - if (current_is_binary) + string? mime_type_for_image = null; + + if (info == null || info.new_file_content_type == null) + { + // Guess mime type from old file name in the case of a deleted file + var oldpath = delta.get_old_file().get_path(); + + if (oldpath != null) + { + bool uncertain; + var ctype = ContentType.guess(Path.get_basename(oldpath), null, out uncertain); + + if (ctype != null) + { + mime_type_for_image = ContentType.get_mime_type(ctype); + } + } + } + else + { + mime_type_for_image = ContentType.get_mime_type(info.new_file_content_type); + } + + if (mime_type_for_image != null && s_image_mime_types.contains(mime_type_for_image)) + { + current_file = new Gitg.DiffViewFile.image(repository, delta); + } + else if (current_is_binary) { current_file = new Gitg.DiffViewFile.binary(repository, delta); } @@ -550,7 +589,7 @@ public class Gitg.DiffView : Gtk.Grid if (preserve_expanded && f.expanded) { - var path = primary_path(f); + var path = primary_path(f.delta); if (path != null) { @@ -567,7 +606,7 @@ public class Gitg.DiffView : Gtk.Grid for (var i = 0; i < files.size; i++) { var file = files[i]; - var path = primary_path(file); + var path = primary_path(file.delta); file.expanded = d_commit_details.expanded || (path != null && was_expanded.contains(path)); diff --git a/libgitg/resources/resources.xml b/libgitg/resources/resources.xml index 3b75da3e..b47a8e21 100644 --- a/libgitg/resources/resources.xml +++ b/libgitg/resources/resources.xml @@ -5,6 +5,7 @@ ui/gitg-authentication-dialog.ui ui/gitg-diff-view.ui ui/gitg-diff-view-file.ui + ui/gitg-diff-view-file-renderer-image.ui ui/gitg-diff-view-file-renderer-text.ui ui/gitg-diff-view-file-renderer-binary.ui ui/gitg-diff-view-options.ui diff --git a/libgitg/resources/ui/gitg-diff-view-file-renderer-image.ui b/libgitg/resources/ui/gitg-diff-view-file-renderer-image.ui new file mode 100644 index 00000000..7532dbe3 --- /dev/null +++ b/libgitg/resources/ui/gitg-diff-view-file-renderer-image.ui @@ -0,0 +1,123 @@ + + + + + + 0 + 1 + 0.5 + + + 0 + 1 + 0.5 + + diff --git a/libgitg/resources/ui/libgitg-style.css b/libgitg/resources/ui/libgitg-style.css index 494e7fe2..d9d27594 100644 --- a/libgitg/resources/ui/libgitg-style.css +++ b/libgitg/resources/ui/libgitg-style.css @@ -117,3 +117,11 @@ GitgDiffViewOptions { .dark .language-frame { background-color: #535353; } + +GitgDiffImageSideBySide.old { + border: 10px solid #ddddff; +} + +GitgDiffImageSideBySide.new { + border: 10px solid #ddffdd; +}