ソースを参照

Milestone: Bug fixes, blur tool implemented. Layers are now a complete
feature, this branch can now be merged with Master.

Signed-off-by: Billy Barrow <billyb@pcthingz.com>

Billy Barrow 8 年 前
コミット
f5654f827f
4 ファイル変更154 行追加23 行削除
  1. 21 3
      PF2/Layer.py
  2. 44 0
      PF2/Tools/Blur.py
  3. 45 15
      PF2/__init__.py
  4. 44 5
      ui/PF2_Activity.glade

+ 21 - 3
PF2/Layer.py

@@ -13,10 +13,14 @@ class Layer:
         self.selected_tool = 0
         self.editable = not base
         self.opacity = 1.0
+        self.blend_mode = "additive"
+        if(not self.editable):
+            self.blend_mode = "overlay"
 
         self.selector_row = None
 
         self.tool_box = Gtk.FlowBox()
+        self.tool_box.set_selection_mode(Gtk.SelectionMode.NONE)
         self.tool_box.set_orientation(1)
 
         self.tool_stack = Gtk.Stack()
@@ -69,6 +73,7 @@ class Layer:
         layerDict["mask"] = self.mask.get_vector_mask_dict()
         layerDict["enabled"] = self.enabled
         layerDict["opacity"] = self.opacity
+        layerDict["blend-mode"] = self.blend_mode
 
         return layerDict
 
@@ -93,13 +98,22 @@ class Layer:
             # Load Opacity Fraction
             self.opacity = dict["opacity"]
 
+            # Load Blend Mode
+            self.blend_mode = dict["blend-mode"]
+
 
     def render_layer(self, baseImage, image, callback=None):
         # Only process if the layer is enabled
         if(self.enabled and self.opacity != 0.0):
-            # We are passed a base image (original)
-            # Make a copy of this to pass through the tools
-            layer = baseImage.copy()
+            layer = None
+
+            if(self.blend_mode == "overlay"):
+                # We are passed a base image (original)
+                # Make a copy of this to pass through the tools
+                layer = baseImage.copy()
+            else: # if(self.blend_mode == "additive"):
+                # Layer is additive, make copy of current working image
+                layer = image.copy()
 
             # Process the Layer
             ntools = len(self.tools)
@@ -157,4 +171,8 @@ class Layer:
 
     def set_enabled(self, enabled):
         self.enabled = enabled
+        self.on_tool_change(None, None)
+
+    def set_blending_mode(self, mode):
+        self.blend_mode = mode
         self.on_tool_change(None, None)

+ 44 - 0
PF2/Tools/Blur.py

@@ -0,0 +1,44 @@
+import cv2
+import numpy
+
+import Tool
+from PF2.Tools import Contrast
+
+
+class Blur(Tool.Tool):
+    def on_init(self):
+        self.id = "blur"
+        self.name = "Blur"
+        self.icon_path = "ui/PF2_Icons/Blur.png"
+        self.properties = [
+            # Detailer
+            Tool.Property("enabled", "Blur", "Header", False, has_toggle=True, has_button=False),
+            Tool.Property("method", "Method", "Combo", 1, options=[
+                "Guassian",
+                "Average",
+                "Median"
+            ]),
+            Tool.Property("strength", "Strength", "Slider", 5, max=100, min=0),
+
+        ]
+
+    def on_update(self, image):
+        im = image
+        if(self.props["enabled"].get_value()):
+            strength = self.props["strength"].get_value()
+            method = self.props["method"].get_value()
+
+            if (strength > 0):
+                blur_size = 2 * round((round(strength) + 1) / 2) - 1
+                if(method == 0):
+                    im = cv2.GaussianBlur(im, (int(blur_size), int(blur_size)), 0)
+
+                elif(method == 1):
+                    im = cv2.blur(im, (int(blur_size), int(blur_size)))
+
+                elif(method == 2):
+                    im = cv2.medianBlur(im, int(blur_size))
+        return im
+
+
+

+ 45 - 15
PF2/__init__.py

@@ -16,6 +16,7 @@ from PF2.Tools import Contrast
 from PF2.Tools import Details
 from PF2.Tools import Tonemap
 from PF2.Tools import HueEqualiser
+from PF2.Tools import Blur
 
 
 class PF2(Activity.Activity):
@@ -78,7 +79,9 @@ class PF2(Activity.Activity):
             "mask_brush_feather_scale",
             "edit_layer_mask_button",
             "layer_opacity",
-            "layer_opacity_scale"
+            "layer_opacity_scale",
+            "layer_blend_mode",
+            "viewport"
         ]
 
         for component in components:
@@ -93,7 +96,8 @@ class PF2(Activity.Activity):
             Details.Details,
             Colours.Colours,
             HueEqualiser.HueEqualiser,
