ThinkGeo.com    |     Documentation    |     Premium Support

WPF 15.0.0-beta148: GPU highlight parity and live source changes

We moved highlights from classic overlays into the GPU scene. Ordinary highlights use InMemoryGeometrySource; richer styles use FeatureLayerTranslator. These are SDK/API findings from the application assemblies and supplied v15 samples.

Case Finding
Dashed lines, hatches, line offsets, thematic/nested styles, layer opacity Available through FeatureLayerTranslator; absence from our geometry-only implementation was an AGK limitation. We now use that path.
Images, glyphs, point masks/rotation/offsets and nested point styles AGK rasterizes the classic point style into its own GeoImage, doubles the canvas while ink touches an edge, then center-crops it and registers it through Images.Add(name, GeoImage). AGK does not pass sizeInPixels to the Style overload.
DirectionPointStyle Translator explicitly reports that direction points have no GPU translation.
TextStyle.Mask / RotationAngle Translator explicitly reports that masks are omitted and rotated labels become unrotated.
AreaStyle offsets with a wide/dashed outline FillPaint.FromAreaStyle carries X/Y offsets; the translator passes zero offsets when emitting a separate outline. No warning is emitted for this mismatch.
Gradient/texture brushes: exception versus warning An unsupported area fill is caught around FillPaint.FromAreaStyle and recorded as a translation warning. A positive-width GeoPen with the same brush makes Translate throw NotSupportedException: ClassicPaints.HasInk(pen) calls StrokeColorOf, then FillColorOf, without a guard, before LinePaint.FromGeoPen is reached. This affects line strokes and area outlines. AGK rejects non-solid pen brushes before calling the translator.
Hatch-brushed pens FillColorOf returns the hatch’s background color. Translation silently reduces the stroke to that color, losing the pattern without a warning; a transparent background can make the stroke disappear.
Scaled image PointStyle through AddFeatureLayer MapStyle.AddFeatureLayer registers the translator’s sprite styles through Images.Add(key, style), without a size. Its automatic canvas uses SymbolSize and outline width, ignoring ImageScale and offsets. Thus the normal translation/registration path clips scaled images when their rendered bounds exceed that canvas: a 64px image at ImageScale = 1.25 needs 80px before padding. This defect affects the SDK’s normal AddFeatureLayer path.
Add/remove translated sources with SetStyleAsync GpuMapController refuses rebinding when the new vector-source count differs from the opened count. AGK currently rebuilds the host/readers for these transitions.

AGK’s direct Style-overload registration in GpuHighlightScene is limited to plain
symbol points without images, masks, rotation or offsets, where that automatic size
is appropriate. Richer points use GpuHighlightSprite’s rasterized GeoImage. For the
translated path, AGK also normalizes the baked image style’s SymbolSize to the actual
sprite width before SDK registration.
Could ThinkGeo fix the area-outline offset mismatch and scaled-image clipping in
AddFeatureLayer, make unsupported pen brushes report warnings consistently with
area fills, and stop silently flattening hatch strokes? We also request parity for
direction points/text masks/rotation and live source-count changes. A richer dynamic
geometry styling API would also avoid tiled snapshots for frequently changing styled
highlights.

Minimal style cases to plug into the v15 MigrateAClassicLayer CPU/GPU comparison:


// Line feature: LINESTRING(0 0, 100 100)

var arrows = LineStyle.CreateSimpleLineStyle(GeoColors.Red, 3, true);
arrows.DirectionPointStyle = PointStyle.CreateSimpleCircleStyle(GeoColors.Blue, 8);

// Polygon feature: POLYGON((0 0, 100 0, 100 100, 0 100, 0 0))
// Expected: fill and 4px outline both move 20px right.
var offsetArea = AreaStyle.CreateSimpleAreaStyle(GeoColors.Red, GeoColors.Black, 4);
offsetArea.XOffsetInPixel = 20;

// A feature with a NAME column. Expected: mask and rotation match classic.
var label = new TextStyle("NAME", new GeoFont("Arial", 12), GeoBrushes.Black)

{
    Mask = AreaStyle.CreateSimpleAreaStyle(GeoColors.White, GeoColors.Black),
    RotationAngle = 30
};

// Translate each case independently and inspect translation.Warnings.
// The outline-offset mismatch currently has no warning.
var translation = FeatureLayerTranslator.Translate(layer, map.ZoomScales);

To reproduce scaled-image clipping through the SDK’s own registration path:


var image = new GeoImage(64, 64);
image.Clear(GeoColors.Blue);
var pointLayer = new InMemoryFeatureLayer();
pointLayer.InternalFeatures.Add(new Feature(0, 0));
pointLayer.ZoomLevelSet.ZoomLevel01.DefaultPointStyle =
    new PointStyle(image) { ImageScale = 1.25 };

pointLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
var translatedPoint = FeatureLayerTranslator.Translate(pointLayer, map.ZoomScales, "scaled-image");
var gpuStyle = new MapStyle().AddFeatureLayer(translatedPoint);

// Compare against a classic overlay: the full blue square should be 80px wide.

For the brush asymmetry, use the same gradient or texture brush first as an area’s
FillBrush with a solid outline, then as the Brush of a positive-width line or outline
GeoPen. Translate each independently: the fill returns a warning; the pen throws.
Repeat the pen case with a hatch brush whose foreground and background colors differ:
the stroke becomes the background color without a warning. These repros must call
ThinkGeo directly, bypassing AGK’s non-solid-pen guard.

For source rebinding, first display a MapStyle containing one translated source and
await Map.RefreshAsync so the host is open. Then call SetStyleAsync with a fresh
MapStyle containing the same translation/source plus a second translated highlight
source. The controller rejects the 1-to-2 source-count change. Expected: support
adding/removing that source while keeping the existing background cache and readers.

Separately, beta148’s opacity compositing group excludes Symbol layers. Overlapping
icons/text combined with geometry therefore still need a CPU/GPU visual comparison;
we are not claiming complete layer-opacity parity for those combinations.

Hi Julian,

Thank you for this list! All of it is in now in beta157.

What you found v15.0.0-beta157
DirectionPointStyle has no GPU translation Direction points translate
TextStyle.Mask omitted, rotated labels come out unrotated Masks translate and a label can be turned. Only a circle still loses its mask, because only a circle is a paint rather than a sprite - and it says so now
AreaStyle offsets move the fill and leave the outline behind, no warning The outline layer takes the same offsets the fill carries
Gradient/texture brush on a pen throws NotSupportedException, while the same brush on a fill is a warning A warning, like the fill. Asking whether a pen has ink no longer asks what colour it strokes in
Hatch-brushed pen silently flattened to the hatch background, invisible when that background is transparent Both say so. The one that would draw nothing is no longer emitted as a layer
AddFeatureLayer clips a scaled image - 64 px at ImageScale 1.25 needs 80 The sprite canvas is measured from what the style actually draws, not from SymbolSize and the pen. You no longer need to pass sizeInPixels yourself
SetStyleAsync refuses to rebind when the vector-source count changes A restyle may add or remove a source, and when it genuinely cannot rebind it says why instead of refusing silently

One more that came out of the same pass: a non-circle point style was being moved twice - its pixel offsets were drawn into the sprite and named on the layout. The layout no longer names them, nor the rotation, since a viewport-aligned icon does not read icon-rotate.

If anything on that list still looks wrong on beta157, tell us which row and we will take it from there.

Thanks,
Ben