layerfx_shadow.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. # Layer effect > Drop shadow
  2. # (fr) Effet de calque > Ombre portée
  3. # (es) Efectos de capa > Sombra paralela
  4. # (de) Ebeneneffekte > Schlagschatten
  5. from lazpaint import dialog
  6. try:
  7. from tkinter import Tk, Frame, Label, Scale, HORIZONTAL, Button, RIGHT, W, E
  8. except ImportError:
  9. dialog.show_message("Please install tkinter. On Debian distributions, use the command: apt install python3-tk")
  10. exit()
  11. from lazpaint import colors, image, layer, filters, tools, selection
  12. translation = dialog.translate_dict(["Layer is empty", "Shadow of ",
  13. "Radius", "Offset", "Opacity", "Ok", "Cancel", "Run this script again to update effect"])
  14. if layer.is_empty():
  15. dialog.show_message(translation["Layer is empty"])
  16. exit()
  17. ############ image processing
  18. FRIENDLY_NAME = dialog.get_script_name()
  19. MAX_RADIUS = 100
  20. MAX_OFFSET = 100
  21. MAX_OPACITY = 255
  22. source_layer_id = layer.get_registry("shadow-source-layer-id")
  23. if source_layer_id is not None:
  24. layer.select_id(source_layer_id)
  25. else:
  26. source_layer_id = layer.get_id()
  27. source_layer_name = layer.get_name()
  28. chosen_radius = layer.get_registry("shadow-radius")
  29. if chosen_radius == None:
  30. chosen_radius = image.get_registry("shadow-radius")
  31. if chosen_radius == None:
  32. chosen_radius = 10
  33. chosen_offset = layer.get_registry("shadow-offset")
  34. if chosen_offset == None or len(chosen_offset) != 2:
  35. chosen_offset = image.get_registry("shadow-offset")
  36. if chosen_offset == None or len(chosen_offset) != 2:
  37. chosen_offset = (10, 10)
  38. shadow_layer_id = layer.get_registry("shadow-layer-id")
  39. if image.get_layer_index(shadow_layer_id) == None:
  40. shadow_layer_id = None
  41. if shadow_layer_id is not None:
  42. layer.select_id(shadow_layer_id)
  43. chosen_opacity = layer.get_opacity()
  44. overlay_color = colors.str_to_RGBA(layer.get_registry("overlay-color"))
  45. layer.select_id(source_layer_id)
  46. else:
  47. chosen_opacity = layer.get_opacity()*2/3
  48. overlay_color = None
  49. if overlay_color is None:
  50. overlay_color = colors.BLACK
  51. def create_shadow_layer():
  52. global shadow_layer_id
  53. image.do_begin()
  54. if shadow_layer_id != None:
  55. layer.select_id(shadow_layer_id)
  56. layer.remove()
  57. layer.select_id(source_layer_id)
  58. layer.duplicate()
  59. layer.set_name(FRIENDLY_NAME + " - " + source_layer_name)
  60. layer.set_registry("shadow-source-layer-id", source_layer_id)
  61. shadow_layer_id = layer.get_id()
  62. shadow_index = image.get_layer_index()
  63. image.move_layer_index(shadow_index, shadow_index-1)
  64. filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, gamma_correction=False)
  65. layer.set_registry("overlay-color", overlay_color)
  66. layer.set_opacity(chosen_opacity)
  67. image.do_end()
  68. blur_done = False
  69. offset_done = False
  70. def apply_blur():
  71. global blur_done, offset_done
  72. if offset_done:
  73. image.undo()
  74. offset_done = False
  75. if blur_done:
  76. image.undo()
  77. blur_done = False
  78. image.do_begin()
  79. filters.blur(radius=chosen_radius)
  80. blur_done = image.do_end()
  81. apply_offset()
  82. def apply_offset():
  83. global offset_done
  84. if offset_done:
  85. image.undo()
  86. offset_done = False
  87. image.do_begin()
  88. tools.choose(tools.MOVE_LAYER)
  89. tools.mouse([(0,0), chosen_offset])
  90. layer.set_opacity(chosen_opacity)
  91. offset_done = image.do_end()
  92. ######## interface
  93. def button_ok_click():
  94. global source_layer_id, chosen_radius, chosen_offset
  95. layer.select_id(source_layer_id)
  96. layer.set_registry("shadow-radius", chosen_radius)
  97. layer.set_registry("shadow-offset", chosen_offset)
  98. layer.set_registry("shadow-layer-id", shadow_layer_id)
  99. image.set_registry("shadow-radius", chosen_radius)
  100. image.set_registry("shadow-offset", chosen_offset)
  101. image.do_end()
  102. exit()
  103. def button_cancel_click():
  104. if image.do_end():
  105. image.undo()
  106. layer.select_id(source_layer_id)
  107. exit()
  108. scale_radius_update_job = None
  109. def scale_radius_update_do():
  110. global scale_radius_update_job, chosen_radius, scale_radius
  111. new_radius = scale_radius.get()
  112. if new_radius != chosen_radius:
  113. chosen_radius = new_radius
  114. apply_blur()
  115. scale_radius_update_job = None
  116. def scale_radius_update(event):
  117. global window, scale_radius_update_job
  118. if scale_radius_update_job:
  119. window.after_cancel(scale_radius_update_job)
  120. scale_radius_update_job = window.after(500, scale_radius_update_do)
  121. scale_offset_update_job = None
  122. def scale_offset_update_do():
  123. global chosen_offset
  124. new_offset = (scale_offset_x.get(), scale_offset_y.get())
  125. if new_offset != chosen_offset:
  126. chosen_offset = new_offset
  127. apply_offset()
  128. scale_offset_update_job = None
  129. def scale_offset_update(event):
  130. global window, scale_offset_update_job
  131. if scale_offset_update_job:
  132. window.after_cancel(scale_offset_update_job)
  133. scale_offset_update_job = window.after(100, scale_offset_update_do)
  134. scale_opacity_update_job = None
  135. def scale_opacity_update_do():
  136. global chosen_opacity
  137. new_opacity = scale_opacity.get()
  138. if new_opacity != chosen_opacity:
  139. chosen_opacity = new_opacity
  140. apply_offset()
  141. scale_opacity_update_job = None
  142. def scale_opacity_update(event):
  143. global window, scale_opacity_update_job
  144. if scale_opacity_update_job:
  145. window.after_cancel(scale_opacity_update_job)
  146. scale_opacity_update_job = window.after(100, scale_opacity_update_do)
  147. window = Tk()
  148. window.title(FRIENDLY_NAME)
  149. window.resizable(False, False)
  150. frame = Frame(window)
  151. frame.pack()
  152. label_radius = Label(frame, text=translation["Radius"])
  153. label_radius.grid(column=0, row=0)
  154. scale_radius = Scale(frame, from_=0, to=MAX_RADIUS, orient=HORIZONTAL, command=scale_radius_update)
  155. scale_radius.grid(column=1, row=0, sticky=W+E, padx=10)
  156. scale_radius.set(chosen_radius)
  157. label_offset = Label(frame, text=translation["Offset"])
  158. label_offset.grid(column=0, row=1)
  159. scale_offset_x = Scale(frame, from_=-MAX_OFFSET, to=MAX_OFFSET, orient=HORIZONTAL, command=scale_offset_update)
  160. scale_offset_x.grid(column=1, row=1, sticky=W+E, padx=10)
  161. scale_offset_x.set(chosen_offset[0])
  162. scale_offset_y = Scale(frame, from_=-MAX_OFFSET, to=MAX_OFFSET, orient=HORIZONTAL, command=scale_offset_update)
  163. scale_offset_y.grid(column=1, row=2, sticky=W+E, padx=10)
  164. scale_offset_y.set(chosen_offset[1])
  165. label_opacity = Label(frame, text=translation["Opacity"])
  166. label_opacity.grid(column=0, row=3)
  167. scale_opacity = Scale(frame, from_=0, to=MAX_OPACITY, orient=HORIZONTAL, command=scale_opacity_update)
  168. scale_opacity.grid(column=1, row=3, sticky=W+E, padx=10)
  169. scale_opacity.set(chosen_opacity)
  170. label_info = Label(frame, text=translation["Run this script again to update effect"],
  171. background="lemon chiffon", foreground="black", borderwidth=1, relief="solid")
  172. label_info.grid(column=0, rows=4, columnspan=2)
  173. frame.columnconfigure(0, pad=20)
  174. frame.columnconfigure(1, minsize=250)
  175. frame.rowconfigure(0, pad=20)
  176. frame.rowconfigure(1, pad=20)
  177. frame.rowconfigure(2, pad=20)
  178. frame.rowconfigure(3, pad=20)
  179. button_ok = Button(window, text=translation["Ok"], command=button_ok_click)
  180. button_ok.pack(side=RIGHT, padx=10, pady=10)
  181. button_cancel = Button(window, text=translation["Cancel"], command=button_cancel_click)
  182. button_cancel.pack(side=RIGHT, pady=10)
  183. image.do_begin()
  184. selection.deselect()
  185. create_shadow_layer()
  186. apply_blur()
  187. window.update()
  188. window_width = window.winfo_width()
  189. screen_width = window.winfo_screenwidth()
  190. window.geometry('+%d+0' % (int((screen_width - window_width) / 2)))
  191. window.lift()
  192. window.attributes('-topmost',True)
  193. window.after_idle(window.attributes,'-topmost',False)
  194. window.mainloop()
  195. button_cancel_click()