Skip to content

Design

DESIGN HELPERs

NO_DIALOG = DialogModes.DisplayNoDialogs module-attribute

FILLING SPACE

content_aware_fill()

Fills the current selection using content aware fill.

Source code in src/helpers/design.py
114
115
116
117
118
119
120
def content_aware_fill() -> None:
    """Fills the current selection using content aware fill."""
    desc = ActionDescriptor()
    desc.putEnumerated(sID("using"), sID("fillContents"), sID("contentAware"))
    desc.putUnitDouble(sID("opacity"), sID("percentUnit"), 100)
    desc.putEnumerated(sID("mode"), sID("blendMode"), sID("normal"))
    app.executeAction(sID("fill"), desc, NO_DIALOG)

content_aware_fill_edges(layer=None)

Rasterizes a given layer (or active layer) and fills remaining pixels using content-aware fill. @param layer: Layer to use for the content aware fill. Uses active if not provided.

Source code in src/helpers/design.py
 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
def content_aware_fill_edges(layer: Optional[ArtLayer] = None) -> None:
    """
    Rasterizes a given layer (or active layer) and fills remaining pixels using content-aware fill.
    @param layer: Layer to use for the content aware fill. Uses active if not provided.
    """
    # Set active layer if needed, then rasterize
    docref = app.activeDocument
    if layer:
        docref.activeLayer = layer
    docref.activeLayer.rasterize(RasterizeType.EntireLayer)

    # Select pixels of active layer and invert
    select_layer_pixels(docref.activeLayer)
    selection = docref.selection
    selection.invert()

    # Guard against no selection made
    try:
        _ = selection.bounds
    except PS_EXCEPTIONS:
        return

    # Expand and smooth selection
    selection.expand(8)
    selection.smooth(4)

    # Content aware fill
    content_aware_fill()
    selection.deselect()

fill_empty_area(reference, color=None)

Fills empty gaps on an art layer, such as an expansion symbol, with a solid color. @param reference: Reference layer to put the new fill layer underneath @param color: Color of the background fill

Source code in src/helpers/design.py
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
def fill_empty_area(reference: ArtLayer, color: Optional[SolidColor] = None) -> ArtLayer:
    """
    Fills empty gaps on an art layer, such as an expansion symbol, with a solid color.
    @param reference: Reference layer to put the new fill layer underneath
    @param color: Color of the background fill
    """
    # Magic Wand contiguous outside symbol
    coords = ActionDescriptor()
    coords.putUnitDouble(cID("Hrzn"), cID("#Pxl"), 5)
    coords.putUnitDouble(cID("Vrtc"), cID("#Pxl"), 5)
    click1 = ActionDescriptor()
    ref1 = ActionReference()
    ref1.putProperty(cID("Chnl"), cID("fsel"))
    click1.putReference(sID("target"), ref1)
    click1.putObject(cID("T   "), cID("Pnt "), coords)
    click1.putInteger(cID("Tlrn"), 12)
    click1.putBoolean(cID("AntA"), True)
    app.executeAction(cID("setd"), click1)

    # Invert selection
    app.activeDocument.selection.invert()
    app.activeDocument.selection.contract(1)

    # Make a new layer
    layer = app.activeDocument.artLayers.add()
    layer.name = "Expansion Mask"
    layer.blendMode = BlendMode.NormalBlend
    layer.moveAfter(reference)

    # Fill selection with stroke color
    app.foregroundColor = color or rgb_black()
    click3 = ActionDescriptor()
    click3.putObject(cID("From"), cID("Pnt "), coords)
    click3.putInteger(cID("Tlrn"), 0)
    click3.putEnumerated(cID("Usng"), cID("FlCn"), cID("FrgC"))
    click3.putBoolean(cID("Cntg"), False)
    app.executeAction(cID("Fl  "), click3)

    # Clear Selection
    app.activeDocument.selection.deselect()
    return layer

generative_fill()

Call Photoshop's AI powered "Generative Fill" on the current selection.

