Explorar el Código

Resize bug fixes

Billy Barrow hace 7 años
padre
commit
1f69b3f2a7
Se han modificado 8 ficheros con 354 adiciones y 102 borrados
  1. 46 0
      PF2/Debounce.py
  2. 1 1
      PF2/Tools/Blur.py
  3. 1 1
      PF2/Tools/Details.py
  4. 1 1
      PF2/Tools/HueEqualiser.py
  5. 2 1
      PF2/Tools/Tonemap.py
  6. 1 0
      PF2/VectorMask/Path.py
  7. 76 77
      PF2/__init__.py
  8. 226 21
      ui/PF2_Activity.glade

+ 46 - 0
PF2/Debounce.py

@@ -0,0 +1,46 @@
+import threading
+import time
+
+class Debouncer:
+    def __init__(self, callback, complete=False, interval=0.5):
+        self.callback = callback
+        self.complete_callback = complete
+        self.interval = interval
+        self.called_while_running = False
+        self.called_during_timeout = False
+        self.timeout_set = False
+        self.running = False
+        self.latest_args = []
+
+    def call(self, *args):
+        self.latest_args = args
+
+        if(self.timeout_set):
+            self.called_during_timeout = True
+
+        elif(self.running):
+            self.called_while_running = True
+
+        else:
+            threading.Thread(target=self._timeout).start()
+
+    def _timeout(self):
+        self.timeout_set = True
+        time.sleep(self.interval)
+        self.timeout_set = False
+
+        if(self.called_during_timeout):
+            self.called_during_timeout = False
+            self._timeout()
+            return
+
+        self.running = True
+        res = self.callback(*self.latest_args)
+        self.running = False
+
+        if(self.called_while_running):
+            self.called_while_running = False
+            self._timeout()
+        
+        elif(self.complete_callback):
+            self.complete_callback(res)

+ 1 - 1
PF2/Tools/Blur.py

@@ -33,7 +33,7 @@ class Blur(Tool.Tool):
             mul = numpy.math.sqrt(size) / 1064.416 #numpy.math.sqrt(1132982.0)
 
             if (strength > 0):
-                blur_size = 2 * round((round(strength*mul) + 1) / 2) - 1
+                blur_size = abs(2 * round((round(strength*mul) + 1) / 2) - 1)
                 if(method == 0):
                     im = cv2.GaussianBlur(im, (int(blur_size), int(blur_size)), 0)
 

+ 1 - 1
PF2/Tools/Details.py

@@ -62,7 +62,7 @@ class Details(Tool.Tool):
                 size = (height * width)
                 mul = numpy.math.sqrt(size) / 1064.416
 
-                blur_size = 2 * round((round(detail*mul) + 1) / 2) - 1
+                blur_size = abs(2 * round((round(detail*mul) + 1) / 2) - 1)
                 blurred = cv2.GaussianBlur(edged, (int(blur_size), int(blur_size)), 0)
             else:
                 blurred = edged

+ 1 - 1
PF2/Tools/HueEqualiser.py

@@ -145,7 +145,7 @@ class HueEqualiser(Tool.Tool):
 
             map = map*255
 
-            blur_size = 2 * round((round(strength * mul) + 1) / 2) - 1
+            blur_size = abs(2 * round((round(strength * mul) + 1) / 2) - 1)
             im = cv2.blur(map, (int(blur_size), int(blur_size)))
             return im/255.0
 

+ 2 - 1
PF2/Tools/Tonemap.py

@@ -44,7 +44,8 @@ class Tonemap(Tool.Tool):
                     size = (height * width)
                     mul = numpy.math.sqrt(size) / 1064.416  # numpy.math.sqrt(1132982.0)
 
-                    blur_size = 2 * round((round((blur/10.0) * mul) + 1) / 2) - 1
+                    blur_size = abs(2 * round((round((blur/10.0) * mul) + 1) / 2) - 1)
+                    print(height, width, mul, blur_size)
                     blurred = cv2.GaussianBlur(inverted, (int(blur_size), int(blur_size)), 0)
                 else:
                     # Or, don't blur

+ 1 - 0
PF2/VectorMask/Path.py

