Layer.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. from gi.repository import Gtk, GLib
  2. import PF2.VectorMask as VectorMask
  3. import cv2, numpy
  4. class Layer:
  5. def __init__(self, base, name, on_change):
  6. self.mask = VectorMask.VectorMask()
  7. self.tools = []
  8. self.tool_map = {}
  9. self.name = name
  10. self.enabled = True
  11. self.selected_tool = 0
  12. self.editable = not base
  13. self.opacity = 1.0
  14. self.blend_mode = "additive"
  15. if(not self.editable):
  16. self.blend_mode = "overlay"
  17. self.selector_row = None
  18. self.tool_box = Gtk.FlowBox()
  19. self.tool_box.set_selection_mode(Gtk.SelectionMode.NONE)
  20. self.tool_box.set_orientation(1)
  21. self.tool_stack = Gtk.Stack()
  22. self.tool_stack.set_transition_type(6)
  23. self.tool_stack.set_homogeneous(False)
  24. self.layer_copy = None
  25. self.layer_changed = on_change
  26. def add_tool(self, tool):
  27. self.tool_box.add(tool.tool_button)
  28. self.tool_stack.add(tool.widget)
  29. self.tool_map[tool.tool_button] = tool
  30. tool.tool_button.connect("clicked", self.on_tool_button_clicked)
  31. tool.connect_on_change(self.on_tool_change)
  32. self.tools += [tool,]
  33. if(len(self.tools) == 1):
  34. tool.tool_button.set_active(True)
  35. def on_tool_change(self, tool, prop):
  36. self.layer_changed(self)
  37. def on_tool_button_clicked(self, sender):
  38. if(sender.get_active()):
  39. self.tool_stack.set_visible_child(self.tool_map[sender].widget)
  40. for key in self.tool_map:
  41. if(key != sender):
  42. key.set_active(False)
  43. elif(self.tool_stack.get_visible_child() == self.tool_map[sender].widget):
  44. sender.set_active(True)
  45. def get_layer_dict(self):
  46. layerDict = {}
  47. # Get tool values
  48. toolDict = {}
  49. for tool in self.tools:
  50. toolDict[tool.id] = tool.get_properties_as_dict()
  51. layerDict["tools"] = toolDict
  52. layerDict["name"] = self.name
  53. layerDict["mask"] = self.mask.get_vector_mask_dict()
  54. layerDict["enabled"] = self.enabled
  55. layerDict["opacity"] = self.opacity
  56. layerDict["blend-mode"] = self.blend_mode
  57. return layerDict
  58. def set_from_layer_dict(self, dict):
  59. # Load Tool Data
  60. for tool in self.tools:
  61. if(tool.id in dict["tools"]):
  62. GLib.idle_add(tool.load_properties, dict["tools"][tool.id])
  63. # The base layer only has tools.
  64. if(self.editable):
  65. # Load Mask Vectors
  66. self.mask.set_from_vector_mask_dict(dict["mask"])
  67. # Load Layer Name
  68. self.name = dict["name"]
  69. # Load Enabled State
  70. self.enabled = dict["enabled"]
  71. # Load Opacity Fraction
  72. self.opacity = dict["opacity"]
  73. # Load Blend Mode
  74. self.blend_mode = dict["blend-mode"]
  75. def render_layer(self, baseImage, image, callback=None):
  76. # Only process if the layer is enabled
  77. if(self.enabled and self.opacity != 0.0):
  78. layer = None
  79. if(self.blend_mode == "overlay"):
  80. # We are passed a base image (original)
  81. # Make a copy of this to pass through the tools
  82. layer = baseImage.copy()
  83. else: # if(self.blend_mode == "additive"):
  84. # Layer is additive, make copy of current working image
  85. layer = image.copy()
  86. # Process the Layer
  87. ntools = len(self.tools)
  88. count = 1
  89. for tool in self.tools:
  90. if (callback != None):
  91. # For showing progress
  92. callback(tool.name, ntools, count - 1)
  93. # Call the tool's image processing function
  94. layer = tool.on_update(layer)
  95. assert type(layer) == numpy.ndarray
  96. count += 1
  97. # Base layer needs no mask processing
  98. if(not self.editable):
  99. image = layer
  100. # Here we would blend with the mask
  101. else:
  102. mask_map = self.mask.get_mask_map()
  103. if(type(mask_map) == numpy.ndarray):
  104. # Only process if there is actually an existing mask
  105. height, width = layer.shape[:2]
  106. mask_map = cv2.resize(mask_map, (width, height), interpolation=cv2.INTER_AREA)
  107. mask_map = mask_map * self.opacity
  108. inverted_map = (mask_map * -1) + 1
  109. for i in range(0,3):
  110. image[0:, 0:, i] = (layer[0:, 0:, i] * mask_map) + (image[0:, 0:, i] * inverted_map)
  111. self.layer_copy = image.copy()
  112. return image
  113. def get_layer_mask_preview(self, image):
  114. mask = self.mask.get_mask_map()
  115. if(type(mask) == numpy.ndarray):
  116. w, h = image.shape[:2]
  117. # Bits per pixel
  118. bpp = float(str(image.dtype).replace("uint", "").replace("float", ""))
  119. # Pixel value range
  120. np = float(2 ** bpp - 1)
  121. mask = cv2.resize(mask, (h, w), interpolation=cv2.INTER_AREA)
  122. inverted_map = (mask * -1) + 1
  123. image[0:, 0:, 2] = (np * mask) + (image[0:, 0:, 2] * inverted_map)
  124. return image
  125. def show_all(self):
  126. self.tool_box.show_all()
  127. self.tool_stack.show_all()
  128. for tool in self.tools:
  129. tool.widget.show_all()
  130. tool.tool_button.show_all()
  131. def reset_tools(self):
  132. for tool in self.tools:
  133. tool.reset()
  134. def set_size(self, width, height):
  135. self.mask.set_dimentions(width, height)
  136. def set_opacity(self, opacity):
  137. self.opacity = opacity
  138. self.on_tool_change(None, None)
  139. def set_enabled(self, enabled):
  140. self.enabled = enabled
  141. self.on_tool_change(None, None)
  142. def set_blending_mode(self, mode):
  143. self.blend_mode = mode
  144. self.on_tool_change(None, None)