This file is indexed.

/usr/lib/gimp/2.0/plug-ins/plugin-heal-selection.py is in gimp-plugin-registry 7.20140602ubuntu3.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env python

'''
Gimp plugin "Heal selection"

Copyright 2009 lloyd konneker (bootch at nc.rr.com)
Based on smart_remove.scm Copyright 2000 by Paul Harrison.

Version:
  1.0 lloyd konneker lkk 9/21/2009 Initial version in python.
  (See release notes for differences over P. Harrison's prior version in scheme language.)

License:

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  The GNU Public License is available at
  http://www.gnu.org/copyleft/gpl.html

'''

from gimpfu import *

gettext.install("resynthesizer", gimp.locale_directory, unicode=True)

debug = False

def heal_selection(timg, tdrawable, samplingRadiusParam=50, directionParam=0, orderParam=0):
  '''
  Create stencil selection in a temp image to pass as source (corpus) to plugin resynthesizer,
  which does the substantive work.
  '''
  if pdb.gimp_selection_is_empty(timg):
    pdb.gimp_message(_("You must first select a region to heal."))
    return
  
  pdb.gimp_image_undo_group_start(timg)
  
  targetBounds = tdrawable.mask_bounds

  # In duplicate image, create the sample (corpus).
  # (I tried to use a temporary layer but found it easier to use duplicate image.)
  tempImage = pdb.gimp_image_duplicate(timg)
  if not tempImage:
      raise RuntimeError, "Failed duplicate image"
  
  # !!! The drawable can be a mask (grayscale channel), don't restrict to layer.
  work_drawable = pdb.gimp_image_get_active_drawable(tempImage)
  if not work_drawable:
      raise RuntimeError, "Failed get active drawable"
      
  '''
  grow and punch hole, making a frisket iow stencil iow donut
  
  '''
  orgSelection = pdb.gimp_selection_save(tempImage) # save for later use
  pdb.gimp_selection_grow(tempImage, samplingRadiusParam)
  # ??? returns None , docs say it returns SUCCESS
  
  # !!! Note that if selection is a bordering ring already, growing expanded it inwards.
  # Which is what we want, to make a corpus inwards.
  
  grownSelection = pdb.gimp_selection_save(tempImage)
  
  # Cut hole where the original selection was, so we don't sample from it.
  # !!! Note that gimp enums/constants are not prefixed with GIMP_
  pdb.gimp_selection_combine(orgSelection, CHANNEL_OP_SUBTRACT)
  
  '''
  Selection (to be the corpus) is donut or frisket around the original target T
    xxx
    xTx
    xxx
  '''
  
  # crop the temp image to size of selection to save memory and for directional healing!!
  frisketBounds = grownSelection.mask_bounds
  frisketLowerLeftX = frisketBounds[0]
  frisketLowerLeftY = frisketBounds[1]
  frisketUpperRightX = frisketBounds[2]
  frisketUpperRightY = frisketBounds[3]
  targetLowerLeftX = targetBounds[0]
  targetLowerLeftY = targetBounds[1]
  targetUpperRightX = targetBounds[2]
  targetUpperRightY = targetBounds[3]
  
  frisketWidth = frisketUpperRightX - frisketLowerLeftX
  frisketHeight = frisketUpperRightY - frisketLowerLeftY
  
  # User's choice of direction affects the corpus shape, and is also passed to resynthesizer plugin
  if directionParam == 0: # all around
      # Crop to the entire frisket
      newWidth, newHeight, newLLX, newLLY = ( frisketWidth, frisketHeight, 
        frisketLowerLeftX, frisketLowerLeftY )
  elif directionParam == 1: # sides
      # Crop to target height and frisket width:  XTX
      newWidth, newHeight, newLLX, newLLY =  ( frisketWidth, targetUpperRightY-targetLowerLeftY, 
        frisketLowerLeftX, targetLowerLeftY )
  elif directionParam == 2: # above and below
      # X Crop to target width and frisket height
      # T
      # X
      newWidth, newHeight, newLLX, newLLY = ( targetUpperRightX-targetLowerLeftX, frisketHeight, 
        targetLowerLeftX, frisketLowerLeftY )
  # Restrict crop to image size (condition of gimp_image_crop) eg when off edge of image
  newWidth = min(pdb.gimp_image_width(tempImage) - newLLX, newWidth)
  newHeight = min(pdb.gimp_image_height(tempImage) - newLLY, newHeight)
  pdb.gimp_image_crop(tempImage, newWidth, newHeight, newLLX, newLLY)
  
  # Encode two script params into one resynthesizer param.
  # use border 1 means fill target in random order
  # use border 0 is for texture mapping operations, not used by this script
  if not orderParam :
      useBorder = 1   # User wants NO order, ie random filling
  elif orderParam == 1 :  # Inward to corpus.  2,3,4
      useBorder = directionParam+2   # !!! Offset by 2 to get past the original two boolean values
  else:
      # Outward from image center.  
      # 5+0=5 outward concentric
      # 5+1=6 outward from sides
      # 5+2=7 outward above and below
      useBorder = directionParam+5
      
  # Note that the old resynthesizer required an inverted selection !!
  
  if debug:
    try:
      gimp.Display(tempImage) 
      gimp.displays_flush()
    except RuntimeError:  # thrown if non-interactive
      pass
    from time import sleep
    sleep(2)
  
  # Not necessary to restore image to initial condition of selection, activity,
  # the original image should not have been changed,
  # and the resynthesizer should only heal, not change selection.

  # Note that the API hasn't changed but use_border param now has more values.
  pdb.plug_in_resynthesizer(timg, tdrawable, 0,0, useBorder, work_drawable.ID, -1, -1, 0.0, 0.117, 16, 500)
  
  # Clean up (comment out to debug)
  gimp.delete(tempImage)
  pdb.gimp_image_undo_group_end(timg)


register(
  "python_fu_heal_selection",
  N_("Heal the selection from surroundings as if using the heal tool."),
  "Requires separate resynthesizer plugin.",
  "Lloyd Konneker",
  "2009 Lloyd Konneker",  # Copyright 
  "2009",
  N_("_Heal selection..."),
  "RGB*, GRAY*",
  [
    (PF_IMAGE, "image",       "Input image", None),
    (PF_DRAWABLE, "drawable", "Input drawable", None),
    (PF_INT, "samplingRadiusParam", _("Context sampling width (pixels):"), 50),
    (PF_OPTION,"directionParam",   _("Sample from:"),0,[_("All around"),_("Sides"),_("Above and below")]),
    (PF_OPTION, "orderParam",   _("Filling order:"), 0, [_("Random"),
      _("Inwards towards center"), _("Outwards from center") ])
  ],
  [],
  heal_selection,
  menu="<Image>/Filters/Enhance",
  domain=("resynthesizer", gimp.locale_directory)
  )

main()