@@ -44,6 +44,7 @@ class Path:
         self.draw_points(map, self.points, 255)
 
         if(self.brush_feather > 1):
+            # TODO, could nuber be negative?
             blur_size = 2 * round((round(self.brush_feather) + 1) / 2) - 1
             map = cv2.blur(map, (int(blur_size), int(blur_size)))
             map = map[:, :, numpy.newaxis]

+ 76 - 77
PF2/__init__.py

@@ -11,7 +11,7 @@ import os
 import traceback
 import uuid
 
-from PF2 import Histogram, Layer
+from PF2 import Histogram, Layer, Debounce
 from PF2.Tools import BlackWhite
 from PF2.Tools import Colours
 from PF2.Tools import Contrast
@@ -83,7 +83,11 @@ class PF2(Activity.Activity):
             "layer_opacity",
             "layer_opacity_scale",
             "layer_blend_mode",
-            "viewport"
+            "viewport",
+            "preview_stack",
+            "export_progress",
+            "export_progress_label",
+            "resize_progress"
         ]
 
         for component in components:
@@ -115,7 +119,7 @@ class PF2(Activity.Activity):
         self.ui["original_toggle"].set_sensitive(False)
         self.ui["export_image"].set_sensitive(False)
 
-        # Setup editor variables
+        # Setup editor variaexport
         self.is_editing = False
         self.has_loaded = False
         self.image_path = ""
@@ -154,6 +158,8 @@ class PF2(Activity.Activity):
         self.current_processing_layer_name = "base"
         self.current_processing_layer_index = 0
         self.update_image_task_id = ""
+        self.resize_debounce = Debounce.Debouncer(self.resize_preview)
+        self.render_debounce = Debounce.Debouncer(self.update_image)
 
         # Setup Open Dialog
         self.ui["open_window"].set_transient_for(self.root)
@@ -214,7 +220,9 @@ class PF2(Activity.Activity):
         else:
             self.image_path = path
 
-        self.show_message("Loading Photo…", "Please wait while PhotoFiddle loads your photo", True)
+        #self.show_message("Loading Photo…", "Please wait while PhotoFiddle loads your photo", True)
+        self.ui["preview_stack"].set_visible_child_name("load")
+
         self.root.get_titlebar().set_subtitle("%s…" % (self.image_path))
 
         # Delete all layers, except the base
@@ -260,9 +268,14 @@ class PF2(Activity.Activity):
             w = (self.ui["scroll_window"].get_allocated_width() - 12) * self.ui["zoom"].get_value()
             h = (self.ui["scroll_window"].get_allocated_height() - 12) * self.ui["zoom"].get_value()
             if(self.pheight != h) or (self.pwidth != w):
-                thread = threading.Thread(target=self.resize_preview,
-                                          args=(w, h))
-                thread.start()
+                print("Resize to w%i h%i" % (w, h))
+                # Don't dismiss the render progress or load progress
+                page_name = self.ui["preview_stack"].get_visible_child_name()
+                if(page_name != "render"):
+                    self.ui["preview_stack"].set_visible_child_name("resize")
+                # Reset the resize progress
+                self.ui["resize_progress"].set_fraction(0.0)
+                self.resize_debounce.call(w, h)
 
     def on_preview_toggled(self, sender):
         if(sender.get_active()):
@@ -316,12 +329,9 @@ class PF2(Activity.Activity):
     def on_layer_change(self, layer):
         print(layer, "changed")
         if(self.has_loaded):
-            if(not self.change_occurred):
-                self.change_occurred = True
-                thread = threading.Thread(target=self.update_image, args=(False, layer))
-                thread.start()
-            else:
-                self.additional_change_occurred = True
+            # TODO this could be problematic if someone somehow rapidly
+            # switches from editing one layer to another
+            self.render_debounce.call(layer)
 
     def on_layer_mask_change(self, layer):
         if(self.has_loaded):
@@ -342,21 +352,11 @@ class PF2(Activity.Activity):
 
     def on_lower_peak_toggled(self, sender):
         self.lower_peak_on = sender.get_active()
