Giant blob of minor changes
[dotfiles/.git] / .config / GIMP / 2.10 / plug-ins / plugin-heal-selection.py
1 #!/usr/bin/env python
2
3 '''
4 Gimp plugin "Heal selection"
5
6 Copyright 2009 lloyd konneker (bootch at nc.rr.com)
7 Based on smart_remove.scm Copyright 2000 by Paul Harrison.
8
9 Version:
10   1.0 lloyd konneker lkk 9/21/2009 Initial version in python.
11   (See release notes for differences over P. Harrison's prior version in scheme language.)
12
13 License:
14
15   This program is free software; you can redistribute it and/or modify
16   it under the terms of the GNU General Public License as published by
17   the Free Software Foundation; either version 2 of the License, or
18   (at your option) any later version.
19
20   This program is distributed in the hope that it will be useful,
21   but WITHOUT ANY WARRANTY; without even the implied warranty of
22   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23   GNU General Public License for more details.
24
25   The GNU Public License is available at
26   http://www.gnu.org/copyleft/gpl.html
27
28 '''
29
30 from gimpfu import *
31
32 gettext.install("resynthesizer", gimp.locale_directory, unicode=True)
33
34 debug = False
35
36 def heal_selection(timg, tdrawable, samplingRadiusParam=50, directionParam=0, orderParam=0):
37   '''
38   Create stencil selection in a temp image to pass as source (corpus) to plugin resynthesizer,
39   which does the substantive work.
40   '''
41   if pdb.gimp_selection_is_empty(timg):
42     pdb.gimp_message(_("You must first select a region to heal."))
43     return
44   
45   pdb.gimp_image_undo_group_start(timg)
46   
47   targetBounds = tdrawable.mask_bounds
48
49   # In duplicate image, create the sample (corpus).
50   # (I tried to use a temporary layer but found it easier to use duplicate image.)
51   tempImage = pdb.gimp_image_duplicate(timg)
52   if not tempImage:
53       raise RuntimeError, "Failed duplicate image"
54   
55   # !!! The drawable can be a mask (grayscale channel), don't restrict to layer.
56   work_drawable = pdb.gimp_image_get_active_drawable(tempImage)
57   if not work_drawable:
58       raise RuntimeError, "Failed get active drawable"
59       
60   '''
61   grow and punch hole, making a frisket iow stencil iow donut
62   
63   '''
64   orgSelection = pdb.gimp_selection_save(tempImage) # save for later use
65   pdb.gimp_selection_grow(tempImage, samplingRadiusParam)
66   # ??? returns None , docs say it returns SUCCESS
67   
68   # !!! Note that if selection is a bordering ring already, growing expanded it inwards.
69   # Which is what we want, to make a corpus inwards.
70   
71   grownSelection = pdb.gimp_selection_save(tempImage)
72   
73   # Cut hole where the original selection was, so we don't sample from it.
74   # !!! Note that gimp enums/constants are not prefixed with GIMP_
75   pdb.gimp_image_select_item(tempImage, CHANNEL_OP_SUBTRACT, orgSelection)
76   
77   '''
78   Selection (to be the corpus) is donut or frisket around the original target T
79     xxx
80     xTx
81     xxx
82   '''
83   
84   # crop the temp image to size of selection to save memory and for directional healing!!
85   frisketBounds = grownSelection.mask_bounds
86   frisketLowerLeftX = frisketBounds[0]
87   frisketLowerLeftY = frisketBounds[1]
88   frisketUpperRightX = frisketBounds[2]
89   frisketUpperRightY = frisketBounds[3]
90   targetLowerLeftX = targetBounds[0]
91   targetLowerLeftY = targetBounds[1]
92   targetUpperRightX = targetBounds[2]
93   targetUpperRightY = targetBounds[3]
94   
95   frisketWidth = frisketUpperRightX - frisketLowerLeftX
96   frisketHeight = frisketUpperRightY - frisketLowerLeftY
97   
98   # User's choice of direction affects the corpus shape, and is also passed to resynthesizer plugin
99   if directionParam == 0: # all around
100       # Crop to the entire frisket
101       newWidth, newHeight, newLLX, newLLY = ( frisketWidth, frisketHeight, 
102         frisketLowerLeftX, frisketLowerLeftY )
103   elif directionParam == 1: # sides
104       # Crop to target height and frisket width:  XTX
105       newWidth, newHeight, newLLX, newLLY =  ( frisketWidth, targetUpperRightY-targetLowerLeftY, 
106         frisketLowerLeftX, targetLowerLeftY )
107   elif directionParam == 2: # above and below
108       # X Crop to target width and frisket height
109       # T
110       # X
111       newWidth, newHeight, newLLX, newLLY = ( targetUpperRightX-targetLowerLeftX, frisketHeight, 
112         targetLowerLeftX, frisketLowerLeftY )
113   # Restrict crop to image size (condition of gimp_image_crop) eg when off edge of image
114   newWidth = min(pdb.gimp_image_width(tempImage) - newLLX, newWidth)
115   newHeight = min(pdb.gimp_image_height(tempImage) - newLLY, newHeight)
116   pdb.gimp_image_crop(tempImage, newWidth, newHeight, newLLX, newLLY)
117   
118   # Encode two script params into one resynthesizer param.
119   # use border 1 means fill target in random order
120   # use border 0 is for texture mapping operations, not used by this script
121   if not orderParam :
122       useBorder = 1   # User wants NO order, ie random filling
123   elif orderParam == 1 :  # Inward to corpus.  2,3,4
124       useBorder = directionParam+2   # !!! Offset by 2 to get past the original two boolean values
125   else:
126       # Outward from image center.  
127       # 5+0=5 outward concentric
128       # 5+1=6 outward from sides
129       # 5+2=7 outward above and below
130       useBorder = directionParam+5
131       
132   # Note that the old resynthesizer required an inverted selection !!
133   
134   if debug:
135     try:
136       gimp.Display(tempImage) 
137       gimp.displays_flush()
138     except RuntimeError:  # thrown if non-interactive
139       pass
140     from time import sleep
141     sleep(2)
142   
143   # Not necessary to restore image to initial condition of selection, activity,
144   # the original image should not have been changed,
145   # and the resynthesizer should only heal, not change selection.
146
147   # Note that the API hasn't changed but use_border param now has more values.
148   pdb.plug_in_resynthesizer(timg, tdrawable, 0,0, useBorder, work_drawable.ID, -1, -1, 0.0, 0.117, 16, 500)
149   
150   # Clean up (comment out to debug)
151   gimp.delete(tempImage)
152   pdb.gimp_image_undo_group_end(timg)
153
154
155 register(
156   "python_fu_heal_selection",
157   N_("Heal the selection from surroundings as if using the heal tool."),
158   "Requires separate resynthesizer plugin.",
159   "Lloyd Konneker",
160   "2009 Lloyd Konneker",  # Copyright 
161   "2009",
162   N_("_Heal selection..."),
163   "RGB*, GRAY*",
164   [
165     (PF_IMAGE, "image",       "Input image", None),
166     (PF_DRAWABLE, "drawable", "Input drawable", None),
167     (PF_INT, "samplingRadiusParam", _("Context sampling width (pixels):"), 50),
168     (PF_OPTION,"directionParam",   _("Sample from:"),0,[_("All around"),_("Sides"),_("Above and below")]),
169     (PF_OPTION, "orderParam",   _("Filling order:"), 0, [_("Random"),
170       _("Inwards towards center"), _("Outwards from center") ])
171   ],
172   [],
173   heal_selection,
174   menu="<Image>/Filters/Enhance",
175   domain=("resynthesizer", gimp.locale_directory)
176   )
177
178 main()