Source code in src/helpers/design.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
def generative_fill() -> None:
    """Call Photoshop's AI powered "Generative Fill" on the current selection."""
    desc1 = ActionDescriptor()
    ref1 = ActionReference()
    desc2 = ActionDescriptor()
    desc3 = ActionDescriptor()
    ref1.putEnumerated(sID("document"), sID("ordinal"), sID("targetEnum"))
    desc1.putReference(sID("target"), ref1)
    desc1.putString(sID("prompt"), """""")
    desc1.putString(sID("serviceID"), """clio""")
    desc1.putEnumerated(sID("mode"), sID("syntheticFillMode"), sID("inpaint"))
    desc3.putString(sID("gi_PROMPT"), """""")
    desc3.putString(sID("gi_MODE"), """ginp""")
    desc3.putInteger(sID("gi_SEED"), -1)
    desc3.putInteger(sID("gi_NUM_STEPS"), -1)
    desc3.putInteger(sID("gi_GUIDANCE"), 6)
    desc3.putInteger(sID("gi_SIMILARITY"), 0)
    desc3.putBoolean(sID("gi_CROP"), False)
    desc3.putBoolean(sID("gi_DILATE"), False)
    desc3.putInteger(sID("gi_CONTENT_PRESERVE"), 0)
    desc3.putBoolean(sID("gi_ENABLE_PROMPT_FILTER"), True)
    desc2.putObject(sID("clio"), sID("clio"), desc3)
    desc1.putObject(sID("serviceOptionsList"), sID("target"), desc2)
    app.executeaction(sID("syntheticFill"), desc1, NO_DIALOG)

generative_fill_edges(layer=None)

Rasterizes a given layer (or active layer) and fills remaining pixels using AI powered generative fill. @param layer: Layer to use for the generative fill. Uses active if not provided.

Source code in src/helpers/design.py
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
def generative_fill_edges(layer: Optional[ArtLayer] = None) -> None:
    """
    Rasterizes a given layer (or active layer) and fills remaining pixels using AI powered generative fill.
    @param layer: Layer to use for the generative fill. Uses active if not provided.
    """
    # Set active layer if needed, then rasterize
    docref: Document = app.activeDocument
    if layer:
        docref.activeLayer = layer
    else:
        layer = docref.activeLayer
    docref.activeLayer.rasterize(RasterizeType.EntireLayer)

    # Create a fill layer the size of the document
    fill_layer: ArtLayer = docref.artLayers.add()
    fill_layer.move(layer, ElementPlacement.PlaceAfter)
    fill_layer_primary()
    fill_layer.opacity = 0
    select_layers([layer, fill_layer])
    smart = smart_layer()
    edit_smart_layer(smart)

    # Select pixels of active layer and invert
    docref = app.activeDocument
    select_layer_pixels(docref.activeLayer)
    selection = docref.selection
    selection.invert()

    # Guard against no selection made
    try:
        _ = selection.bounds
    except PS_EXCEPTIONS:
        return

    # Expand and smooth selection
    selection.expand(8)
    selection.smooth(4)

    # Call Generative fill
    generative_fill()
    selection.deselect()
    docref.close(SaveOptions.SaveChanges)

repair_edges(edge=6)

Select a small area at the edges of an image and content aware fill to repair upscale damage. @param edge: How many pixels to select at the edge.

Source code in src/helpers/design.py
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
def repair_edges(edge: int = 6) -> None:
    """
    Select a small area at the edges of an image and content aware fill to repair upscale damage.
    @param edge: How many pixels to select at the edge.
    """
    # Select all
    desc632724 = ActionDescriptor()
    ref489 = ActionReference()
    ref489.putProperty(sID("channel"), sID("selection"))
    desc632724.putReference(sID("target"), ref489)
    desc632724.putEnumerated(sID("to"), sID("ordinal"), sID("allEnum"))
    app.ExecuteAction(sID("set"), desc632724, NO_DIALOG)

    # Contract selection
    contract = ActionDescriptor()
    contract.putUnitDouble(sID("by"), sID("pixelsUnit"), edge)
    contract.putBoolean(sID("selectionModifyEffectAtCanvasBounds"), True)
    app.ExecuteAction(sID("contract"), contract, NO_DIALOG)

    # Inverse the selection
    app.ExecuteAction(sID("inverse"), None, NO_DIALOG)

    # Content aware fill
    desc_caf = ActionDescriptor()
    desc_caf.putEnumerated(
        sID("cafSamplingRegion"),
        sID("cafSamplingRegion"),
        sID("cafSamplingRegionRectangular")
    )
    desc_caf.putBoolean(sID("cafSampleAllLayers"), False)
    desc_caf.putEnumerated(
        sID("cafColorAdaptationLevel"),
        sID("cafColorAdaptationLevel"),
        sID("cafColorAdaptationDefault")
    )
    desc_caf.putEnumerated(
        sID("cafRotationAmount"),
        sID("cafRotationAmount"),
        sID("cafRotationAmountNone")
    )
    desc_caf.putBoolean(sID("cafScale"), False)
    desc_caf.putBoolean(sID("cafMirror"), False)
    desc_caf.putEnumerated(
        sID("cafOutput"),
        sID("cafOutput"),
        sID("cafOutputToNewLayer")
    )
    app.ExecuteAction(sID("cafWorkspace"), desc_caf, NO_DIALOG)

    # Deselect
    app.activeDocument.selection.deselect()