-        if (self.lower_peak_on):
-            thread = threading.Thread(target=self.process_peaks, args=(True,))
-            thread.start()
-        else:
-            thread = threading.Thread(target=self.update_image, args=(True,))
-            thread.start()
+        self.update_image()
 
     def on_upper_peak_toggled(self, sender):
         self.upper_peak_on = sender.get_active()
-        if (self.lower_peak_on):
-            thread = threading.Thread(target=self.process_peaks, args=(True,))
-            thread.start()
-        else:
-            thread = threading.Thread(target=self.update_image, args=(True,))
-            thread.start()
+        self.update_image()
 
     def image_opened(self):
         self.root.get_titlebar().set_subtitle("%s (%s Bit)" % (self.image_path, self.bit_depth))
@@ -370,6 +370,7 @@ class PF2(Activity.Activity):
         self.is_exporting = False
         self.on_export_state_change()
         self.show_message("Export Complete!", "Your photo has been exported to '%s'" % filename)
+        self.ui["preview_stack"].set_visible_child_name("preview")
 
     def on_export_state_change(self):
         self.ui["control_reveal"].set_sensitive(not self.is_exporting)
@@ -379,6 +380,9 @@ class PF2(Activity.Activity):
     def on_export_started(self):
         self.is_exporting = True
         self.on_export_state_change()
+        self.ui["preview_stack"].set_visible_child_name("export")
+        self.ui["export_progress"].set_fraction(0.0)
+        self.ui["export_progress_label"].set_text("Getting ready to render")
 
     def update_undo_state(self):
         self.ui["undo"].set_sensitive((self.undo_position > 0) and (not self.is_working))
@@ -463,13 +467,6 @@ class PF2(Activity.Activity):
             nh = height * ratio
 
 
-            # Do quick ui resize
-            # if(self.image != None):
-            #     # If we have an edited version, show that
-            #     image = cv2.resize(self.image, (int(nw), int(nh)), interpolation=cv2.INTER_NEAREST)
-            #     self.pimage = self.pimage = self.create_pixbuf(image)
-            #     GLib.idle_add(self.show_current)
-
             if(type(self.pimage) == numpy.ndarray and not self.is_scaling):
                 self.is_scaling = True
                 # If we have an edited version, show that
@@ -493,53 +490,51 @@ class PF2(Activity.Activity):
             self.image = self.original_image.copy()
 
             # Update image
-            if (not self.change_occurred):
-                self.change_occurred = True
-                self.update_image()
-            else:
-                self.additional_change_occurred = True
+            self.render_debounce.call(None, self.update_resize_progress)
 
+    def update_resize_progress(self, name, count, current):
+        fraction = current+(self.current_processing_layer_index*count)
+        fraction = fraction / (count*(len(self.layers)-1))
 
+        GLib.idle_add(self.ui["resize_progress"].set_fraction, fraction)
 
-    def update_image(self, immediate=False, changed_layer=None):
-        if(not immediate):
-            time.sleep(0.5)
-        self.additional_change_occurred = False
+
+
+    def update_image(self, changed_layer=None, callback=None):
         self.is_working = True
         GLib.idle_add(self.update_undo_state)
         GLib.idle_add(self.start_work)
         image = numpy.copy(self.original_image)
         rst = time.time()
-        task_id = uuid.uuid4()
-        self.update_image_task_id = task_id
-        image = self.run_stack(image, changed_layer=changed_layer)
-        if(task_id == self.update_image_task_id):
-            self.image = image
-            if(type(self.image) != numpy.ndarray):
-                GLib.idle_add(self.stop_work)
-                self.is_working = False
-                self.change_occurred = False
-                GLib.idle_add(self.update_undo_state)
-                print("self.image == None")
-                GLib.idle_add(self.show_message, "Image Render Failed…",
-                              "PhotoFiddle encountered an internal error and was unable to render the image… Please try again.")
-                raise Exception()
+        image = self.run_stack(image, callback=callback, changed_layer=changed_layer)
+        self.image = image
+        if(type(self.image) != numpy.ndarray):
+            GLib.idle_add(self.stop_work)
+            self.is_working = False
+            self.change_occurred = False
+            GLib.idle_add(self.update_undo_state)
+            print("self.image == %s" % type(self.image))
+            #GLib.idle_add(self.show_message, "Image Render Failed…",
+            #              "PhotoFiddle encountered an internal error and was unable to render the image… Please try again.")
+            raise Exception()
+
+        self.process_mask()
+        self.image_is_dirty = False
 