-            BlackWhite.BlackWhite
+            BlackWhite.BlackWhite,
+            Blur.Blur
         ]
 
 
@@ -136,7 +140,9 @@ class PF2(Activity.Activity):
         self.current_layer_path = None
         self.mousex = 0
         self.mousey = 0
+        self.mousedown = False
         self.layer_order = []
+        self.pre_undo_layer_name = "base"
 
         # Setup Open Dialog
         self.ui["open_window"].set_transient_for(self.root)
@@ -168,8 +174,9 @@ class PF2(Activity.Activity):
         self.ui["edit_layer_mask_button"].connect("toggled", self.edit_mask_toggled)
         self.ui["layer_opacity"].connect("value-changed", self.update_layer_opacity)
         self.ui["scroll_window"].connect_after("draw", self.draw_ui_brush_circle)
-        self.ui["scroll_window"].connect('motion-notify-event', self.mouse_coords_changed)
+        self.ui["scroll_window"].connect_after('motion-notify-event', self.mouse_coords_changed)
         self.ui["mask_brush_size"].connect("value-changed", self.brush_size_changed)
+        self.ui["layer_blend_mode"].connect("changed", self.layer_blend_mode_changed)
 
 
 
@@ -349,11 +356,13 @@ class PF2(Activity.Activity):
         self.ui["redo"].set_sensitive(len(self.undo_stack)-1 > self.undo_position)
 
     def on_undo(self, sender):
+        self.pre_undo_layer_name = self.get_selected_layer().name
         self.undo_position -= 1
         self.update_undo_state()
         self.update_from_undo_stack(self.undo_stack[self.undo_position])
 
     def on_redo(self, sender):
+        self.pre_undo_layer_name = self.get_selected_layer().name
         self.undo_position += 1
         self.update_undo_state()
         self.update_from_undo_stack(self.undo_stack[self.undo_position])
@@ -434,9 +443,8 @@ class PF2(Activity.Activity):
             time.sleep(0.5)
         self.additional_change_occurred = False
         GLib.idle_add(self.start_work)
-        self.image_is_dirty = True
-        self.image = numpy.copy(self.original_image)
-        self.image = self.run_stack(self.image)
+        image = numpy.copy(self.original_image)
+        self.image = self.run_stack(image)
         self.image_is_dirty = False
         if(self.additional_change_occurred):
             self.update_image()
@@ -520,8 +528,8 @@ class PF2(Activity.Activity):
         if(not self.undoing) and (self.has_loaded):
             if(len(self.undo_stack)-1 != self.undo_position):
                 self.undo_stack = self.undo_stack[:self.undo_position+1]
-            if(self.undo_stack[self.undo_position] != layerDict):
-                self.undo_stack += [layerDict,]
+            if(self.undo_stack[self.undo_position] != {"layers": layerDict, "layer-order": layerOrder}):
+                self.undo_stack += [{"layers": layerDict, "layer-order": layerOrder},]
                 self.undo_position = len(self.undo_stack)-1
                 GLib.idle_add(self.update_undo_state)
 
@@ -594,7 +602,7 @@ class PF2(Activity.Activity):
             if (layer_name == "base"):
                 self.base_layer.set_from_layer_dict(data["layers"][layer_name])
             else:
-                ilayer = self.create_layer(layer_name, False)
+                ilayer = self.create_layer(layer_name, False, layer_name == self.pre_undo_layer_name)
                 ilayer.set_from_layer_dict(data["layers"][layer_name])
                 self.show_layers()
 
@@ -660,6 +668,9 @@ class PF2(Activity.Activity):
 
             self.on_layer_change(layer)
 
+            self.mouse_down_coords_changed(widget, event)
+            return True
+
     def new_path(self, widget, event):
         draw = self.ui["mask_draw_toggle"].get_active()
         erase = self.ui["mask_erase_toggle"].get_active()
@@ -691,7 +702,7 @@ class PF2(Activity.Activity):
         layer.set_opacity(sender.get_value())
 
 
-    def create_layer(self, layer_name, is_base):
+    def create_layer(self, layer_name, is_base, select = False):
         layer = Layer.Layer(is_base, layer_name, self.on_layer_change)
         for tool in self.tools:
             tool_instance = tool()
@@ -699,11 +710,11 @@ class PF2(Activity.Activity):
 
         self.layers += [layer,]
 
-        GLib.idle_add(self.create_layer_ui, layer)
+        GLib.idle_add(self.create_layer_ui, layer, select)
 
         return layer
 
-    def create_layer_ui(self, layer):
+    def create_layer_ui(self, layer, select):
         layer_box = Gtk.HBox()
         layer_box.set_hexpand(False)
         layer_box.set_halign(Gtk.Align.START)
