The handler accumulated every rect from one EmfPlusFillRects
record into a single B2DPolyPolygon and emitted a single
PolyPolygonColorPrimitive2D, which drawinglayer fills under the
even-odd rule. With overlapping rects, even-odd cancels pairwise
overlaps - three south-east-shifted gray squares rendered as
filled-then-hollow-then-filled stripes (the triple-overlap centre
came back as filled because three crossings is again odd).
Per MS-EMFPLUS each rect in EmfPlusFillRects fills its interior
independently; there is no fill-rule choice. Emit one fill
primitive per rect inside the loop and keep the polypolygon path
only for EmfPlusDrawRects, where stroking is independent of fill
rule so the existing single-call shape stays correct.
Tests: testEmfPlusFillRectsOverlap with three 300x300 squares
shifted south-east by 100px each (extracted from the tdf#107034
sample EMF) asserts three separate polypolygoncolor primitives
rather than one combined.
Signed-off-by: Andras Timar <andras.timar@collabora.com>
Change-Id: I567e6a06d103684a723f513297684c66f700587a
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/2314
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 679c654a5b9a5f4a1cd787e5fa2d2380eb5574fb)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/205087
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
Replace the previous SvgRadialGradientPrimitive2D approximation of
PathGradientBrush with a bitmap-textured polypolygongraphic fill,
where the bitmap content is computed by a per-triangle Gouraud
shading of the brush boundary.
The old approximation positioned the gradient centre via
Map(brush->firstPoint), which typically landed far outside the
polygon's unit-coordinate range because the brush's path lives in
its own coordinate space; with SpreadMethod::Pad the gradient then
degenerated into a flat boundary-colour fill - the "orange square
instead of tiled gradient circles" of tdf#143031.
The bitmap is allocated with the brush bbox's aspect ratio
(longest side 256 px) and anchored to aBrushBounds.getMinX/MinY in
raw EMF coords, so when maMapTransform sends it to world space it
lines up with the fill polygon. wrapMode != WrapModeClamp routes
it through the existing texture-tiling path so larger fill polygons
repeat the bitmap.
Per-pixel content (Gouraud sweep):
- Partition the brush boundary into N triangles formed by (centre,
V_i, V_{i+1}) and Gouraud-shade each one with vertex colours
(centerColor, surround[i], surround[(i+1) % nSurround]). Inside a
triangle the pixel colour is the barycentric blend of the three
vertex colours; this reproduces multi-surround-colour brushes
(e.g. the libUEMF 5-pointed star with red/blue/red/blue/magenta
outer points and green inner indents on a black centre) and
collapses to a smooth radial gradient when all surround colours
are equal.
- The brush's BlendFactors curve (sampleFactor) remaps the
centre/edge parameter t before mixing centre and per-segment
edge colour. PresetColors / ColorBlend (sampleColor), when
present, override the centre/edge mix entirely with a sampled
stop colour. Both fall back to a linear centre-to-edge mix when
the brush carries no curve.
Rasterization is done per-triangle: Pass 1 walks each fan
triangle's pixel-space AABB (clipped to the bitmap) and writes
only the pixels that fall inside, so total work is O(sum of
triangle AABB areas) rather than O(bitmap area * triangle count).
Structurally this is what cairo's mesh-pattern rasterizer
(cairo-mesh-pattern-rasterizer.c) does for PDF type-7 shadings -
each patch paints into its own pixel range rather than every
pixel testing every patch. With N up to ~512 boundary segments
after Bezier flattening, that turns a 256x256-pixel bitmap from
a ~30M pixel-tests-triangle loop into ~50k AABB iterations
(per-pixel arithmetic is unchanged so the rendered colours match
bit-for-bit).
Boundary handling:
- BrushDataPath set: take the brush's path polygon and flatten
Bezier segments via adaptiveSubdivideByDistance with bound =
bbox/512 so the boundary closely follows the curve.
- BrushDataPath unset: use a new EMFPPath::GetRawPointsPolygon()
helper that builds a polygon directly from the boundary points.
GetPolygon() can't be used here because the trailing bytes of
boundary-only brush data don't encode valid path-point types and
produce a degenerate polygon.
Pixels outside every fan triangle (in the bitmap bbox but not in
the boundary) are handled per wrapMode in a second pass:
- Tile / TileFlipX / TileFlipY / TileFlipXY: left transparent so
adjacent tiles in the fill polygon stay visually separate. Pass
2 is skipped entirely for these wrap modes.
- Clamp: extended using the closest triangle's edge colour
(smallest-negative-margin barycentric weights), eliminating
sub-pixel transparent jaggies along smooth Bezier fill polygons
that extend slightly past the flattened boundary.
Tests: testEmfPlusBrushPathGradientWithBlendColors expectations
updated to assert the new polypolygongraphic-with-bitmap structure
(single bitmap copy fills the polygon when bitmap and polygon bbox
coincide). New testEmfPlusBrushPathGradientMultiSurroundColor
with a hand-extracted libUEMF 5-pointed-star EMF exercises the
boundary-only / multi-surround-colour path. testEmfPlusBrushPath-
GradientTiled covers the texture-tiling case where the brush
gradient is repeated across a larger fill polygon.
Signed-off-by: Andras Timar <andras.timar@collabora.com>
Change-Id: I92aa6d90ef6d2106f1d8d1cad97a3bb83594bd23
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/2209
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit fa76144b9cf130e232465c2480a5a7127bb282ed)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/205086
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
The fill-mode bit (0x2000) of EMF+ FillClosedCurve records was
logged but ignored, so a self-intersecting closed curve drawn with
WINDING semantics rendered the same as ALTERNATE - the centre of a
pentagram fill came out as a hole instead of being filled. Convert
the polypolygon at parse time the same way the pre-existing
EMR_SETPOLYFILLMODE path does (solveCrossovers +
stripNeutralPolygons + mergeToSinglePolyPolygon for single self-
intersecting polygons, createNonzeroConform for nested poly-
polygons) so the resulting even-odd fill of primitive2d primitives
matches the intended winding-fill visual.
Add a unit test with a minimal EMF+ FillClosedCurve drawing a
self-intersecting pentagram in winding mode.
Signed-off-by: Andras Timar <andras.timar@collabora.com>
Change-Id: Ic02048aae223527dca0f556f0171f117baf147c0
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/2313
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit fd018f6203f61aaf0c8c781c11a93578bf1eaf6b)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/205085
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
The tracking field was parsed but ignored, so DrawString always
used the font's natural character advances. Now build a DX-array
by multiplying each cumulative width by tracking, and pass it to
the text primitive. Also use the stretched width for alignment
math so center/far positioning stays correct.
testDrawStringTracking loads an EMF+ with tracking == 1.5 and checks
that the resulting textsimpleportion carries a stretched DX-array.
testDrawStringAlign turned out to use a file with tracking == 1.03 and
its expected x positions were based on the old buggy behavior (tracking
silently ignored). Update the centered/right-aligned expectations to
match the corrected text width.
Signed-off-by: Andras Timar <andras.timar@collabora.com>
Change-Id: Ica93f6ba11d2d951688ee18230e5993d97a34db6
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/1986
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 1a9cce32f7408aab06289295c63b3e24146472a0)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/204839
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Jenkins
Previously, nested EMF images were sized based on rclBounds, which
only encompasses the drawn objects. This caused incorrect scaling
and offsets when the drawing did not fill the entire logical canvas.
Changes:
- Switched from rclBounds to rclFrame for logical canvas dimensions.
- Implemented DPI calculation using szlDevice and szlMillimeters
from the EMF header.
- Correctly mapped rclFrame (0.01mm units) to image pixel
coordinates (x, y, width, height).
Change-Id: I32f6679bedb26e86b069abd6b4d270a385ca7e89
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201586
Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
Tested-by: Jenkins
And not truncate it to an integer value.
Add additional overload for attribute that takes an enumerated type, otherwise
the compiler will complain about ambiguous conversion because it cannot
choose between the attribute that takes a integral value and the one that
takes a double value.
Change-Id: I9eae22817ec3b26da7b9468d1c8b5e6f3e900538
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201193
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Tested-by: Jenkins
This commit fixes the incorrect positioning and scaling of Metafiles
rendered via EMF+ DrawImagePoints.
Key changes:
- Implemented a normalization matrix for Metafiles to properly map
source coordinates (SrcRect) to the destination.
- Updated EMFPImage to parse and store bounds from nested EMF headers,
ensuring correct dimensions (x, y, width, height) are available.
Change-Id: I07b4d99c334611a10e7faff42b76699f04fa1520
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201084
Tested-by: Jenkins
Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
This patch implements the missing TextureFill brush support for EMF+
records. Previously, texture fills were ignored, resulting in missing
backgrounds or shapes.
Change-Id: I61f80a9851f3929651f54b68d6d4d21e65720aa6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/200547
Tested-by: Jenkins
Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
The implementation of SetPageTransform was incorrectly overwriting mnMmX and mnMmY
with calculated pixel values.
These variables represent the Reference Device size from the EMF+ header and should
remain constant. Overwriting them causes mappingChanged() to calculate an incorrect
transformation matrix, as it relies on the original ratio between reference
millimeters and pixels. This led to incorrect scaling and cumulative errors
if multiple SetPageTransform records were present in the document.
This patch ensures that header constants are preserved and the Page Scale
is applied as a separate step in the transformation pipeline.
Change-Id: I7a957f57894cc079292e8f93c119dd0fdb456fb4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199380
Tested-by: Jenkins
Reviewed-by: Bartosz Kosiorek <gang65@poczta.onet.pl>
these were skipped in previous runs, since they contain some code behind #if
After inspecting these, it seems like the removals should not cause trouble.
This also shaves off some 140M input
Change-Id: Ie1f57174dd6fec3ba5b0b91eb0688acdc1a8143d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/197494
Tested-by: Jenkins
Reviewed-by: Gabor Kelemen <gabor.kelemen@collabora.com>
Found after running bin/find-unneeded-includes --headersfwd include/vcl/[d-g]*
This shaves off about 150M input
Change-Id: I2b15257451d4b392dca3e5d07f4a443ee653b04c
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196395
Tested-by: Jenkins
Reviewed-by: Gabor Kelemen <gabor.kelemen@collabora.com>
Found after running bin/find-unneeded-includes --headersfwd include/vcl/[A-Z]*
This shaves off about 100M input, not bad for such a small amount
of files checked
This time --headersfwd skips files that contain #if defs,
in hope of not breaking builds that are not checked by CI
(...except for include/vcl/BitmapTools.hxx - this was cleaned accidentally,
due to a bug in the bin/find-unneeded-includes script that is fixed
by the previous commit 930ff2b830
- but let's keep this anyway, it seems fine)
Change-Id: Ia8e49d94f6adb3cdbedbc40ffa56dbf846c9b6e4
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/196235
Reviewed-by: Gabor Kelemen <gabor.kelemen@collabora.com>
Tested-by: Jenkins
Use attributeDouble instead of OUString::number
Use O(U)StringBuffer
Do not use u"" literals where "" is better
Change-Id: I73f74777dfab77509c3926702c3a94b2a0b2e653
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194188
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
After recent commit 851fb75779
recheck these directories in include/ again
to test drive the new --headersfwd mode of bin/find-unneeded-includes
Change-Id: I0ede437c4f9958cd98210ed1760a5b98ea8bd1ad
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192377
Tested-by: Jenkins
Reviewed-by: Gabor Kelemen <gabor.kelemen@collabora.com>
Revert unrelated changes from:
commit 1bdf1e2d52
Author: Khaled Hosny <khaled@aliftype.com>
Date: Thu Sep 25 16:33:09 2025 +0300
tdf#168371: Disable ligatures in Impress/Draw with character spacing
I added a new parameter to TextSimplePortionPrimitive2D constructor, and
rTextFillColor that previously was using the default value had now to be
explicitly set, but I should have used COL_TRANSPARENT as that is the
default.
Change-Id: Ie51e8f3f4c68a9539eb3f6cc77fb1e6cf44f673f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/192779
Reviewed-by: Khaled Hosny <khaled@libreoffice.org>
Tested-by: Jenkins
This is follow up to commit:
commit b9f0caad5d
Author: Khaled Hosny <khaled@aliftype.com>
Date: Tue Aug 23 04:13:28 2022 +0200
tdf#66819: Disable ligatures with character spacing
Even though editengine was applying letter spacing, when drawing with
drawinglayer, letter spacing was not propagated which caused VCL to not
disable the ligature features.
Change-Id: Ia8bf2231478720262484a757e16fe5afec3cd28e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191512
Reviewed-by: Khaled Hosny <khaled@libreoffice.org>
Tested-by: Jenkins
to move the logic of constructing a new Bitmap from an existing Bitmap and an AlphaMask,
into vcl, where it can later be optimised
Change-Id: I2af1c6d1a35d968abdd07546448f7e211772e70b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190366
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
now that Bitmap supports transparency
Disable part of the redaction test. I have
verified that the visual result of auto-image redaction
is the same before and after this change, but we
seem to end up with more rectangles than before.
Probably something weird inside the EMF/WMF machinery.
(see the fixMetaFile function).
Change-Id: I85f308ae66ddf7592e41fde1ab9e49f9c882146e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190338
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
now that Bitmap supports transparency
Note that the SVM unit test changed because we no longer
store some bitmaps as an 8-bit palette color layer and an
8-bit alpha layer. When those pass through Bitmap, they
get converted to a 32-bit bitmap.
Change-Id: Icbb30e291a7ed5c53319876637ae25e88a97d90d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190330
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Tested-by: Jenkins