-            self.process_mask()
-            self.image_is_dirty = False
-            if(self.additional_change_occurred):
-                self.update_image()
-            else:
-                self.save_image_data()
-                threading.Thread(target=self.draw_hist, args=(self.image,)).start()
+        self.save_image_data()
+        threading.Thread(target=self.draw_hist, args=(self.image,)).start()
 
-                self.process_peaks()
-                self.update_preview()
-                GLib.idle_add(self.stop_work)
-                self.is_working = False
-                GLib.idle_add(self.update_undo_state)
-                self.change_occurred = False
-                self.undoing = False
+        self.process_peaks()
+        self.update_preview()
+        GLib.idle_add(self.stop_work)
+        self.is_working = False
+        GLib.idle_add(self.update_undo_state)
+        page_name = self.ui["preview_stack"].get_visible_child_name()
+        if(page_name != "render" and page_name != "load"):
+            self.ui["preview_stack"].set_visible_child_name("preview")
+
+        self.change_occurred = False
+        self.undoing = False
 
 
 
@@ -697,6 +692,8 @@ class PF2(Activity.Activity):
 
                     self.undo_stack = [data,]
                     self.undo_position = 0
+                    # Wait the timeout of the debouncer to prevent double rendering
+                    time.sleep(self.render_debounce.interval)
                     loadDefaults = False
             except:
                 GLib.idle_add(self.show_message,"Unable to load previous edits…",
@@ -747,22 +744,24 @@ class PF2(Activity.Activity):
 
     def get_export_image(self, w, h):
         GLib.idle_add(self.on_export_started)
-        GLib.idle_add(self.show_message, "Exporting Photo", "Please wait…", True, True)
         img = cv2.imread(self.image_path, 2 | 1)
         img = cv2.resize(img, (int(w), int(h)), interpolation=cv2.INTER_AREA)
         img = self.run_stack(img, self.export_progress_callback)
-        GLib.idle_add(self.show_message, "Exporting Photo", "Saving to filesystem…", True, True)
-        GLib.idle_add(self.update_message_progress, 1, 1)
+        GLib.idle_add(self.ui["export_progress_label"].set_text, "Saving to filesystem")
+        GLib.idle_add(self.ui["export_progress"].set_fraction, 1.0)
         return img
 
     def export_progress_callback(self, name, count, current):
         layer_name = self.current_processing_layer_name
         if(layer_name == "base"):
-            layer_name = "Base Layer"
-        GLib.idle_add(self.show_message, "Exporting Photo", "%s: %s" % (layer_name,
-                                                                                    name), True, True)
-        GLib.idle_add(self.update_message_progress, current+(self.current_processing_layer_index*count),
-                      count*len(self.layers))
+            layer_name = "the base layer"
+
+        GLib.idle_add(self.ui["export_progress_label"].set_text, "Processing %s: %s" % (layer_name, name))
+
+        fraction = current+(self.current_processing_layer_index*count)
+        fraction = fraction / (count*len(self.layers))
+
+        GLib.idle_add(self.ui["export_progress"].set_fraction, fraction)
 
 
 

+ 226 - 21
ui/PF2_Activity.glade

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.20.0 -->
+<!-- Generated with glade 3.20.4 -->
 <interface>
   <requires lib="gtk+" version="3.20"/>
   <object class="GtkHeaderBar" id="PF2_bulk_open_header">
@@ -78,6 +78,9 @@
         </child>
       </object>
     </child>
+    <child type="titlebar">
+      <placeholder/>
+    </child>
   </object>
   <object class="GtkWindow" id="PF2_open_window">
     <property name="width_request">700</property>
@@ -93,6 +96,9 @@
         <property name="filter">PF2_filefilter</property>
       </object>
     </child>
+    <child type="titlebar">
+      <placeholder/>
+    </child>
   </object>
   <object class="GtkAdjustment" id="PF2_layer_opacity">
     <property name="upper">1</property>
@@ -118,39 +124,241 @@
     <property name="can_focus">False</property>
     <property name="margin_top">1</property>
     <child>
-      <object class="GtkScrolledWindow" id="PF2_scroll_window">
+      <object class="GtkStack" id="PF2_preview_stack">
         <property name="width_request">500</property>
         <property name="height_request">500</property>
         <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="margin_left">18</property>
-        <property name="margin_right">18</property>
-        <property name="margin_top">18</property>
-        <property name="margin_bottom">18</property>
-        <property name="hexpand">True</property>
-        <property name="vexpand">True</property>
-        <property name="shadow_type">in</property>
+        <property name="can_focus">False</property>
+        <property name="transition_type">crossfade</property>
         <child>
-          <object class="GtkViewport" id="PF2_viewport">
+          <object class="GtkScrolledWindow" id="PF2_scroll_window">
+            <property name="width_request">500</property>
+            <property name="height_request">500</property>
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
+            <property name="can_focus">True</property>
+            <property name="margin_left">18</property>
+            <property name="margin_right">18</property>
+            <property name="margin_top">18</property>
+            <property name="margin_bottom">18</property>
+            <property name="hexpand">True</property>
+            <property name="vexpand">True</property>
+            <property name="shadow_type">in</property>
             <child>
-              <object class="GtkEventBox" id="PF2_preview_eventbox">
+              <object class="GtkViewport" id="PF2_viewport">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="above_child">True</property>
                 <child>
-                  <object class="GtkImage" id="PF2_preview">
+                  <object class="GtkEventBox" id="PF2_preview_eventbox">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="halign">center</property>
-                    <property name="valign">center</property>
-                    <property name="pixbuf">logo.png</property>
+                    <property name="above_child">True</property>
+                    <child>
+                      <object class="GtkImage" id="PF2_preview">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="halign">center</property>
+                        <property name="valign">center</property>
+                        <property name="pixbuf">logo.png</property>
+                      </object>
+                    </child>
                   </object>
                 </child>
               </object>
             </child>
           </object>
+          <packing>
+            <property name="name">preview</property>
+            <property name="title" translatable="yes">page0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="PF_preview_resize">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkSpinner">
+                <property name="width_request">64</property>
+                <property name="height_request">64</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="active">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Resizing Preview…</property>
+                <attributes>
+                  <attribute name="scale" value="1.5"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkProgressBar" id="PF2_resize_progress">
+                <property name="width_request">400</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_top">8</property>
+                <property name="text" translatable="yes">Getting ready to render</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="name">resize</property>
+            <property name="title" translatable="yes">page1</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="PF_load_image">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkSpinner">
+                <property name="width_request">64</property>
+                <property name="height_request">64</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="active">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Loading Photo…</property>
+                <attributes>
+                  <attribute name="scale" value="1.5"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_top">8</property>
+                <property name="label" translatable="yes">Please wait while PhotoFiddle loads your photo</property>
+                <attributes>
+                  <attribute name="scale" value="1"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="name">load</property>
+            <property name="title" translatable="yes">page0</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="PF_export_image_progress">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkSpinner">
+                <property name="width_request">64</property>
+                <property name="height_request">64</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="active">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Exporting Photo…</property>
+                <attributes>
+                  <attribute name="scale" value="1.5"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="PF2_export_progress_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_top">8</property>
+                <property name="label" translatable="yes">Getting ready to render</property>
+                <attributes>
+                  <attribute name="scale" value="1"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkProgressBar" id="PF2_export_progress">
+                <property name="width_request">400</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="margin_top">8</property>
+                <property name="text" translatable="yes">Getting ready to render</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="name">export</property>
+            <property name="title" translatable="yes">page0</property>
+            <property name="position">3</property>
+          </packing>
         </child>
       </object>
       <packing>
@@ -172,9 +380,6 @@
           <object class="GtkBox">
             <property name="visible">True</property>
             <property name="can_focus">False</property>
-            <child>
-              <placeholder/>
-            </child>
             <child>
               <object class="GtkSeparator">
                 <property name="visible">True</property>