layerfx_stroke.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. # Layer effect > Stroke
  2. from lazpaint import dialog
  3. try:
  4. from tkinter import *
  5. except ImportError:
  6. dialog.show_message("Please install tkinter.")
  7. exit()
  8. from lazpaint import colors, image, layer, filters, tools, selection
  9. if layer.is_empty():
  10. dialog.show_message("Layer is empty")
  11. exit()
  12. ############ image processing
  13. MAX_RADIUS = 100
  14. MAX_OPACITY = 255
  15. source_layer_id = layer.get_id()
  16. source_layer_name = layer.get_name()
  17. chosen_radius = layer.get_registry("stroke-radius")
  18. if chosen_radius == None:
  19. chosen_radius = image.get_registry("stroke-radius")
  20. if chosen_radius == None:
  21. chosen_radius = 10
  22. stroke_layer_id = layer.get_registry("stroke-layer-id")
  23. if image.get_layer_index(stroke_layer_id) == None:
  24. stroke_layer_id = None
  25. if stroke_layer_id is not None:
  26. layer.select_id(stroke_layer_id)
  27. chosen_opacity = layer.get_opacity()
  28. overlay_color = colors.str_to_RGBA(layer.get_registry("overlay-color"))
  29. layer.select_id(source_layer_id)
  30. else:
  31. chosen_opacity = layer.get_opacity()
  32. overlay_color = None
  33. if overlay_color is None:
  34. overlay_color = colors.BLACK
  35. def create_stroke_layer():
  36. global stroke_layer_id
  37. image.do_begin()
  38. if stroke_layer_id != None:
  39. layer.select_id(stroke_layer_id)
  40. stroke_index = image.get_layer_index()
  41. selection.select_all()
  42. selection.delete()
  43. layer.select_id(source_layer_id)
  44. layer.duplicate()
  45. image.move_layer_index(image.get_layer_index(), stroke_index+1)
  46. layer.merge_over()
  47. else:
  48. layer.select_id(source_layer_id)
  49. layer.duplicate()
  50. layer.set_name("Stroke of "+source_layer_name)
  51. layer.set_registry("stroke-source-layer-id", source_layer_id)
  52. stroke_layer_id = layer.get_id()
  53. stroke_index = image.get_layer_index()
  54. image.move_layer_index(stroke_index, stroke_index-1)
  55. image.do_end()
  56. stroke_done = False
  57. opacity_done = False
  58. stroke_initial_color = None
  59. def apply_stroke():
  60. global stroke_done, opacity_done, stroke_initial_color
  61. if opacity_done:
  62. image.undo()
  63. opacity_done = False
  64. if stroke_done:
  65. image.undo()
  66. stroke_done = False
  67. image.do_begin()
  68. if chosen_radius > 0.5:
  69. layer.duplicate()
  70. disk_id = layer.get_id()
  71. filters.blur(name=filters.BLUR_DISK, radius=chosen_radius-0.5)
  72. filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, alpha="min(alpha*"+str(chosen_radius*5)+",(1-alpha)*"+str(chosen_radius*5)+")", gamma_correction=False)
  73. layer.select_id(stroke_layer_id)
  74. else:
  75. disk_id = None
  76. filters.blur(name=filters.BLUR_CORONA, radius=chosen_radius+0.5)
  77. filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, alpha="min(alpha*"+str(chosen_radius)+",(1-alpha)*"+str(chosen_radius)+")", gamma_correction=False)
  78. if disk_id is not None:
  79. layer.select_id(disk_id)
  80. layer.merge_over()
  81. layer.set_registry("overlay-color", overlay_color)
  82. stroke_initial_color = overlay_color
  83. stroke_done = image.do_end()
  84. apply_opacity()
  85. def apply_opacity():
  86. global opacity_done, chosen_opacity, overlay_color
  87. if opacity_done:
  88. image.undo()
  89. opacity_done = False
  90. image.do_begin()
  91. layer.set_opacity(chosen_opacity)
  92. if overlay_color != stroke_initial_color:
  93. filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, gamma_correction=False)
  94. layer.set_registry("overlay-color", overlay_color)
  95. opacity_done = image.do_end()
  96. ######## interface
  97. def button_ok_click():
  98. global source_layer_id, chosen_radius, chosen_offset
  99. layer.select_id(source_layer_id)
  100. layer.set_registry("stroke-radius", chosen_radius)
  101. layer.set_registry("stroke-layer-id", stroke_layer_id)
  102. image.set_registry("stroke-radius", chosen_radius)
  103. image.do_end()
  104. exit()
  105. def button_cancel_click():
  106. if image.do_end():
  107. image.undo()
  108. layer.select_id(source_layer_id)
  109. exit()
  110. scale_radius_update_job = None
  111. def scale_radius_update_do():
  112. global scale_radius_update_job, chosen_radius, scale_radius
  113. new_radius = scale_radius.get()
  114. if new_radius != chosen_radius:
  115. chosen_radius = new_radius
  116. apply_stroke()
  117. scale_radius_update_job = None
  118. def scale_radius_update(event):
  119. global window, scale_radius_update_job
  120. if scale_radius_update_job:
  121. window.after_cancel(scale_radius_update_job)
  122. scale_radius_update_job = window.after(500, scale_radius_update_do)
  123. scale_opacity_update_job = None
  124. def scale_opacity_update_do():
  125. global chosen_opacity
  126. new_opacity = scale_opacity.get()
  127. if new_opacity != chosen_opacity:
  128. chosen_opacity = new_opacity
  129. apply_opacity()
  130. scale_opacity_update_job = None
  131. def scale_opacity_update(event):
  132. global window, scale_opacity_update_job
  133. if scale_opacity_update_job:
  134. window.after_cancel(scale_opacity_update_job)
  135. scale_opacity_update_job = window.after(100, scale_opacity_update_do)
  136. def button_color_click():
  137. global overlay_color, window
  138. new_color = colors.show_dialog(overlay_color)
  139. window.attributes('-topmost', True)
  140. window.attributes('-topmost', False)
  141. if new_color is not None and new_color != overlay_color:
  142. overlay_color = new_color
  143. apply_opacity()
  144. window = Tk()
  145. window.title("Layer stroke")
  146. window.resizable(False, False)
  147. frame = Frame(window)
  148. frame.pack()
  149. label_radius = Label(frame, text="Radius:")
  150. label_radius.grid(column=0, row=0)
  151. scale_radius = Scale(frame, from_=0, to=MAX_RADIUS, orient=HORIZONTAL, command=scale_radius_update)
  152. scale_radius.grid(column=1, row=0, sticky=W+E, padx=10)
  153. scale_radius.set(chosen_radius)
  154. label_opacity = Label(frame, text="Opacity:")
  155. label_opacity.grid(column=0, row=1)
  156. scale_opacity = Scale(frame, from_=0, to=MAX_OPACITY, orient=HORIZONTAL, command=scale_opacity_update)
  157. scale_opacity.grid(column=1, row=1, sticky=W+E, padx=10)
  158. scale_opacity.set(chosen_opacity)
  159. label_color = Label(frame, text="Color:")
  160. label_color.grid(column=0, row=2)
  161. button_color = Button(frame, text="Color...", command=button_color_click)
  162. button_color.grid(column=1, row=2)
  163. frame.columnconfigure(0, pad=20)
  164. frame.columnconfigure(1, minsize=250)
  165. frame.rowconfigure(0, pad=20)
  166. frame.rowconfigure(1, pad=20)
  167. frame.rowconfigure(2, pad=20)
  168. button_ok = Button(window, text="Ok", command=button_ok_click)
  169. button_ok.pack(side=RIGHT, padx=10, pady=10)
  170. button_cancel = Button(window, text="Cancel", command=button_cancel_click)
  171. button_cancel.pack(side=RIGHT, pady=10)
  172. image.do_begin()
  173. selection.deselect()
  174. create_stroke_layer()
  175. apply_stroke()
  176. window.update()
  177. window_width = window.winfo_width()
  178. screen_width = window.winfo_screenwidth()
  179. window.geometry('+%d+0' % (int((screen_width - window_width) / 2)))
  180. window.mainloop()
  181. button_cancel_click()