Import simple Photoshop text as editable text (12.11b2)
This commit is contained in:
1 parent
a2241601ec
commit
008493b0a3
20 files changed
+2081
-17
No files matched your search
@@ -135,6 +135,7 @@ Platform adaptations, each forced by a missing Qt analog:
|
||||
- Crop, Vignette and text colour from Swift 1.2.5 (12.10c: `8b5a6af`, `5ece82b`, `0488cab`, `3317d02`, `4e5f4ec`). The crop ratios gain 3:4 and 9:16 (`cropRatio`, `CropControls`). Choosing the Crop tool with a selection that is not empty starts the frame at the outline's box (`boundingRect`, which leaves control points out as CoreGraphics' `boundingBoxOfPath` does; `controlPointRect` would not) rounded out to whole pixels (`integral`) and cut to the canvas, the canvas when that is no valid frame (an outline nudged wholly off it; an empty line between rows, which rounds to a row, stays out through `isEmpty`). Vignette opens round (`vignetteRoundness` 100, the default a number that is none falls back to). On an empty layer (no pixels, no adjustment, no folder: `canVignette`, the gate the Filter menu's entry reads) it begins from clear pixels the size of the layer's box, grows its grid to the canvas and frames the canvas (`FilterJob::canvas`, mapped into the image's pixels through the inverse mapping; top-left rows, so Swift's flip is gone), painting clear pixels too (`AdjustPixels.c`'s `fillsClear`, Swift's kernel as it stands); on a layer with pixels it frames them and recolours only them, as before. The commit lands on a layer still without pixels (`startedEmpty`), and the canvas draws an empty layer while a filter previews it. `canAdjustColors` and `canVignette` share `canAdjust(allowingEmpty)`; Swift's separate `adjustment == nil` and `isGroup` terms are the twin's argument and `canAdjust`'s own. The text colour previews while a picker is open: the Type bar's picker and the foreground picker opened over open text (`ColorPickerState::editedText`, the draft and its colour), through `previewTextColor`, which the Type bar calls on every change of the working colour (Swift's `onChange`), and only on the draft the picker opened for; Cancel puts the draft's colour back, OK keeps it and moves the foreground, and a Cancel with no text open leaves the next text's colour. Swift's `background: false` term in `previewTextColor` is dead (only the foreground picker records `editedText`) and left out. The tab strip's edge fade (`1fb6a3f`, `e8ed83b`) has no subject here: the twin's strip draws no fade, and it sits in a `QToolBar` under the desktop's own title bar, which drags the window itself. Tests: Swift's `textColorPickerPreviewsAndRestoresDraft` and `theForegroundPickerPreviewsOpenTextToo` in `TypeToolTests`, `thePickersColourPreviewsOnOpenText` in `TypeControlsTests`, `theFrameStartsAtTheSelection` and the ratios in `CropTests` and `CropControlsTests`, `VignetteCanvasTests` (the kernel's frame and fill, the gate and menu, an empty layer's commit and canvas preview, a layer with pixels), the defaults and the kernel's arguments in `FinishingFilterTests`.
|
||||
- Limits, ants and wheels from Swift 1.2.8–1.2.10 (12.11a: `7dadc88`, `bc865fb`, `8eba077`, `96edd5f`). `Document/DocumentLimits` twins Swift's enum as a namespace: `maxSide` (30,000), `maxSurfacePixels` (200 million, one canvas, export, filter target, layer, mask or generated surface), `documentPixelBudget()` (every layer, and apart every mask, of a document together: a sixteenth of physical memory in bytes, Swift's `physicalMemory / 16`, read once through `sysconf`, between one surface and 800 million) and the megapixels the messages quote; `budgetFor(memory)` is the budget's formula alone, so tests reach every memory size. Swift's `maxSideExtent` and `maxSurfaceExtent` (CGFloat copies) have no twin: C++ compares across types. Each literal Swift replaced is replaced as Swift's diff does it, the surface ceiling or the document budget, and the twin-only ones as their Swift counterparts: the RAW import's remaining pixels and the effects kernel's ceiling. Messages print the numbers (`maxSideText`, English grouping as the status bar). Tests fill budgets with layers that claim pixels they do not hold (`tests/BudgetFixtures.h`: `claimed`, `claiming`), so a boundary costs no memory whatever the machine; where an import or a save would still make the pixels, a boundary is read where it is judged before any pixel: the resizer judges a layer before it looks for its image (a missing image then says the budget passed; a canvas widened in one direction puts a layer on exactly the remainder), the store judges a file by its PNG header on load (`claimingPNG`, a header with a size its pixels never fill) and by the asset's size on save, before flattening it, as Swift reads a lazy `CGImage`'s size unrendered (the twin flattened first until 12.11a; a mask both invalid and oversized is now refused as too large where Swift says invalid). Save and load count through one `checkSize(size, isMask, budgets)`, images and masks apart. Every mask the resizer passes is drawn, so its exact edge is read under `AddressSpaceLimit`: the budget passes and the surface then finds no memory (`ExportError(render)`), one pixel more is refused as too large. The marching ants (`Rendering/TransformOverlay+Ants.cpp`, Swift's level of detail): an outline past 20,000 path elements, drawn below one screen pixel a document pixel, is filled into a gray mask (at the step at least, else as fine as about 40 megapixels allows, Swift's formula and its comment's bound alike; the path's own fill rule, where Swift's are winding and the twin's paths keep theirs), averaged down to its power-of-two step (`SmoothTransformation` for CoreGraphics' medium interpolation), any coverage counted, and traced along its pixels' edges through `MagicWand::outline`, in a `QtConcurrent` job whose watcher the overlay owns: a new source or step replaces the watcher, so an older result drops, and until a step lands the last one stands in, none at first. A trace whose tracer throws (`MagicWandError`) logs on `omaphoto.rendering` where Swift's `try?` is silent. The overlay asks the canvas to repaint the ants when a trace lands (`repaintAnts`); the canvas skips a tick while its last repaint is pending (`m_antsRepaintPending`, cleared as it paints: Swift's `needsDisplay` guard). The grading wheels' direction (`96edd5f`) came with 12.8b2: Qt's conical gradient already runs counterclockwise, and `theWheelsHueRunsCounterclockwise` holds it. Equivalent mutant: the landing's repaint (the ants timer repaints the same rect within a tick while a selection shows). Tests: `DocumentLimitsTests`, `MarchingAntsTests`, and every test that pinned the old ceilings, now read from `DocumentLimits`.
|
||||
- Photoshop Large Documents, background-only files and oversized layers (12.11b1, Swift 1.2.8–1.2.10: `b8e3cd1`, `e6bcfa1`, `1fcf854`). Swift's `PSDReader.swift` runs to 543 lines, so the twin splits it: `IO/PSD/PSDReader+Records.h/.cpp` hold the cursor, the raw records, their pixels and the cut (`PSDRecords`), `PSDReader.cpp` the header, the assembly and the merged image. A version-2 header is a Large Document (`Header::isPSB`): the layer section's and layer info's lengths, each channel's length and the additional-info keys Swift lists (`psbLargeKeys`) take eight bytes (`Cursor::length`, a length past `qint64` too large), and PackBits rows count in four bytes (`PSDChannelCoder::decode`'s `largeDocument`); another version is `unsupportedVersion`, whose message names no format now (Swift's words, OmaPhoto for Compositor). The import panel offers `*.psb`, a dropped picture prefers the PSB type right after PSD's (`ImageFileDrop`, Swift's order), and the MIME package declares `image/x-photoshop-large-image` (a `*.psb` glob and the version-2 signature as magic) for the desktop entry, as Swift's Info.plist declares the type. A file whose records do not fit the budget together (`fitsBudget`, the check `decodeChannels` makes, summed) has every layer and mask cut to the canvas (`cropToCanvas`, the bounds Swift's `crop` gives, the channels' source bounds kept) and is judged again, refused only if it still does not fit; a cut channel is decoded straight into its part (`PSDCrop`: raw rows copied in part, PackBits rows outside it skipped by their counts, one routine with the whole image as its crop for an uncut channel), and each cut layer's report says so (`PSDRecord::croppedToCanvas`, the note first in its layer's notes). A file with no layer records, Photoshop's background alone, reads as its merged image (`PSDReader::merged`, ImageIO's read for Swift): the header, its ICC profile (resource 1039) attached as Qt reads it and sRGB when it has none, the sides and the remaining budget judged from the 26-byte header before the file is read, its ICC profile (resource 1039) copied only for this read, since a layered read skips it as Swift's does, as Swift asks ImageIO's properties before it decodes (the layered canvas's one-surface ceiling does not apply, as those properties know none), the first three of its channels' planes from the image data section (every channel's row counts precede the rows), opaque; `ImageImporter::decode`'s `flattenedPhotoshop` sends it through the importer's own colour conversion and thumbnail, and when the Photoshop worker finds no records (`PhotoshopRead::layerless`) the session ends the reading first, as Swift does, then decodes the merged image through the ordinary decoder (`decode(url, flattened)`, which also takes the merged reader's `PSDError` and `ExportError`) and inserts the one layer without the conversion sheet. Known difference: ImageIO also reads 16-bit and non-RGB merged images; the twin's reader keeps the layered path's 8-bit RGB rule. The second `fitsBudget` refuses before any pixel is read (decoding the first layer could fail otherwise, and differently), and the crop's `max` cuts an inverted record to nothing, reported. A source's bounds and the cut stay 64-bit (`PSDCrop`, the coder's widths): once a cut makes a source past the side limit acceptable, its width can pass `int`, and Swift's `Int` holds it; PackBits rows decode straight into the crop's columns, so no row the width of the source is held, and the counts table is judged against the data before it is allocated. A PackBits row past the crop writes through a checked `std::span`, so a lost skip aborts. Tests: `PSBImportTests` (Swift's three; the merged image, its profile, budget and surface rule; the session's import at a point), `CropToCanvasImportTests` (Swift's five; the layers summed before any cut, a layer wholly before the left edge), the version and oversized cases in `PSDRoundTripTests` and `PSDReaderLayerTests`, the MIME package in `DesktopIntegrationTests`.
|
||||
- Photoshop text (12.11b2, Swift 1.2.10: `b8cce6e`, `fa2b342`): `IO/PSD/PSDText.h/.cpp` twin Swift's file, a namespace; Swift's 682 lines split into `PSDText+Engine` (the text engine's PostScript dictionary, a `std::variant` tree read through `walk`, `number`, `boolean`, `string` and `array`) and `PSDText+Descriptor` (Adobe's action descriptors, `Reader` and the item lookups; a rectangle is a normalised `QRectF`, and a side's key is chosen by presence before its type is read, Swift's `??`). A type layer (`TySh`, else `tySh`; 8,000,000 bytes at most) reads as Swift's: version 1, a finite matrix, version 50, the text descriptor; vertical text, shear and uneven scale refuse (the matrix's scale alone maps engine sizes to pixels, never the document's resolution); a warp other than none, faux bold or italic, runs whose twelve-field signature differs from the first, and justification other than left, right or centre each add Swift's note; the words come from `Txt ` (else `Txt`), cleaned of a leading BOM or NUL, trailing NULs and CR line ends, and only when that item is missing from the engine's `EngineDict/Editor/Text`; 1 to 100,000 UTF-16 units. A frame (`bounds`) wider and taller than the letters (`boundingBox`) by more than four makes box text, its corner mapped through the matrix; rectangles read standardised, as `CGRect` reads them. `render` draws through `EditorSession::textImage` and places the anchor (the frame's padded corner, or point text's first baseline at its alignment's x, `TextLines::baseline`, new here) on the document point through the layer transform, a transform that is not valid being `ProjectError(tooLarge)`. The reader parses type first, so a parsed type layer never becomes a live shape or a rasterized vector mask; the builder draws the text (any `std::runtime_error` keeps the pixels with Swift's rasterized note, Swift's `try?`), reports the parser's notes and a missing face, and makes a live text layer. A missing face is one fontconfig does not list by PostScript name (`TextLayout::availableFonts`), where Swift asks `NSFont`; its note says the text was drawn with a substitute font, since Qt draws fontconfig's match where macOS draws the system font. Engine numbers read as Swift's `Double(text)` (whole token, C locale, overflow infinite, through `strtod_l`); a name with a high byte reads empty and an identifier with one refuses, Swift's ASCII decoding; an odd count of UTF-16 bytes in an engine string reads empty, and a lone surrogate in either reader becomes U+FFFD (Qt's decoder keeps it; Swift's strings repair it), a valid pair kept. Fixes beyond Swift: nesting deeper than 512 in either reader refuses the block, where Swift's recursion overflows its stack, and a font index or justification past `Int64` reads as absent (left, with the note), where Swift's `Int()` traps. Left out as dead: `placement`'s finite-scale guard (a scale over 1e-6 from finite numbers), `parse`'s font-size guard (every path clamps it), the inner frame guard (the final `isValid` refuses the same style), `baseline`'s empty sample and no-glyph fallback (the words are never empty, and point text's image holds its first line), the frame's `width > 1` and `height > 1` (each side already passes the letters' by four) and `Double(text)`'s empty token (a number starts with a byte it keeps). Equivalent mutants: `runs.size() > 1` as `> 0` (it only keeps `begin() + 1` off an empty list), `rounded`'s `Int64` bound (past it the cast is undefined, and x86 yields a value no index or justification matches), the string's end after a lone backslash (the dictionary cannot close then) and the retry's `start > 0` (a retry at zero fails again). Tests: `PSDTextTests` (Swift's ten, the builder's notes, rendered pixels and exact transform, the matrix's two per cent, each signature field, a rendered frame's corner, the baseline in ink, frames placed and flipped, the style's fallbacks, a type layer with a shape), `PSDTextReaderTests` (the engine's forms, deep nesting, every descriptor type and cap, the list and reference counts, nesting and names, lone surrogates, colours in one, three and four channels, each channel as a second style, the sources' precedence, rounding, scaled leading, the `Txt` key, negative exponents, both versions and the limits at their edges); `PSDFixture+Text.h` twins `tySh`, and `PSDFixture::data` now takes several additional blocks.
|
||||
- Drops between projects: `ProjectWorkspace::draggedLayerSource` finds the tab whose document holds the dragged id (Swift reads the drag pasteboard; the twin reads the drag's `QMimeData`, which every drop target has), `canReceiveDrag(data, destination)` refuses only a layer's own tab, and `receiveProviders` copies several ids one after another, as Swift awaits each provider, so a two-row drop on the New button opens two tabs as Swift's does. `ContentView` takes such a drop over its whole area (`setWorkspace` hands it the workspace; `acceptsDrop` keeps Swift's guards: busy, the sheet, the importer, a rename; `levels` arrives with 9.x), shows the 3-point accent ring inside the canvas while targeted and hands the drop's document point on when it fell on the canvas. `ProjectTabButton` takes drops for its tab (accent fill and rim, "Add to <title>"), and `NewCanvasButton`, the toolbar's New canvas button with the action as its default, for a fresh tab ("Open in a new project tab"), both refusing an Alt drag, which duplicates in the panel; Swift's transient `NewTabDropSlot` in the strip has no twin, the New canvas button is `NewProjectDropTarget` already. Files and pictures arrived with 10.4.
|
||||
- `CanvasThumbnail` draws a layer or a mask inside the canvas's box at `backingScale` 2 (the panel's pixmaps carry that ratio); a mask's ground is the mean of its edge pixels, as Swift's `edgeTone`. `LayerIcons` draws the panel's SF Symbols as paths, in one palette colour, like `ToolIcons`. `BlendModePicker` is a `QComboBox` whose popup previews the highlighted mode through `previewBlendMode` for the layer it opened on (Qt calls `hidePopup` before `activated`, so the preview, the target and the highlight are cleared from the event loop, after a choice had its chance) and refuses a choice once another layer is active. `LayerAppearanceControls` is Swift's blend and opacity row: the slider's press and release are one opacity drag (the release is watched application-wide while the slider is down, so a popup opened mid-drag cannot keep the step open), the percent field applies once through its `FocusOut`, which Return and Escape cause (Swift's `releasesFocusOnCommit`), and steps with Up and Down (Swift's `isFinite` test and the steps' clamp are left out: `setLayerOpacity` refuses a value that is no finite number and clamps the rest), and another active layer makes the controls anew, as Swift's `.id` does.
|
||||
- `ProjectWorkspaceView` is the `QMainWindow`: the front tab's `ContentView` as its central widget, made anew when another tab comes to the front, as SwiftUI's `.id` rebuilds it, and disabled while the workspace manages; the window holds the shown tab (`std::shared_ptr`) until the old editor is deleted, as Swift's views hold their session, so a replaced or closed tab's session never ends under its editor (`ProjectWorkspace::tab` is public for this); a `QToolBar` with the New canvas action, the `ProjectTabStrip`, a spacer and the four zoom actions, since SwiftUI's `.toolbar` modifier lives in `ContentView` but the toolbar belongs to the window. Its actions follow the front session's `changed()`, and on that signal the window syncs every tab's button as well, because a busy front tab dims the other tabs' buttons, which their own sessions' signals do not reach; the strip syncs its buttons on the workspace's signal, each button on its own session's. The window's title is the front tab's, with Qt's `[*]` placeholder, so `setWindowModified` can show the mark that `isDocumentEdited` shows; `setWindowFilePath` carries `representedURL`. The editor's own minimum size is cleared inside the window, which owns the 800 × 520 minimum for the whole, toolbar included. Closing the window ignores the event and asks the workspace (`closeWindow`), which confirms tab by tab and then closes the window again with the property `closeConfirmed` set, since the workspace may not include `UI/` headers; a confirmed close is accepted and the mark cleared, so a window shown again asks again. `ProjectTabStrip` is a `QScrollArea` whose buttons are kept for the tabs that stay, moved for the tabs that moved, made for new tabs and deleted for tabs that went; its row is sized to its tabs by hand (a resizable scroll area would squeeze every title below Swift's 35 points) and resized again whenever a title changes, and the front tab scrolls into view at once; it and its viewport fill no background, so the toolbar shows through as Swift's mask lets it (a stylesheet would do the same but freezes the widgets' palette, so a theme switch would pass them by); the unsaved dot is a character before the title. Files named on the command line go to `ProjectWorkspace::receive`, as Finder's Open With reaches the delegate. Layer drops on tabs and the New canvas button are 4.7's; file and picture drops arrived with 10.4.
|
||||
|
||||
Reference in new issue
Block a user