layerfx_innerlight.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. # Layer effect > Inner light
  2. # (fr) Effet de calque > Lumière intérieure
  3. from lazpaint import dialog
  4. try:
  5. from tkinter import Tk, Frame, Label, Scale, HORIZONTAL, Button, RIGHT, W, E
  6. except ImportError:
  7. dialog.show_message("Please install tkinter. On Debian distributions, use the command: apt install python3-tk")
  8. exit()
  9. from lazpaint import colors, image, layer, filters, tools, selection
  10. import math
  11. translation = dialog.translate_dict(["Layer is empty", "Radius", "Angle",
  12. "Opacity", "Ok", "Cancel", "Run this script again to update effect"])
  13. if layer.is_empty():
  14. dialog.show_message(translation["Layer is empty"])
  15. exit()
  16. ############ image processing
  17. FRIENDLY_NAME = dialog.get_script_name()
  18. REGISTRY_NAME = "innerlight"
  19. OPPOSITE_REGISTRY_NAME = "innershadow"
  20. DEFAULT_ANGLE = 315
  21. DEFAULT_COLOR = colors.WHITE
  22. MAX_RADIUS = 200
  23. MAX_OPACITY = 255
  24. MAX_ANGLE = 360
  25. source_layer_id = layer.get_registry(REGISTRY_NAME+"-source-layer-id")
  26. if source_layer_id is not None:
  27. layer.select_id(source_layer_id)
  28. else:
  29. source_layer_id = layer.get_id()
  30. source_layer_name = layer.get_name()
  31. chosen_radius = layer.get_registry(REGISTRY_NAME+"-radius")
  32. if chosen_radius == None:
  33. chosen_radius = layer.get_registry(OPPOSITE_REGISTRY_NAME+"-radius")
  34. if chosen_radius == None:
  35. chosen_radius = image.get_registry(REGISTRY_NAME+"-radius")
  36. if chosen_radius == None:
  37. chosen_radius = image.get_registry(OPPOSITE_REGISTRY_NAME+"-radius")
  38. if chosen_radius == None:
  39. chosen_radius = 20
  40. chosen_angle = layer.get_registry(REGISTRY_NAME+"-angle")
  41. if chosen_angle == None:
  42. chosen_angle = layer.get_registry(OPPOSITE_REGISTRY_NAME+"-angle")
  43. if chosen_angle is not None:
  44. chosen_angle = (chosen_angle+180) % 360
  45. if chosen_angle == None:
  46. chosen_angle = image.get_registry(REGISTRY_NAME+"-angle")
  47. if chosen_angle == None:
  48. chosen_angle = image.get_registry(OPPOSITE_REGISTRY_NAME+"-angle")
  49. if chosen_angle is not None:
  50. chosen_angle = (chosen_angle+180) % 360
  51. if chosen_angle == None:
  52. chosen_angle = DEFAULT_ANGLE
  53. shadow_layer_id = layer.get_registry(REGISTRY_NAME+"-layer-id")
  54. if image.get_layer_index(shadow_layer_id) == None:
  55. shadow_layer_id = None
  56. if shadow_layer_id is not None:
  57. layer.select_id(shadow_layer_id)
  58. chosen_opacity = layer.get_opacity()
  59. overlay_color = colors.str_to_RGBA(layer.get_registry("overlay-color"))
  60. layer.select_id(source_layer_id)
  61. else:
  62. chosen_opacity = layer.get_opacity()*2/3
  63. overlay_color = None
  64. if overlay_color is None:
  65. overlay_color = DEFAULT_COLOR
  66. def create_shadow_layer():
  67. global shadow_layer_id
  68. image.do_begin()
  69. if shadow_layer_id != None:
  70. layer.select_id(shadow_layer_id)
  71. layer.remove()
  72. layer.select_id(source_layer_id)
  73. layer.duplicate()
  74. layer.rasterize()
  75. layer.set_name(FRIENDLY_NAME+" - "+source_layer_name)
  76. layer.set_registry(REGISTRY_NAME+"-source-layer-id", source_layer_id)
  77. shadow_layer_id = layer.get_id()
  78. layer.set_registry("overlay-color", overlay_color)
  79. layer.set_opacity(chosen_opacity)
  80. image.do_end()
  81. blur_done = False
  82. opacity_done = False
  83. def apply_blur():
  84. global blur_done, opacity_done
  85. if opacity_done:
  86. image.undo()
  87. opacity_done = False
  88. if blur_done:
  89. image.undo()
  90. blur_done = False
  91. image.do_begin()
  92. if chosen_radius == 0:
  93. filters.filter_function(alpha=0, gamma_correction=False)
  94. else:
  95. layer.duplicate()
  96. layer.set_opacity(255)
  97. filters.filter_function(red="alpha", green="alpha", blue="alpha", alpha=1, gamma_correction=False)
  98. mask_layer_id = layer.get_id()
  99. layer.select_id(shadow_layer_id)
  100. filters.filter_function(red=overlay_color.red/255, green=overlay_color.green/255, blue=overlay_color.blue/255, gamma_correction=False)
  101. tools.choose(tools.MOVE_LAYER)
  102. offset = (math.sin(chosen_angle*math.pi/180)*chosen_radius, -math.cos(chosen_angle*math.pi/180)*chosen_radius)
  103. tools.mouse([(0,0), (offset[0],offset[1])])
  104. layer.select_id(mask_layer_id)
  105. layer.duplicate()
  106. mask_layer_id2 = layer.get_id()
  107. layer.select_id(mask_layer_id)
  108. tools.choose(tools.MOVE_LAYER)
  109. tools.mouse([(offset[0]/2,offset[1]/2), (0,0)])
  110. colors.linear_negative()
  111. layer.set_blend_op(layer.BLEND_MASK)
  112. layer.merge_over()
  113. mask_layer_id = mask_layer_id2
  114. filters.blur(radius=chosen_radius)
  115. layer.select_id(mask_layer_id)
  116. layer.set_blend_op(layer.BLEND_MASK)
  117. layer.merge_over()
  118. blur_done = image.do_end()
  119. apply_opacity()
  120. def apply_opacity():
  121. global opacity_done
  122. if opacity_done:
  123. image.undo()
  124. opacity_done = False
  125. image.do_begin()
  126. layer.set_opacity(chosen_opacity)
  127. opacity_done = image.do_end()
  128. ######## interface
  129. def button_ok_click():
  130. global source_layer_id, chosen_radius, chosen_angle
  131. layer.select_id(source_layer_id)
  132. layer.set_registry(REGISTRY_NAME+"-radius", chosen_radius)
  133. layer.set_registry(REGISTRY_NAME+"-angle", chosen_angle)
  134. layer.set_registry(REGISTRY_NAME+"-layer-id", shadow_layer_id)
  135. image.set_registry(REGISTRY_NAME+"-radius", chosen_radius)
  136. image.set_registry(REGISTRY_NAME+"-angle", chosen_angle)
  137. image.do_end()
  138. exit()
  139. def button_cancel_click():
  140. if image.do_end():
  141. image.undo()
  142. layer.select_id(source_layer_id)
  143. exit()
  144. scale_radius_update_job = None
  145. def scale_radius_update_do():
  146. global scale_radius_update_job, chosen_radius, scale_radius
  147. new_radius = scale_radius.get()
  148. if new_radius != chosen_radius:
  149. chosen_radius = new_radius
  150. apply_blur()
  151. scale_radius_update_job = None
  152. def scale_radius_update(event):
  153. global window, scale_radius_update_job
  154. if scale_radius_update_job:
  155. window.after_cancel(scale_radius_update_job)
  156. scale_radius_update_job = window.after(800, scale_radius_update_do)
  157. scale_angle_update_job = None
  158. def scale_angle_update_do():
  159. global scale_angle_update_job, chosen_angle, scale_angle
  160. new_angle = scale_angle.get()
  161. if new_angle != chosen_angle:
  162. chosen_angle = new_angle
  163. apply_blur()
  164. scale_angle_update_job = None
  165. def scale_angle_update(event):
  166. global window, scale_angle_update_job
  167. if scale_angle_update_job:
  168. window.after_cancel(scale_angle_update_job)
  169. scale_angle_update_job = window.after(800, scale_angle_update_do)
  170. scale_opacity_update_job = None
  171. def scale_opacity_update_do():
  172. global chosen_opacity
  173. new_opacity = scale_opacity.get()
  174. if new_opacity != chosen_opacity:
  175. chosen_opacity = new_opacity
  176. apply_opacity()
  177. scale_opacity_update_job = None
  178. def scale_opacity_update(event):
  179. global window, scale_opacity_update_job
  180. if scale_opacity_update_job:
  181. window.after_cancel(scale_opacity_update_job)
  182. scale_opacity_update_job = window.after(100, scale_opacity_update_do)
  183. window = Tk()
  184. window.title(FRIENDLY_NAME)
  185. window.resizable(False, False)
  186. frame = Frame(window)
  187. frame.pack()
  188. label_radius = Label(frame, text=translation["Radius"])
  189. label_radius.grid(column=0, row=0)
  190. scale_radius = Scale(frame, from_=0, to=MAX_RADIUS, orient=HORIZONTAL, command=scale_radius_update)
  191. scale_radius.grid(column=1, row=0, sticky=W+E, padx=10)
  192. scale_radius.set(chosen_radius)
  193. label_angle = Label(frame, text=translation["Angle"])
  194. label_angle.grid(column=0, row=1)
  195. scale_angle = Scale(frame, from_=0, to=MAX_ANGLE, orient=HORIZONTAL, command=scale_angle_update)
  196. scale_angle.grid(column=1, row=1, sticky=W+E, padx=10)
  197. scale_angle.set(chosen_angle)
  198. label_opacity = Label(frame, text=translation["Opacity"])
  199. label_opacity.grid(column=0, row=2)
  200. scale_opacity = Scale(frame, from_=0, to=MAX_OPACITY, orient=HORIZONTAL, command=scale_opacity_update)
  201. scale_opacity.grid(column=1, row=2, sticky=W+E, padx=10)
  202. scale_opacity.set(chosen_opacity)
  203. label_info = Label(frame, text=translation["Run this script again to update effect"],
  204. background="lemon chiffon", foreground="black", borderwidth=1, relief="solid")
  205. label_info.grid(column=0, rows=3, columnspan=2)
  206. frame.columnconfigure(0, pad=20)
  207. frame.columnconfigure(1, minsize=250)
  208. frame.rowconfigure(0, pad=20)
  209. frame.rowconfigure(1, pad=20)
  210. frame.rowconfigure(2, pad=20)
  211. button_ok = Button(window, text=translation["Ok"], command=button_ok_click)
  212. button_ok.pack(side=RIGHT, padx=10, pady=10)
  213. button_cancel = Button(window, text=translation["Cancel"], command=button_cancel_click)
  214. button_cancel.pack(side=RIGHT, pady=10)
  215. image.do_begin()
  216. selection.deselect()
  217. create_shadow_layer()
  218. apply_blur()
  219. window.update()
  220. window_width = window.winfo_width()
  221. screen_width = window.winfo_screenwidth()
  222. window.geometry('+%d+0' % (int((screen_width - window_width) / 2)))
  223. window.lift()
  224. window.attributes('-topmost',True)
  225. window.after_idle(window.attributes,'-topmost',False)
  226. window.mainloop()
  227. button_cancel_click()