@@ -721,6 +732,8 @@ class PF2(Activity.Activity):
 
         layer_label = Gtk.Label()
         layer_label.set_label(layer.name)
+        if(layer.name == "base"):
+            layer_label.set_label("Base Layer")
         layer_label.set_hexpand(True)
         layer_label.set_halign(Gtk.Align.FILL)
         layer_label.set_margin_top(4)
@@ -739,6 +752,9 @@ class PF2(Activity.Activity):
         self.ui["tool_box_stack"].add(layer.tool_box)
         self.ui["tool_stack"].add(layer.tool_stack)
 
+        if(select):
+            self.select_layer(layer)
+
 
     def layer_ui_activated(self, widget, row):
         layer_index = row.get_index()
@@ -747,6 +763,8 @@ class PF2(Activity.Activity):
         self.ui["remove_layer_button"].set_sensitive(self.layers[layer_index].editable)
         self.ui["edit_layer_mask_button"].set_sensitive(self.layers[layer_index].editable)
         self.ui["layer_opacity_scale"].set_sensitive(self.layers[layer_index].editable)
+        self.ui["layer_blend_mode"].set_sensitive(self.layers[layer_index].editable)
+        self.ui["layer_blend_mode"].set_active(["additive", "overlay"].index(self.layers[layer_index].blend_mode))
         self.ui["layer_opacity"].set_value(self.layers[layer_index].opacity)
         if(self.ui["edit_layer_mask_button"].get_active()):
             self.ui["edit_layer_mask_button"].set_active(self.layers[layer_index].editable)
@@ -836,14 +854,26 @@ class PF2(Activity.Activity):
         drawing = self.ui["edit_layer_mask_button"].get_active()
         if(drawing):
             size = self.ui["mask_brush_size"].get_value()
-            context.set_source_rgb(255, 255, 255)
+            if(self.mousedown):
+                context.set_source_rgb(255, 0, 0)
+            else:
+                context.set_source_rgb(255, 255, 255)
             context.arc(self.mousex, self.mousey, size/2.0, 0.0, 2 * numpy.pi);
             context.stroke()
 
     def mouse_coords_changed(self, widget, event):
+        self.mousedown = False
         self.mousex, self.mousey = event.x, event.y
-        print(event.x, event.y)
+        widget.queue_draw()
+
+    def mouse_down_coords_changed(self, widget, event):
+        self.mousedown = True
+        self.mousex, self.mousey = widget.translate_coordinates(self.ui["scroll_window"], event.x, event.y)
         widget.queue_draw()
 
     def brush_size_changed(self, sender):
-        self.ui["scroll_window"].queue_draw()
+        self.ui["scroll_window"].queue_draw()
+
+    def layer_blend_mode_changed(self, sender):
+        layer = self.get_selected_layer()
+        layer.set_blending_mode(sender.get_active_text().lower())

+ 44 - 5
ui/PF2_Activity.glade

@@ -101,7 +101,7 @@
     <property name="page_increment">0.5</property>
   </object>
   <object class="GtkAdjustment" id="PF2_mask_brush_feather">
-    <property name="upper">100</property>
+    <property name="upper">200</property>
     <property name="value">10</property>
     <property name="step_increment">1</property>
     <property name="page_increment">10</property>
@@ -131,7 +131,7 @@
         <property name="vexpand">True</property>
         <property name="shadow_type">in</property>
         <child>
-          <object class="GtkViewport">
+          <object class="GtkViewport" id="PF2_viewport">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
             <child>
@@ -346,6 +346,45 @@
                                 <property name="position">0</property>
                               </packing>
                             </child>
+                            <child>
+                              <object class="GtkBox">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="spacing">8</property>
+                                <child>
+                                  <object class="GtkLabel">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="label" translatable="yes">Blending Mode</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkComboBoxText" id="PF2_layer_blend_mode">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <items>
+                                      <item translatable="yes">Additive</item>
+                                      <item translatable="yes">Overlay</item>
+                                    </items>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">True</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
                             <child>
                               <object class="GtkScrolledWindow">
                                 <property name="height_request">100</property>
@@ -369,7 +408,7 @@
                               <packing>
                                 <property name="expand">False</property>
                                 <property name="fill">True</property>
-                                <property name="position">1</property>
+                                <property name="position">2</property>
                               </packing>
                             </child>
                             <child>
@@ -482,7 +521,7 @@
                               <packing>
                                 <property name="expand">False</property>
                                 <property name="fill">True</property>
-                                <property name="position">2</property>
+                                <property name="position">3</property>
                               </packing>
                             </child>
                             <child>
@@ -493,7 +532,7 @@
                               <packing>
                                 <property name="expand">False</property>
                                 <property name="fill">True</property>
-                                <property name="position">3</property>
+                                <property name="position">4</property>
                               </packing>
                             </child>
                           </object>