@pixerate/editor 0.191.0 → 0.192.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/image-editor/controller.d.cts +19 -0
- package/dist/image-editor/controller.d.ts +19 -0
- package/dist/image-editor/index.cjs +236 -131
- package/dist/image-editor/index.js +120 -15
- package/dist/image-editor/selection/tool-options.d.cts +7 -1
- package/dist/image-editor/selection/tool-options.d.ts +7 -1
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +1 -0
- package/skills/pixerate-image-editor/references/shapes-and-type.md +2 -0
- package/skills/pixerate-image-selection-masking/SKILL.md +2 -0
|
@@ -9346,6 +9346,62 @@ var ImageEditorController = class {
|
|
|
9346
9346
|
* text wrapped in `box`. Refused without a text shaper. Returns its id.
|
|
9347
9347
|
*/
|
|
9348
9348
|
addTextLayer(init) {
|
|
9349
|
+
const layer = this.layoutTypeLayer(init);
|
|
9350
|
+
if (init.opacity !== void 0 && !(Number.isFinite(init.opacity) && init.opacity >= 0 && init.opacity <= 1)) throw new LayerOperationError("opacity must be between 0 and 1.");
|
|
9351
|
+
if (init.blend !== void 0 && !isBlendMode(init.blend)) throw new LayerOperationError(`Unknown blend mode "${String(init.blend)}".`);
|
|
9352
|
+
if (init.opacity !== void 0) layer.opacity = init.opacity;
|
|
9353
|
+
if (init.blend !== void 0) layer.blend = init.blend;
|
|
9354
|
+
const next = placeLayer(this.state.layers, layer, this.newLayerPlacement(init));
|
|
9355
|
+
this.commitLayers(next, () => {
|
|
9356
|
+
this.state.activeLayerId = layer.id;
|
|
9357
|
+
this.state.activeTarget = "pixels";
|
|
9358
|
+
});
|
|
9359
|
+
return layer.id;
|
|
9360
|
+
}
|
|
9361
|
+
/**
|
|
9362
|
+
* Type Mask (horizontal or vertical): a selection in the shape of `text`, laid out and drawn as
|
|
9363
|
+
* `addTextLayer` would place it (font, size, tracking, box, rotation), combined with the current
|
|
9364
|
+
* selection by `mode`. Vertical type stacks the characters top to bottom, centred on `x`. No
|
|
9365
|
+
* layer is added. One undo step. Returns whether anything is selected.
|
|
9366
|
+
*/
|
|
9367
|
+
selectTextShape(init) {
|
|
9368
|
+
const vertical = init.orientation === "vertical";
|
|
9369
|
+
if (init.orientation !== void 0 && init.orientation !== "horizontal" && !vertical) throw new LayerOperationError("orientation must be horizontal or vertical.");
|
|
9370
|
+
if (init.feather !== void 0 && !(init.feather >= 0 && init.feather <= 1e3)) throw new LayerOperationError("feather must be from 0 to 1000 px.");
|
|
9371
|
+
let text = normalizeText(init.text);
|
|
9372
|
+
if (!text.trim()) throw new LayerOperationError("The text is empty, so there is nothing to select; give some characters.");
|
|
9373
|
+
let paragraph = init.paragraph;
|
|
9374
|
+
if (vertical) {
|
|
9375
|
+
if (/\n/.test(text)) throw new LayerOperationError("Vertical type masks take one column; select each column separately (mode add) instead of line breaks.");
|
|
9376
|
+
if (init.box) throw new LayerOperationError("Vertical type masks are point text; leave out box.");
|
|
9377
|
+
text = Array.from(text).join("\n");
|
|
9378
|
+
paragraph = { align: "center", ...paragraph ?? {} };
|
|
9379
|
+
}
|
|
9380
|
+
const layer = this.layoutTypeLayer({ ...init, text, paragraph });
|
|
9381
|
+
const { width, height } = this.naturalSize;
|
|
9382
|
+
const b = contentBounds(layer.surface);
|
|
9383
|
+
const x0 = Math.max(0, b?.x ?? 0);
|
|
9384
|
+
const y0 = Math.max(0, b?.y ?? 0);
|
|
9385
|
+
const x1 = Math.min(width, b ? b.x + b.width : 0);
|
|
9386
|
+
const y1 = Math.min(height, b ? b.y + b.height : 0);
|
|
9387
|
+
let mask;
|
|
9388
|
+
if (!b || x1 <= x0 || y1 <= y0) mask = createSelectionMask(width, height);
|
|
9389
|
+
else {
|
|
9390
|
+
const rect = { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };
|
|
9391
|
+
const rgba = readRegion(layer.surface, rect);
|
|
9392
|
+
const bytes = new Uint8Array(rect.width * rect.height);
|
|
9393
|
+
const hard = init.antiAlias === false;
|
|
9394
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
9395
|
+
const a = rgba[i * 4 + 3];
|
|
9396
|
+
bytes[i] = hard ? a >= 128 ? 255 : 0 : a;
|
|
9397
|
+
}
|
|
9398
|
+
mask = selectionFromRegion(width, height, rect, bytes);
|
|
9399
|
+
}
|
|
9400
|
+
if ((init.feather ?? 0) > 0 && mask.bounds) mask = modifySelection(mask, "feather", init.feather);
|
|
9401
|
+
return this.setSelection(mask.bounds ? mask : null, init.mode ?? "replace");
|
|
9402
|
+
}
|
|
9403
|
+
/** A type layer laid out and drawn from `init` (not added): the shared part of `addTextLayer` and the type masks. */
|
|
9404
|
+
layoutTypeLayer(init) {
|
|
9349
9405
|
const text = normalizeText(init.text);
|
|
9350
9406
|
if (!Number.isFinite(init.x) || !Number.isFinite(init.y)) throw new LayerOperationError("x and y must be numbers (document pixels).");
|
|
9351
9407
|
const style = patchTextStyle({ ...DEFAULT_TEXT_STYLE, color: this.state.paint.foreground }, init.style ?? {});
|
|
@@ -9357,19 +9413,9 @@ var ImageEditorController = class {
|
|
|
9357
9413
|
frame = { kind: "box", width, height };
|
|
9358
9414
|
}
|
|
9359
9415
|
if (init.rotation !== void 0 && !Number.isFinite(init.rotation)) throw new LayerOperationError("rotation must be a number of degrees.");
|
|
9360
|
-
if (init.opacity !== void 0 && !(Number.isFinite(init.opacity) && init.opacity >= 0 && init.opacity <= 1)) throw new LayerOperationError("opacity must be between 0 and 1.");
|
|
9361
|
-
if (init.blend !== void 0 && !isBlendMode(init.blend)) throw new LayerOperationError(`Unknown blend mode "${String(init.blend)}".`);
|
|
9362
9416
|
const t = [1, 0, 0, 1, init.x, init.y];
|
|
9363
9417
|
const transform = init.rotation ? multiplyMatrices(t, rotateMatrix(init.rotation, { x: init.x, y: init.y })) : t;
|
|
9364
|
-
|
|
9365
|
-
if (init.opacity !== void 0) layer.opacity = init.opacity;
|
|
9366
|
-
if (init.blend !== void 0) layer.blend = init.blend;
|
|
9367
|
-
const next = placeLayer(this.state.layers, layer, this.newLayerPlacement(init));
|
|
9368
|
-
this.commitLayers(next, () => {
|
|
9369
|
-
this.state.activeLayerId = layer.id;
|
|
9370
|
-
this.state.activeTarget = "pixels";
|
|
9371
|
-
});
|
|
9372
|
-
return layer.id;
|
|
9418
|
+
return createTypeLayer({ text, runs: [{ length: text.length, style }], paragraphs: [{ length: text.length, style: paragraph }], frame, transform, name: init.name ?? typeLayerName(text) }, this.textEnv());
|
|
9373
9419
|
}
|
|
9374
9420
|
/**
|
|
9375
9421
|
* Replaces text [start, end) (default all of it) with `text`; inserted text takes the style at
|
|
@@ -11255,7 +11301,7 @@ var DEPTH_MASK_CONTROLS = [
|
|
|
11255
11301
|
// src/image-editor/playback.ts
|
|
11256
11302
|
var IMAGE_PLAYBACK_KIND = "layer";
|
|
11257
11303
|
var PAINT = /^(paint_stroke|clone_stamp|heal_brush|spot_heal|dodge|burn|sponge|smudge|blur_stroke|sharpen_stroke|history_brush|set_clone_source|set_clone_transform|fill_pixels|clear_pixels|set_paint|set_retouch_options|load_mixer_brush|clean_mixer_brush)$/;
|
|
11258
|
-
var SELECT = /^(select_region|select_subject|select_by_color|select_all|deselect|invert_selection|select_rect|select_ellipse|select_polygon|select_line|magic_wand|grow_selection|select_similar|reselect|select_layer_transparency|select_color_range|load_selection_from_image|load_selection_from_mask|set_selection_display|modify_selection|refine_selection|transform_selection|apply_selection_as_mask|layer_via_copy|stroke_selection|content_aware_fill|copy_pixels|cut_pixels|paste_pixels|save_selection|load_selection_from_channel|set_channel_properties|duplicate_channel|delete_channel|load_channel_from_image|set_quick_mask|path_to_selection)$/;
|
|
11304
|
+
var SELECT = /^(select_region|select_subject|select_by_color|select_all|deselect|invert_selection|select_rect|select_ellipse|select_polygon|select_line|magic_wand|grow_selection|select_similar|reselect|select_layer_transparency|select_color_range|load_selection_from_image|load_selection_from_mask|set_selection_display|modify_selection|refine_selection|transform_selection|apply_selection_as_mask|layer_via_copy|stroke_selection|content_aware_fill|copy_pixels|cut_pixels|paste_pixels|save_selection|load_selection_from_channel|set_channel_properties|duplicate_channel|delete_channel|load_channel_from_image|set_quick_mask|path_to_selection|select_text_shape)$/;
|
|
11259
11305
|
var ANNOTATE = /^(add_shape_layer|update_shape|add_shape_component|set_shape_component|remove_shape_component|merge_shape_components|add_text_layer|set_text|set_text_style|set_paragraph_style|set_text_frame|convert_text_to_shape)$/;
|
|
11260
11306
|
var CROP = /^(set_crop|crop_to_selection|reset_crop|set_rotation|flip_image|transform_layer|warp_layer|flip_layer|offset_layer)$/;
|
|
11261
11307
|
var ADJUST = /^(set_adjustments|add_adjustment_layer|update_adjustment_layer|remove_adjustment_layer|move_adjustment_layer)$/;
|
|
@@ -11376,7 +11422,9 @@ var SELECTION_TOOLS = [
|
|
|
11376
11422
|
{ id: "column", label: "Single Column" },
|
|
11377
11423
|
{ id: "lasso", label: "Lasso" },
|
|
11378
11424
|
{ id: "polygon", label: "Polygonal Lasso" },
|
|
11379
|
-
{ id: "range", label: "Color Range" }
|
|
11425
|
+
{ id: "range", label: "Color Range" },
|
|
11426
|
+
{ id: "type", label: "Type Mask" },
|
|
11427
|
+
{ id: "type-vertical", label: "Vertical Type Mask" }
|
|
11380
11428
|
];
|
|
11381
11429
|
var COLOR_RANGE_PRESET_LABELS = {
|
|
11382
11430
|
reds: "Reds",
|
|
@@ -11407,7 +11455,10 @@ var DEFAULT_SELECTION_TOOL_OPTIONS = {
|
|
|
11407
11455
|
feather: 0,
|
|
11408
11456
|
fuzziness: 40,
|
|
11409
11457
|
localized: 0,
|
|
11410
|
-
rangeInvert: false
|
|
11458
|
+
rangeInvert: false,
|
|
11459
|
+
typeText: "Type",
|
|
11460
|
+
typeSize: 72,
|
|
11461
|
+
typeFamily: "sans-serif"
|
|
11411
11462
|
};
|
|
11412
11463
|
function selectionModeFromModifiers(modifiers, fallback) {
|
|
11413
11464
|
if (modifiers.shiftKey && modifiers.altKey) return "intersect";
|
|
@@ -13789,6 +13840,24 @@ var SelectionGesture = class {
|
|
|
13789
13840
|
}
|
|
13790
13841
|
return;
|
|
13791
13842
|
}
|
|
13843
|
+
if (options.tool === "type" || options.tool === "type-vertical") {
|
|
13844
|
+
this.start = null;
|
|
13845
|
+
try {
|
|
13846
|
+
this.controller.selectTextShape({
|
|
13847
|
+
text: options.typeText || "Type",
|
|
13848
|
+
x: point.x,
|
|
13849
|
+
y: point.y,
|
|
13850
|
+
orientation: options.tool === "type-vertical" ? "vertical" : "horizontal",
|
|
13851
|
+
style: { fontSize: options.typeSize ?? 72, fontFamily: options.typeFamily || "sans-serif" },
|
|
13852
|
+
mode: selectionModeFromModifiers(modifiers, options.mode),
|
|
13853
|
+
feather: options.feather ?? 0,
|
|
13854
|
+
antiAlias: options.antiAlias
|
|
13855
|
+
});
|
|
13856
|
+
} catch (err) {
|
|
13857
|
+
this.error = err instanceof Error ? err.message : String(err);
|
|
13858
|
+
}
|
|
13859
|
+
return;
|
|
13860
|
+
}
|
|
13792
13861
|
if (options.tool === "wand" || options.tool === "range") {
|
|
13793
13862
|
this.start = null;
|
|
13794
13863
|
const common = {
|
|
@@ -16333,7 +16402,43 @@ function createImageTypeTools(controller) {
|
|
|
16333
16402
|
return { layer: layerSummary(id), shape: controller.getShapeLayer(id), warnings };
|
|
16334
16403
|
}
|
|
16335
16404
|
};
|
|
16336
|
-
|
|
16405
|
+
const typeMask = {
|
|
16406
|
+
name: "select_text_shape",
|
|
16407
|
+
title: "Select the shape of text",
|
|
16408
|
+
description: "Horizontal / Vertical Type Mask: selects the shape of text laid out as add_text_layer would place it (font family, size, weight, tracking, box, rotation; colour is ignored), combined with the current selection by mode. No layer is added. Vertical stacks the characters top to bottom, centred on x (one column; add more with mode add). Use it to cut text out of an image (layer_via_copy), fill letters with a photo (add_layer_mask), or mask an adjustment. Check fonts with list_fonts first. One undo step. Returns the selection.",
|
|
16409
|
+
inputSchema: {
|
|
16410
|
+
type: "object",
|
|
16411
|
+
properties: {
|
|
16412
|
+
text: { type: "string", minLength: 1, description: "The text; \\n starts a new line (horizontal only)." },
|
|
16413
|
+
x: { type: "number", description: "Document x of the origin, pixels (vertical: the column centre)." },
|
|
16414
|
+
y: { type: "number", description: "Document y of the origin (the first baseline), pixels." },
|
|
16415
|
+
orientation: { type: "string", enum: ["horizontal", "vertical"], description: "horizontal (default) or vertical (characters stacked top to bottom)." },
|
|
16416
|
+
box: {
|
|
16417
|
+
type: "object",
|
|
16418
|
+
description: "Horizontal only: wrap in this box, pixels.",
|
|
16419
|
+
properties: { width: { type: "number", minimum: 1, description: "Box width, pixels." }, height: { type: "number", minimum: 1, description: "Box height, pixels." } },
|
|
16420
|
+
required: ["width", "height"],
|
|
16421
|
+
additionalProperties: false
|
|
16422
|
+
},
|
|
16423
|
+
style: { type: "object", description: "Character style (size in px, family, weight, tracking\u2026; colour is ignored).", properties: STYLE, additionalProperties: false },
|
|
16424
|
+
paragraph: { type: "object", description: "Paragraph style (alignment, leading\u2026).", properties: PARAGRAPH, additionalProperties: false },
|
|
16425
|
+
rotation: { type: "number", description: "Degrees clockwise about the origin (default 0)." },
|
|
16426
|
+
mode: { type: "string", enum: ["replace", "add", "subtract", "intersect"], description: "How it combines with the current selection (default replace)." },
|
|
16427
|
+
feather: { type: "number", minimum: 0, maximum: 1e3, description: "Feather radius in px (default 0)." },
|
|
16428
|
+
antiAlias: { type: "boolean", description: "Soft glyph edges (default true)." }
|
|
16429
|
+
},
|
|
16430
|
+
required: ["text", "x", "y"],
|
|
16431
|
+
additionalProperties: false
|
|
16432
|
+
},
|
|
16433
|
+
annotations: { title: "Select the shape of text", ...WRITE9 },
|
|
16434
|
+
handler: (args) => {
|
|
16435
|
+
const init = pick2(args, ["text", "x", "y", "orientation", "box", "style", "paragraph", "rotation", "mode", "feather", "antiAlias"]);
|
|
16436
|
+
controller.selectTextShape(init);
|
|
16437
|
+
const sel = controller.getSelection();
|
|
16438
|
+
return { empty: !sel, bounds: sel?.bounds ?? null, area: Math.round(sel ? selectionArea(sel) : 0) };
|
|
16439
|
+
}
|
|
16440
|
+
};
|
|
16441
|
+
return [listFonts, get, add2, setText, setStyle, setParagraph, setFrame, toShape, typeMask];
|
|
16337
16442
|
}
|
|
16338
16443
|
|
|
16339
16444
|
// src/image-editor/tools.ts
|
|
@@ -2,7 +2,7 @@ import { type SelectionMask, type SelectionMode } from './mask.cjs';
|
|
|
2
2
|
import type { ColorDistance, ColorRangePreset } from './wand.cjs';
|
|
3
3
|
import type { RefineEdgeParams } from './refine.cjs';
|
|
4
4
|
/** Selection tools of the image editor UI. */
|
|
5
|
-
export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'range';
|
|
5
|
+
export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'range' | 'type' | 'type-vertical';
|
|
6
6
|
export declare const SELECTION_TOOLS: readonly {
|
|
7
7
|
id: SelectionToolKind;
|
|
8
8
|
label: string;
|
|
@@ -31,6 +31,12 @@ export interface SelectionToolOptions {
|
|
|
31
31
|
localized?: number;
|
|
32
32
|
/** Color Range: select everything except the sampled colours. */
|
|
33
33
|
rangeInvert?: boolean;
|
|
34
|
+
/** Type Mask: the text to select (default "Type"). A click places its first baseline. */
|
|
35
|
+
typeText?: string;
|
|
36
|
+
/** Type Mask: font size in px (default 72). */
|
|
37
|
+
typeSize?: number;
|
|
38
|
+
/** Type Mask: font family (default sans-serif). */
|
|
39
|
+
typeFamily?: string;
|
|
34
40
|
}
|
|
35
41
|
export declare const DEFAULT_SELECTION_TOOL_OPTIONS: SelectionToolOptions;
|
|
36
42
|
/**
|
|
@@ -2,7 +2,7 @@ import { type SelectionMask, type SelectionMode } from './mask.js';
|
|
|
2
2
|
import type { ColorDistance, ColorRangePreset } from './wand.js';
|
|
3
3
|
import type { RefineEdgeParams } from './refine.js';
|
|
4
4
|
/** Selection tools of the image editor UI. */
|
|
5
|
-
export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'range';
|
|
5
|
+
export type SelectionToolKind = 'wand' | 'rect' | 'ellipse' | 'row' | 'column' | 'lasso' | 'polygon' | 'range' | 'type' | 'type-vertical';
|
|
6
6
|
export declare const SELECTION_TOOLS: readonly {
|
|
7
7
|
id: SelectionToolKind;
|
|
8
8
|
label: string;
|
|
@@ -31,6 +31,12 @@ export interface SelectionToolOptions {
|
|
|
31
31
|
localized?: number;
|
|
32
32
|
/** Color Range: select everything except the sampled colours. */
|
|
33
33
|
rangeInvert?: boolean;
|
|
34
|
+
/** Type Mask: the text to select (default "Type"). A click places its first baseline. */
|
|
35
|
+
typeText?: string;
|
|
36
|
+
/** Type Mask: font size in px (default 72). */
|
|
37
|
+
typeSize?: number;
|
|
38
|
+
/** Type Mask: font family (default sans-serif). */
|
|
39
|
+
typeFamily?: string;
|
|
34
40
|
}
|
|
35
41
|
export declare const DEFAULT_SELECTION_TOOL_OPTIONS: SelectionToolOptions;
|
|
36
42
|
/**
|
package/package.json
CHANGED
|
@@ -307,6 +307,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
|
|
|
307
307
|
| `set_paragraph_style` | Set `align`, `autoLeading`, indents, `spaceBefore` / `spaceAfter` on every paragraph `start`..`end` touches (default all). Returns the text layer. |
|
|
308
308
|
| `set_text_frame` | `kind: point` (one line per paragraph) or `kind: box` with `width` and `height` (wrapping paragraph text), or resize a box; the first line stays put. Returns the text layer and its lines. |
|
|
309
309
|
| `convert_text_to_shape` | Type › Convert to Shape: glyph outlines become a shape layer with the same id (text no longer editable; fill from the first run's colour; `warnings` lists losses). Needs an outline font shaper. Destructive. |
|
|
310
|
+
| `select_text_shape` | Horizontal / Vertical Type Mask: select the shape of `text` at `x`, `y` (style, box, rotation as `add_text_layer`; `orientation` `vertical` stacks the letters), by `mode`, with `feather`. No layer is added. Returns the selection. |
|
|
310
311
|
| `add_shape_layer` | Add a vector shape layer above the active layer (or placed with `aboveId` / `belowId` / `parentId` / `index`) and make it active: `shape` (live rect, ellipse, polygon, star or line, document px), `subpaths` (SVG path data or anchors) or several `components` with path ops; `fill` (colour, gradient or pattern, default the foreground colour; `null` none), `stroke` (`width`, `paint`, `align`, `cap`, `join`, `miterLimit`, `dashes` in widths), `fillRule`, `name`, `opacity`, `blend`. Returns the `layer` and the `shape`. One undo step. |
|
|
311
312
|
| `update_shape` | Restyle a shape: `fill` (replace, `null` removes), `stroke` (only the settings given change; `null` removes; a new stroke needs `width` and `paint`), `fillRule`. Geometry is unchanged. Returns the shape. |
|
|
312
313
|
| `set_shape_component` | Change component `componentIndex` (from `get_shape_layer`): `shape` fields (without `kind`, or with the same kind, unset fields keep their values), new `subpaths` (ends the live shape), or `op`. Returns the shape. |
|
|
@@ -49,6 +49,8 @@ Detail for the `pixerate-image-editor` skill: how to draw and edit vector shapes
|
|
|
49
49
|
refused: rasterize first. Masks, clipping, blend modes and effects work as on any layer.
|
|
50
50
|
5. **Outlines and pixels**: `convert_text_to_shape` makes glyph outlines a shape layer (needs an
|
|
51
51
|
outline font shaper; the browser canvas cannot); `rasterize_layer` makes it pixels.
|
|
52
|
+
For a selection in the shape of text (photo-filled letters, text cut out of the image) use
|
|
53
|
+
`select_text_shape`, the Type Mask: it lays the text out the same way but adds no layer.
|
|
52
54
|
|
|
53
55
|
## Photoshop files
|
|
54
56
|
|
|
@@ -106,6 +106,7 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
|
|
|
106
106
|
| A cutout, shape or text already on its own layer | `select_layer_transparency` (exact, soft where the layer is) |
|
|
107
107
|
| A dead pixel row or column | `select_line`, then fill or heal it |
|
|
108
108
|
| Smooth curves you can describe (a logo, a road, a lens) | `path_to_selection` with Bézier `subpaths` or SVG path data |
|
|
109
|
+
| Letters (photo-filled text, text cut out of the image) | `select_text_shape` (check fonts with `list_fonts`), then `add_layer_mask` or `layer_via_copy` |
|
|
109
110
|
|
|
110
111
|
## Recipes
|
|
111
112
|
|
|
@@ -203,6 +204,7 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
|
|
|
203
204
|
- `add_vector_mask`, `set_vector_mask_path`, `set_vector_mask_properties`, `get_vector_mask`: sharp, editable path masks
|
|
204
205
|
- `rasterize_vector_mask`, `remove_vector_mask`: turn a vector mask into pixels, or drop it
|
|
205
206
|
- `selection_to_path`, `path_to_selection`: a selection's outline as Bézier paths, and a path back into a selection
|
|
207
|
+
- `select_text_shape`: the Type Mask, a selection in the shape of text
|
|
206
208
|
- `modify_selection`: expand, contract, feather, border, smooth
|
|
207
209
|
- `refine_selection`: snap the edge to the image
|
|
208
210
|
- `transform_selection`: move or scale the outline only
|