Skip to content

Design

src.helpers.design.fill_empty_area(reference: ArtLayer, color: Optional[SolidColor] = None) -> ArtLayer

Fills empty gaps on an art layer, such as a symbol, with a solid color.

Parameters:

Name Type Description Default
reference ArtLayer

Reference layer to put the new fill layer underneath

required
color SolidColor | None

Color of the background fill

None
Source code in src\helpers\design.py
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
def fill_empty_area(reference: ArtLayer, color: Optional[SolidColor] = None) -> ArtLayer:
    """Fills empty gaps on an art layer, such as a symbol, with a solid color.

    Args:
        reference: Reference layer to put the new fill layer underneath
        color: Color of the background fill
    """
    # Magic Wand contiguous outside symbol
    docref = APP.activeDocument
    docsel = docref.selection
    coords = ActionDescriptor()
    click1 = ActionDescriptor()
    ref1 = ActionReference()
    idPaint = sID('paint')
    idPixel = sID('pixelsUnit')
    idTolerance = sID('tolerance')
    coords.putUnitDouble(sID('horizontal'), idPixel, 5)
    coords.putUnitDouble(sID('vertical'), idPixel, 5)
    ref1.putProperty(sID('channel'), sID('selection'))
    click1.putReference(sID('target'), ref1)
    click1.putObject(sID('to'), idPaint, coords)
    click1.putInteger(idTolerance, 12)
    click1.putBoolean(sID('antiAlias'), True)
    APP.executeAction(sID('set'), click1)

    # Invert selection
    docsel.invert()
    docsel.contract(1)

    # Make a new layer
    layer = docref.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(sID('from'), idPaint, coords)
    click3.putInteger(idTolerance, 0)
    click3.putEnumerated(sID('using'), sID('fillContents'), sID('foregroundColor'))
    click3.putBoolean(sID('contiguous'), False)
    APP.executeAction(sID('fill'), click3)

    # Clear Selection
    docsel.deselect()
    return layer

src.helpers.design.content_aware_fill_edges(layer: Optional[ArtLayer] = None, feather: bool = False) -> None

Fills pixels outside art layer using content-aware fill.

Parameters:

Name Type Description Default
layer ArtLayer | None

Layer to use for the content aware fill. Uses active if not provided.

None
feather bool

Whether to feather the selection before performing the fill operation.

False
Source code in src\helpers\design.py
 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
def content_aware_fill_edges(layer: Optional[ArtLayer] = None, feather: bool = False) -> None:
    """Fills pixels outside art layer using content-aware fill.

    Args:
        layer: Layer to use for the content aware fill. Uses active if not provided.
        feather: Whether to feather the selection before performing the fill operation.
    """
    # Set active layer if needed, then rasterize
    docref = APP.activeDocument
    if layer:
        docref.activeLayer = layer
    docref.activeLayer.rasterize(RasterizeType.EntireLayer)

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

    # Guard against no selection made
    try:
        # Create a feathered or smoothed selection, then invert
        if feather:
            selection.contract(22)
            selection.feather(8)
        else:
            selection.contract(10)
            selection.smooth(5)
        selection.invert()
        content_aware_fill()
    except PS_EXCEPTIONS:
        # Unable to fill due to invalid selection
        CONSOLE.update(
            "Couldn't make a valid selection!\n"
            "Skipping automated fill.")

    # Clear selection
    with suppress(Exception):
        selection.deselect()

src.helpers.design.generative_fill_edges(layer: Optional[ArtLayer] = None, feather: bool = False, close_doc: bool = True, docref: Optional[Document] = None) -> Optional[Document]

Fills pixels outside an art layer using AI powered generative fill.

Parameters:

Name Type Description Default
layer ArtLayer | None

Layer to use for the generative fill. Uses active if not provided.

None
feather bool

Whether to feather the selection before performing the fill operation.

False
close_doc bool

Whether to close the smart layer document after the fill operation.

True
docref Document | None

Reference document, use active if not provided.

None

Returns:

Type Description
Document | None

Smart layer document if Generative Fill operation succeeded, otherwise None.

Source code in src\helpers\design.py
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
def generative_fill_edges(
    layer: Optional[ArtLayer] = None,
    feather: bool = False,
    close_doc: bool = True,
    docref: Optional[Document] = None
) -> Optional[Document]:
    """Fills pixels outside an art layer using AI powered generative fill.

    Args:
        layer: Layer to use for the generative fill. Uses active if not provided.
        feather: Whether to feather the selection before performing the fill operation.
        close_doc: Whether to close the smart layer document after the fill operation.
        docref: Reference document, use active if not provided.

    Returns:
        Smart layer document if Generative Fill operation succeeded, otherwise None.
    """
    # Set docref and use active layer if not provided
    docref = docref or APP.activeDocument
    if not layer:
        layer = docref.activeLayer
    docref.activeLayer = layer

    # 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

    # Create a smart layer document and enter it
    select_layers([layer, fill_layer])
    smart_layer()
    edit_smart_layer()

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

    # Guard against no selection made
    try:
        # Create a feathered or smoothed selection, then invert
        if feather:
            selection.contract(22)
            selection.feather(8)
        else:
            selection.contract(10)
            selection.smooth(5)
        selection.invert()
        try:
            generative_fill()
        except PS_EXCEPTIONS:
            # Generative fill call not responding
            CONSOLE.update("Generative fill failed!\n"
                           "Falling back to Content Aware Fill.")
            docref.activeLayer.rasterize(RasterizeType.EntireLayer)
            content_aware_fill()
            close_doc = True
    except PS_EXCEPTIONS:
        # Unable to fill due to invalid selection
        CONSOLE.update("Couldn't make a valid selection!\n"
                       "Skipping automated fill.")
        close_doc = True

    # Deselect
    with suppress(Exception):
        selection.deselect()

    # Doc requested and operation successful
    if not close_doc:
        return docref
    docref.close(SaveOptions.SaveChanges)
    return

src.helpers.design.content_aware_fill() -> None

Fills the current selection using content aware fill.

Source code in src\helpers\design.py
201
202
203
204
205
206
207
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)

src.helpers.design.generative_fill() -> None

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

Source code in src\helpers\design.py
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
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)
    desc3.putBoolean(sID("dualCrop"), True)
    desc3.putString(sID("gi_ADVANCED"), """{"enable_mts":true}""")
    desc2.putObject(sID("clio"), sID("clio"), desc3)
    desc1.putObject(sID("serviceOptionsList"), sID("target"), desc2)
    APP.executeAction(sID("syntheticFill"), desc1, NO_DIALOG)

src.helpers.design.repair_edges(edge: int = 6) -> None

Select a small area at the edges of an image and content aware fill to repair upscale damage.

Parameters:

Name Type Description Default
edge int

How many pixels to select at the edge.

6
Source code in src\helpers\design.py
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
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.

    Args:
        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()