Commit Graph
136 Commits
Author SHA1 Message Date
Gabriel Heuser 24be8ea6e2 Add editable arrowheads to Line tool (#6408) (#9259)
* Add configurable arrowheads to line tool

* Add editable arrowheads to Line tool

* Fix arrow scaling validation and PSD import defaults

* Preserve arrow geometry under affine transforms

* Preserve scaled arrowhead dimensions and prevent non-finite geometry

* Localize arrowhead Start and End labels

* Fix arrow transform editability and short line handling

* Fix PSD arrow concavity serialization

* Fix arrow dimension fallback validation

* Refine arrowhead options UI to match Photoshop

* Add opt-in arrowhead defaults to Shape properties

* Fix tool reset syntax after upstream merge

* Validate arrowhead flags and clamp copied defaults

* Preserve editable headless lines under transforms

* Fix arrowhead UI imports and formatting after v0.17 merge

* Format arrowhead properties after v0.18 merge

* Fix Clippy lint errors in arrow geometry and tests

* Fix headless arrow geometry after nonuniform scaling

* Fix proportional arrow scaling in Properties panel

* Fix Rust formatting in Properties arrow scaling tests

* test: validate invalid arrowhead dimensions without mutation

* fix: preserve arrow compatibility with older pcraft versions
2026-11-01 18:46:05 -05:00
David Mann b2db24400e Merge main at b1cfff9d3 into integration batch 10
Conflicts resolved as unions of both sides: codecs re-exports (PngRows + TiffBands), smart_cmds source imports
(main's source_surface_from_buffer and cache API with #8571's svg_source_image_in/convert_svg_source; main's
duplicate rendering import dropped), io flat.rs test modules (#8536's banded TIFF tests and main's PNG ones),
menus/invoke.rs (#8571's smart SVG intercept and main's layer.selectUnderCursor), state.rs (#8311's
type_box_overflow and main's type_insertion_style), the canvas_geom test keeps the is_finite guard, docs
status lines keep both change lists, i18n catalogs keep both sides' rows (hi.tsv: main's wording where both
added the same string).
2026-10-30 14:28:02 +01:00
FredApps 0f120bbda1 Merge current main, reconcile streamed PNG profiles and workflow inputs 2026-10-30 06:52:05 +01:00
David Mann fea300dd4c Merge origin/main into integration batch 10 2026-10-30 01:47:01 +01:00
FredApps c93c0bb36d Merge main into trimmed integration; reconcile blur, pattern and Liquify behavior 2026-10-29 23:47:53 +01:00
FredApps 29bf828ec2 Merge upstream main into reviewed performance integration
Preserve both UI parity status entries; retain all reviewed performance changes.
2026-10-29 23:35:19 +01:00
FredApps 255ecc81d0 Remove 21 requested PR contributions from the 108-PR integration
Retain 87 PRs. Reconcile overlapping integration fixes and update testing scope.
Removed: #7153 #7742 #5493 #9282 #3455 #9172 #3139 #3967 #4783 #5453 #8347 #1991 #3345 #4042 #4305 #4883 #4890 #5090 #6621 #5870 #5939.

Combined CI pending for this source; historical portable artifact is not applicable.
2026-10-29 18:49:17 +01:00
David Mann afacb057f7 Merge origin/main into pr-8571-fixup
Conflicts: svg.rs (main's bufread inflate and shared render helper, kept
with the PR's bounded raster path), psd_import.rs (the PR's vector
preview moved into main's smart_content), menus/invoke.rs (the SVG
intercept moved into main's split invoke_unguarded), rendering.rs and
the ui-egui import lists (main's explicit imports), Cargo.lock, docs
status lines (unions).
2026-10-29 16:38:24 +01:00
FredApps 53dbfe280c Integrate 16 reviewed performance and usability PRs
Includes #8472 #8488 #9461 #9793 #9809 #9813 #9814 #9822 #9826 #9847 #9850 #9852 #9921 #9924 #9961 #10080 at reviewed heads.

Preserves each PR head as a parent. Reconciles CI workflow inputs and benchmark jobs without dropping checks. Excludes #8536 #9825 #9969 #10129. Combined CI pending; no manual validation claimed.
2026-10-29 14:31:15 +01:00
FredApps a62b9ee217 Merge approved PhotoCraft PR #8347: Type: Roman Hanging Punctuation (trailing marks hang, don't steer the alignment)
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Ported hanging punctuation into current line placement and kerning modules, preserving forced breaks, bounded edge-tip row merging, justified-last-line behavior, area exclusions and independent Roman orientation shaping; kept PSD/engine/UI regression coverage.
2026-10-29 05:22:57 +01:00
FredApps 2e2eec3a43 Merge approved PhotoCraft PR #9478: fix(text): vertical type sets roman letters upright, one per em, as Photoshop does (#9470)
Port behavior and regression coverage into current modules; preserve current upstream behavior.
Ported upright Roman classification, independent unkerned/unligated em advances and tab reflow into current Para/VerticalPiece shaping modules; preserved area and paragraph flow; retained vertical geometry and PSD BaselineDirection regressions.
2026-10-29 05:18:29 +01:00
Federico Olivares 5a6270921c Merge main into the integration batch
Main now carries #9031 (Halftone) and #8923; this branch keeps only what it adds on top: the guard for a setting beside a Gallery stack's effect list, its test, and the PSD open warning for patterns that paint nothing.
2026-10-28 11:17:59 -03:00
aiongg 6e9fb30dbc Merge #6723: Color Lookup: PSD save keeps a .3dl/.look LUT file as loaded (#6544) 2026-10-27 17:44:05 +00:00
Khawar Zamman Wani 34af1d8e66 fix(engine): convert Bitmap images in bands 2026-10-27 13:18:39 +05:30
Don Omondi c129dd50e0 Merge main and preserve original-format embedded vector save-back 2026-10-27 00:45:48 +03:00
Hector VillarrealandClaude Opus 5.5 026f9aae34 fix(text): vertical type sets roman letters upright, one per em, as Photoshop does (#9470)
Vertical type laid Latin letters and digits on their side (the word
turned 90°), and called that Photoshop's default. Photoshop's Vertical
Type tool stacks them upright, one character per em down the column:
the run's BaselineDirection is 1 (withStream); 2 (rotated) turns them.

- doc: CharStyle::rotate_roman (false = upright, the default).
- text/psd: BaselineDirection is read (a run without one takes the
  style sheet's, which Photoshop writes as 2) and written back.
- text/layout: in vertical type, roman characters take the upright
  path unless their run rotates them. Each is given one em down the
  column (a second shaping pass sets its letter spacing from the shaped
  advance), with pair kerning off between the stacked letters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 09:53:07 -06:00
Federico Olivares c53d7d3984 Keep every pattern a render uses, add wasm limits and finish the legacy text style keys
A pattern past the per-call budget is converted under one lock and shared while in use, with a one-time warning, and the desktop budget keeps every pattern a call uses (1 GiB). The web build caps a pattern's working copy at 128 MiB and a call at 256 MiB, and the pattern readers reserve their memory fallibly on every target. The legacy text restyle writes and compares BaselineShift, FontCaps, FontBaseline, UnderlinePosition, StrikethroughPosition and the ligatures per run, keeps an automatic leading's own value, and import reads the legacy AutoKern; a corpus test edits the size in psd-tools' text.psd and checks the rest of the legacy object is unchanged.
2026-10-24 21:47:59 -03:00
Federico Olivares 11ae3d026c Address the maintainer's review of the pattern work
Align measures shape extents at the document's own sample type, a render keeps two of the largest patterns converted per call instead of rebuilding the second for every tile, a pattern too large to paint is named by every command that would use it, by pattern.list and by a PSD open warning, and a style-only edit restyles the older Photoshop's legacy text objects too, checked against the real psd-tools text.psd.
2026-10-24 20:13:56 -03:00
Federico Olivares 9f92ddd75b Hold large patterns at their own depth, return the batch to the pre-8000 code and fix the review blockers
Return the pattern code to the state before the earlier 8000 x 8000 attempts. A pattern that fits the compositor's 256 MiB cap is expanded as before; a larger 8-bit or 16-bit one is held as straight RGBA8 or RGBA16, exactly its values at 4 or 8 bytes a pixel, so Define Pattern's 8000 x 8000 paints at 8 bits. Define Pattern refuses an area the compositor could not hold with its limit, patterns whose RGBA32F texture passes 256 MiB paint on the CPU, shape bounds for Align keep full alpha precision, Define Pattern judges opacity on the stored samples, and a Halftone Gallery stack with an unknown setting beside its effect list stays verbatim.
2026-10-24 18:23:24 -03:00
Federico Olivares 66466d5dc7 Paint very large 8-bit patterns on the CPU and keep the GPU path as it was
The GPU path keeps its RGBA32F pattern textures and shader, and declines a pattern whose texture would pass 256 MiB so the CPU compositor, which holds an 8-bit pattern at 4 bytes a pixel, paints it. Add tests for the fallback, the pattern id staying the same when read in bands, and the paintable limits.
2026-10-24 17:19:09 -03:00
Federico Olivares 43afacaecc Address the second review of the integration batch
Define Pattern accepts 8-bit patterns up to 8000 x 8000: an 8-bit pattern is held at 4 bytes a pixel by the compositor and the GPU, read in bands, and what is paintable follows the working-copy bytes. Shape bounds for Align keep full alpha precision, Define Pattern judges opacity on the stored samples, and a Halftone Gallery stack with an unknown setting beside its effect list stays verbatim.
2026-10-24 16:53:47 -03:00
VladimirPozdnyakov d16841f2fb Merge remote-tracking branch 'upstream/main' into qol-type-hanging
# Conflicts:
#	crates/text/src/layout.rs
#	docs/scorecard.md
#	docs/ui-parity.md
#	scorecard/type.toml
2026-10-24 22:05:31 +03:00
Federico Olivares af83232e83 Merge PR #8902 into integration batch 2026-10-24 00:51:30 -03:00
David Mann 23d8f053cb Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/compose/src/shape_split.rs
2026-10-24 03:12:55 +02:00
David Mann feb20ebc60 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	crates/gpu/src/plan.rs
#	crates/io/src/adjust_map.rs
2026-10-24 02:59:10 +02:00
aiongg 7aaa59712a Merge main into integration-batch-7 2026-10-23 20:37:05 +00:00
Dylan M. Taylor 3729c3f664 security(raster,doc): count tile grids without walking them; share rebuilt tiles; index slice merges
Rect::tile_count counts a rect's tiles in u64; try_take_tiles uses it (no usize overflow on 32-bit). Surface::translated reuses one rebuilt tile for equal source-tile windows. auto_slices merges vertically adjacent runs through a hash index instead of searching the output.
2026-10-23 14:41:44 -04:00
MarekSpodBiedry 148e87944a Raise the Define Pattern size cap from 4000 to 8000 px per edge (#8820) 2026-10-23 17:31:29 +02:00
David Mann c16f899467 Merge origin/main into fix/6544-lut-format-roundtrip
# Conflicts:
#	crates/format/tests/it/roundtrip.rs
2026-10-23 00:06:48 +02:00
David Mann fff01d8918 Integration: stack #7335 (its merge onto main, conflicts resolved) 2026-10-22 23:40:11 +02:00
David MannandClaude Opus 5.5 6c7e920d5a Merge PR #7335: Gradient Overlay: add Method selector and retain editable source stops
- crates/ui-egui/src/layer_style.rs: values_of keeps the PR's method/smoothness keys and source-stop ends, and main's (#7072) unconditional From/To and geometry fields.
- crates/compose/src/cmyk_tests.rs (no textual conflict): add the PR's new `interpolation: None` field to main's CMYK test GradientOverlay literal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:39:14 +02:00
David MannandClaude Opus 5.5 cab3ef6191 Merge PR #5815: perf(doc): share the XMP packet, Duotone PSD data and unsupported adjustment bytes between snapshots
- crates/engine/src/tests.rs: kept both tests (main's restore_layer_selection test and the PR's history_states_share_large_document_payloads).
- crates/io/src/psd_import.rs: kept main's parsed Duotone inks (duotone_map) with the PR's Arc<[u8]> psd_raw; also main's unknown-adjustment-key import (#6575) now builds Arc<[u8]> raw.
- crates/io/tests/adjust_warnings.rs: moved to tests/it/adjust_warnings.rs on main; PR's .into() edits applied there, plus #6322's Color Lookup test (raw.as_slice().into()).
- docs/architecture.md: union; status line names both changes, both revision-history rows kept.
- Adapted main-new sites to the PR's types: psd_export.rs (Duotone write-back via as_deref/to_vec, Unsupported block compare by slice), inspect.rs and ui-egui unsupported_marker_tests.rs (raw .into()), io tests/it/psd_unknown_adjustment.rs (slice compare).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:38:56 +02:00
David MannandClaude Opus 5.5 95e0848332 Merge main into fix/4381-psd-frame-stroke
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:09:11 +02:00
David MannandClaude Opus 5.5 f5533e5d82 Integration: merge main into batch 6
Main merged #5940, #6472, #7838 and #7922 directly. Kept from the batch:
the snapshot/History-follow identity at the history limit (#5940,
c7ef3676a; main still had the 3-tuple HistorySpot) and the strokeless
shape at a 0 px width step (#6472, 962539607; main still added the
stroke). Main already carries the other two fix-ups (34c9d0ea4, dff8bfd0e).

Overlaps with main resolved:
- #5842 vs main's #5568: one path, apply_color, tool-gated as #5842
  has it, with #5568's float fill, kept fill opacity and lock skip;
  shape_fill_ui's duplicate foreground_changed/gesture dropped; #5568's
  tests run with a shape tool.
- #6230 vs main's #5180: one Type tool path click (path_target), no
  second type.attachToPath; vertical type does not start type on a path,
  as main has it; main's path_at/path_cursor and canvas arm dropped.
- #5452 moved the shape swatch to shape_paint_ui.rs: main's hex field
  (#6201) ported there.
- type_extra_cmds::with_text takes main's dpi form, pub(crate) for
  type_area_cmds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:20:26 +02:00
aiongg 934eb71d84 Integration: merge main
crates/engine/src/file_cmds.rs: batch keeps the save-point closure (takes the output path) and also copies the session's authorize hook into each scratch session, as main now does.
crates/raw/src/sensor.rs: both sides cap the memory of retained tiles; kept the existing retained_segment_bytes check (checked multiplication, same limit) and dropped main's duplicate inline loop.
2026-10-22 15:39:51 +00:00
aiongg 576ba0e02b Integration: track t12
# Conflicts:
#	contributors/people.toml
#	crates/compose/src/lib.rs
#	crates/compose/src/tests.rs
#	crates/engine/src/print_cmds.rs
#	crates/engine/src/print_cmds/tests.rs
#	docs/target-app-parity.md
2026-10-22 15:38:33 +00:00
aiongg ba120dc48e Integration: track t06
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/i18n/tr.tsv
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-22 15:38:19 +00:00
aiongg 26dbd7953b Merge PR #5834: Shapes: rounded corners on live triangles, polygons and stars (TOOL-214-12)
docs/scorecard.md: ours (regenerated at combine).
docs/ui-parity.md: kept main's header and added the PR's change entry once.
2026-10-22 15:37:29 +00:00
Dylan M. Taylor 58cd22d4ff security(doc): compute auto-slice coverage per band, not per cell
auto_slices tested every grid cell against the full slice-rectangle list
(covered(x, y) = taken.iter().any(...)). With slice edges forming the
grid, that is cubic in the slice count, so a crafted document's slice
arrangement could stall ordinary canvas drawing on the UI thread.

For each row band, mark covered x-cells in a single pass over the rects
that span the band, then scan the precomputed cells. This drops the inner
work from O(rects) per cell to O(1), while producing the same tiling.

Finding: 9fbea43b-3096-4cdc-b005-86d44a8fe57e (CWE-400)
2026-10-22 07:11:06 -04:00
David MannandClaude Opus 5.5 7a54fa9c77 Integration: merge main into batch 6
Conflicts, both sides kept: main's .act swatches next to the batch's .asl
styles (Open filters, preset extensions), main's Render smart filters next
to the batch's Distort ones (53 mapped), parity doc status lines.
docs/scorecard.md regenerated.

Follow-ups the merge needed: the Distort smart filter readers pass their
enum type to main's stricter enum_name (the type their writers use), and
the CMYK effect tests set main's new StrokeFx overprint and Bevel
gloss_anti_alias.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 12:09:25 +02:00
Yonghyuk Choi bea003acd8 Rebuild unrecorded Gradient Overlays from their end colours 2026-10-22 12:38:20 +09:00
Yonghyuk Choi dc029b93c5 Merge main into Gradient Overlay method branch 2026-10-22 12:38:20 +09:00
aiongg bf1000302d Merge PR #8367: Layers: per-layer XMP metadata (layer.xmpMetadata), stored as the reference app stores it
docs/file-format-parity.md: kept main's PSD row (45 smart filters) and added the PR's layer XMP packet phrase.
2026-10-22 02:23:45 +00:00
aiongg 8c92678a30 Merge PR #7815: PNG: correct sTER eye-order names without changing stored bytes 2026-10-22 02:22:43 +00:00
David Mann a8f91f54ad Merge PR #6230: feat(type): area type, text that wraps inside a closed path or shape
Integration: resolve with main for area type (#6230)

- Image Size with a new resolution (#7124, canvas_geom::set_resolution) now scales an area type's
  path with the text space like a box or a type path, so the same words wrap onto the same lines;
  test a_resolution_change_keeps_area_type_wrapping_where_it_was fails without it.
- The Paragraph panel's justify buttons (#7713) stay available on area type, which is paragraph
  text in Photoshop; test justify_buttons_work_on_area_type.
- Test files: main's tests plus #6230's; layout.rs imports joined; docs and scorecard regenerated.
2026-10-21 23:46:05 +02:00
Tom InglingandClaude Opus 5.5 052c1c3ab7 Layers: per-layer XMP metadata, as Photoshop's layer.xmpMetadata
A layer keeps its own XMP packet (Layer::xmp), where scripts and embedding
apps store data about the layer under their own namespaces.

- PSD/PSB/layered TIFF: read from and written to shmd > cust > layerXMP
  (UTF-16, NUL-terminated, after layerTime), laid out as Photoshop 2026
  writes it; an unchanged Photoshop block round-trips byte for byte.
- .pcraft: optional `xmp` on each layer.
- layer.xmpMetadata {layer?, rawData?}: read, set (one undo step) or
  remove with null; refuses malformed XML, DTDs, NUL and > 4 MiB.
- Duplicate Layer copies the packet; document.inspect reports hasXmp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 17:03:02 -04:00
VladimirPozdnyakov 0dfac5081f Roman Hanging Punctuation: trailing marks hang and don't steer the alignment
Photoshop's Paragraph panel checkbox, per paragraph style:

- doc: ParagraphStyle.hanging (serde default off; .pcraft keeps it).
- layout: a trailing run of closing marks (. , : ; ! ? and fullwidth
  twins, ellipsis) trims out of the aligned width on center/right lines,
  so a centered line ending in an ellipsis stays centered; a wrapped row
  of nothing but marks steps back onto the line above and hangs past the
  box edge (contiguous ranges, merged-row baseline, manual kerning; no
  RTL, vertical or tabs).
- PSD: /Hanging bool in the paragraph sheet, right after /LeadingType as
  the corpus files show; read defaults off.
- engine: type.setStyle takes "hanging":bool, params doc updated.
- UI: checkbox in the Paragraph section, a Paragraph Styles editor row,
  Character dock group grows one row; translations for the new label.
- tests: layout hang runs (trim, merge-back, plain no-op), PSD write and
  paragraph-sheet read round trips, engine setStyle, panel harness.
2026-10-21 22:45:10 +03:00
bazmarth 4573037531 Merge remote-tracking branch 'origin/main' into feat/polygon-corner-radius
# Conflicts:
#	docs/scorecard.md
#	docs/ui-parity.md
#	scorecard/tools.toml
2026-10-21 16:49:42 +00:00
aiongg 77557d75f1 Merge PR #8229: Integration batch: 8 PRs on main
# Conflicts:
#	crates/engine/src/layer_style.rs
#	crates/io/src/psd_export.rs
#	crates/io/src/smart_map.rs
#	docs/file-format-parity.md
2026-10-21 15:45:07 +00:00
David Mann 86811ddeee Merge PR #6002: Stroke: keep Overprint (overprint)
# Conflicts:
#	crates/engine/src/layer_style.rs
#	crates/ui-egui/src/layer_style.rs
#	docs/file-format-parity.md
2026-10-21 11:36:58 +02:00