Tcl/Tk 9 upgrade and additions

Rbc 0.5.0 targets Tcl/Tk 9 on Windows and Linux. This page summarizes the visible additions first, followed by the API, numerical and resource-management changes behind them. Tcl/Tk 8.x compatibility is not a goal of this fork. Build instructions and dependency details are in the documentation overview.

User-visible additions

This fork adds plotting, interaction and numerical features alongside the Tcl/Tk 9 API migration.

MSVC builds on Windows

The win/makefile.vc build supports MSVC and Nmake with Tcl/Tk 9, including optional Cairo via CAIRO=1 and CAIRODIR. It provides build, test, install and shell targets independently of the MSYS2 build. Matching MSVC-built Tcl/Tk source trees and libraries are required; Cairo runtime DLLs and their dependencies must be available when loading RBC. See win/README.md for commands.

Optional Cairo rendering

Configure with --enable-cairo to build the antialiased renderer. Cairo-enabled builds default to -renderer cairo; builds without Cairo default to -renderer native. Applications can select either available renderer explicitly or through Tk’s option database.

::rbc::graph .g -renderer cairo -antialias default
.g element create signal -data {0 0 1 2 2 1} -symbol none
.g configure -renderer native

Cairo covers line and strip traces, symbols, error bars, bars, grid and axis lines, ticks, legend symbols, and supported marker geometry, bitmaps and photos. Text glyphs remain native, as do XOR crosshairs, window markers, Tk relief drawing and unsupported image types. Snapshots use the selected renderer; PostScript and Windows metafile output retain their existing exporters.

Cairo improves edge quality but is not a universal speed improvement. Native rendering remains useful for dense geometry; image-heavy workloads can favour Cairo. Windows marker passes share a drawing target, and compatible marker strokes reuse their Cairo context while retaining separate drawing operations.

The automated test runner defaults to native through the option database. Dedicated renderer tests select Cairo explicitly. Manual tests and demos use the compiled default unless they request another renderer. Benchmark scripts have their own explicit -renderer and -antialias settings and CSV comparison tools.

Display decimation and streaming updates

Line and strip elements support -decimate none|auto; none remains the default. Eligible monotonic waveforms retain first/minimum/maximum/last representatives in each physical data-X pixel, preserving narrow extrema rather than averaging them away. Source data and PostScript resolution remain unchanged.

With auto, the activation threshold is 4 source points per plot pixel for Cairo and 8 for native. Renderer changes remap the data. The persistent cache supports axis changes, ranged Y updates and monotonic tail appends, while unsupported data combinations use the full or fallback mapping paths.

Graph interaction and toolbar

graphtoolbar provides zooming, panning, closest-point annotations, legend controls and configurable pointer appearance. Windows touchpad scrolling is supported alongside mouse-wheel zoom. Consecutive axis-scroll actions retain their selected axis when rescaling moves it away from the pointer.

Crosshair annotations refresh beneath a stationary pointer when streaming data or axis mapping changes. Current-coordinate annotations stay beside the pointer; closest-point annotations repeat the search. Annotation boxes are recalculated after zooming, including with inverted axes.

Graph widgets emit <<RbcGraphChanged>> after relevant display changes. The toolbar combines refresh requests into an idle callback and respects temporary suspension during zooming, panning and menus. Windows crosshair motion was corrected to avoid flicker. Symbols crossing the plot boundary are clipped to the visible area instead of disappearing while still partially visible.

Graph widgets also provide <<RbcAxisChanged>> for successful axis configuration or changed effective limits, and <<RbcAxisLimitsChanged>> for numeric limit changes alone. Both queue one list of affected axis names in %d after display, including initial limits. Applications can use these events to synchronize plots or update controls without reacting to unrelated redraws. The broader event includes log/linear switching, tick configuration and styling; synchronization handlers must account for queued feedback.

Custom closest-point annotations

The toolbar’s -closestcommand accepts a Tcl command prefix receiving element x y info and returning annotation text. Configuring it adds Custom to the available formats; -coordclosestmark custom selects it. Empty results suppress the annotation. Ordinary coordinates follow the element’s mapped axes; Polar coordinates are Cartesian components. Smith charts supply normalized admittance for an admittance grid, or normalized impedance for impedance and combined grids. The information dictionary retains the closest-search fields, including the source index.

Built-in closest annotations display the element’s nonempty -label, falling back to its name. Toolbar control mode now uses a Crosshairs menu button instead of two comboboxes, with the same mode and format choices as context control mode. Polar formats include polar (radians) and polardegrees (degrees). Format changes refresh the annotation without requiring another pointer movement.

Complex vectors

The vector subsystem now supports both real and complex numeric vectors. Complex vectors are created explicitly with -type complex, and Tcl-level complex values use the form {real imag}:

::rbc::vector create z -type complex
z set {{1 2} {3 -4} {5 0}}

Complex vectors are supported by most vector instance operations and by the vector expression engine, including arithmetic, complex exponentiation, trigonometric and logarithmic functions, projections such as real, imag, abs, and arg, and complex statistical reductions.

Operations that require an ordering of values remain real-only. In particular, sort and normalize are not supported for complex vectors.

Traditional graph, barchart, and stripchart X/Y element data remain real-valued. Polar elements can attach a complex vector directly through -cdata, and the spline subsystem provides complex-vector parametric natural and quadratic interpolation.

Applications can still explicitly derive real projections such as real, imag, abs, or arg when a traditional Cartesian graph or scalar spline is required.

The public C API was extended with typed vector access, including Rbc_VectorType, Rbc_Complex, Rbc_CreateVectorWithType(), Rbc_VectorComplexData(), Rbc_ResetComplexVector(), and Rbc_VectorChanged(). The original real-vector API remains available for compatibility.

Polar and Smith charts

This fork adds the polar graph widget for plotting Cartesian complex-plane data with either a conventional Polar grid or a Smith-chart representation.

Polar elements accept real lists or vectors through -xdata and -ydata, paired lists through -data, or first-class complex vectors through -cdata. With the default -datacoordinates cartesian, X and Y are real and imaginary components. With -datacoordinates polar, X supplies angles in radians and Y supplies radii; conversion to Cartesian coordinates occurs internally. Source lists and vectors remain unchanged. Polar input coordinates cannot be combined with -cdata or error-bar data options.

Complex data can be interpreted as:

  • reflection coefficient with -cdataformat gamma;

  • physical impedance with -cdataformat impedance;

  • physical admittance with -cdataformat admittance.

The -z0 option specifies the positive reference impedance used for impedance/admittance conversion.

Smith charts support impedance, admittance, or combined contour grids, configurable major/minor normalized contour values, and Tcl callbacks for formatting real and imaginary contour labels.

Polar grids provide independently configurable angular major/minor spokes, angular label callbacks, radial label formatting through the mapped X axis, and configurable label anchoring.

Polar and Smith grid geometry follows the axes selected by grid -mapx/-mapy.

Unless -aspect is explicitly configured, Polar widgets automatically maintain equal physical scale for X and Y data units. This keeps circles circular even when the mapped numerical X/Y ranges have different spans.

Polar grid rendering remains meaningful when the origin is outside the current viewport. Radial circles which intersect the viewport are retained, angular spokes are clipped to the visible Cartesian area, and angular labels move from the normal circular layout to the visible ends of their spokes when no complete centered circle is available.

Radial labels remain tied to the positive zero-degree ray and are displayed whenever their corresponding {radius,0} point is visible, even when the Polar origin itself is outside the viewport.

Polar autoscaling also supplies a neutral fallback range when a grid-mapped axis has no contributing visible element data, allowing the Polar grid to remain usable when the widget is empty or all plotted elements are hidden.

Smith reactive labels likewise adapt to zoomed views. When the complete unit circle is no longer visible, major reactance and susceptance labels are moved to the first visible boundary of their corresponding contour instead of disappearing with their normal unit-circle label position.

Natural/cubic and quadratic smoothing on Polar elements is parametric, allowing vertical paths, non-monotonic-X paths and loops. Catmull-Rom smoothing remains parametric as well.

Missing, non-finite, logarithmically invalid, or singular impedance/admittance points split a trace into continuous runs. Smoothing is performed independently on those runs and never bridges a gap.

element closest on a Polar widget additionally reports radius and angle. The angle field now uses radians in [0, 2*pi), including for Cartesian and complex-vector input; this deliberately replaces the previous degree result. The origin has angle zero, and returned X/Y coordinates remain Cartesian. Polar grid angular labels and their formatting callback continue to use degrees. In Smith representation, closest results also report Gamma and normalized and physical impedance and admittance quantities.

Polar/Smith grid rendering, labels, custom formatter callbacks and smoothed element traces are supported by PostScript output as well as on-screen drawing.

Area closure and transparency

Line and Polar elements support -areaclose baseline|chord|origin. The default baseline retains the bottom-of-plot closure; chord joins the endpoints directly, and origin closes through Cartesian {0 0}. These choices control the fill polygon without adding segments to the visible trace. They apply to solid, stippled and tiled areas, and the same closure geometry is used in PostScript.

-areaopacity controls solid fills (-areapattern solid) in Cairo and SVG, from 0.0 (invisible) to 1.0 (opaque, the default). It does not change lines, symbols, stipples or image tiles. Photo tiles retain their own per-pixel alpha, including partial transparency.

Native rendering accepts but ignores -areaopacity, and native image tiles use binary transparency masks. PostScript also ignores the option and retains opaque solid fills; standard PostScript cannot express general alpha blending. These area options are available on line and Polar elements, not bars or strip elements.

Plot-only element hiding

Line, stripchart, and bar elements now support the -hideplot option.

Unlike -hide, which removes an element from the graph display list, -hideplot yes suppresses plotting while leaving the element in the display list. Its legend entry therefore remains available, making it possible for applications to hide and restore plotted data without rebuilding or reordering the legend.

A plot-hidden element is also excluded from automatic axis limits, closest-element searches, element hit testing, and PostScript element output. Plot-hidden bars are additionally excluded from bar grouping and stacking calculations.

-hide and -hideplot are independent, so changing the normal visibility of an element does not alter its -hideplot state.

Arrowheads for line markers

Line markers now support arrowheads through the -arrow and -arrowshape options.

-arrow accepts none, first, last, or both, allowing an arrowhead to be drawn at either or both ends of a line marker. -arrowshape {a b c} controls the arrowhead geometry using screen distances.

.g marker create line -name direction -coords {0 0 5 4 10 2} -outline red -linewidth 2 -arrow last

.g marker configure direction -arrow both -arrowshape {8 10 3}

Arrowheads use the line marker’s -outline color, and the line shaft is shortened to the arrowhead neck so that it does not extend through the filled arrowhead.

The drawing implementation was also corrected for X11/Linux. Arrowheads are concave polygons, so they are now passed to the native polygon renderer with the correct non-convex geometry and without the duplicate closing vertex. This keeps arrow rendering consistent across supported platforms.

Bar geometry from element closest

The element closest operation now provides additional geometry information when the selected element is a bar.

In addition to the normal name, index, x, y, and dist entries, the result array contains:

  • left

  • top

  • right

  • bottom

These values are the physical widget coordinates of the final mapped bar rectangle that is actually drawn. They therefore reflect the current bar mode: stacked bars return the selected stacked segment, while aligned and overlapping bars return the adjusted rectangle belonging to the selected element.

The values remain physical widget coordinates when -invertxy is enabled: left and right always describe physical X coordinates, while top and bottom always describe physical Y coordinates.

This makes element closest directly usable for bar highlighting, overlays, annotations, and interactive interfaces without requiring applications to reconstruct bar geometry themselves.

Custom element value labels

Value labels accept -valueoffset {dx dy} on elements and pens. Signed integer pixel offsets move the label anchor right/down, independently of axis inversion and text rotation. The default {0 0} preserves existing placement in both renderers and PostScript.

Elements and named pens now accept -valuecommand, a Tcl command prefix receiving the graph pathname, element name, source index, X and Y. Its result replaces the -showvalues label on screen and in PostScript. Empty results hide individual labels; clearing the option restores -valueformat.

Implementation modernization

The migration replaces obsolete Tcl/Tk interfaces, widens collection sizes and indexes, and improves configuration rollback, numerical boundaries and resource lifetime management.

Tcl 9 command and object API

  • Converted command implementations to the Tcl 9 object-based command interface.

  • Updated command callbacks to use Tcl_Size argument counts where appropriate.

  • Migrated command creation to Tcl_CreateObjCommand2() for Tcl 9 large-argument support.

  • Replaced remaining old string-based command and result handling with Tcl_Obj-based APIs.

  • Removed obsolete command parsing infrastructure inherited from the original BLT/Rbc implementation.

  • Reworked operation dispatch tables and argument validation around object-based parsing.

  • Improved const-correctness throughout Tcl-facing APIs and removed casts that previously discarded const.

  • Removed several local copies or emulations of Tcl internal parsing structures and functions.

64-bit and Tcl_Size support

A large part of the original code assumed that collection sizes, vector lengths and indexes fit in an int. These assumptions were systematically removed.

  • Converted vector lengths, indexes and capacities to Tcl_Size.

  • Converted spline point counts and interpolation indexes to Tcl_Size.

  • Converted graph element point indexes and data-index mappings to Tcl_Size.

  • Converted error-bar index mappings and element-to-data mappings to Tcl_Size.

  • Updated sorting, searching, closest-point and line-reduction code for large indexes.

  • Updated chain/list traversal code where sizes or indexes could exceed the old 32-bit assumptions.

  • Fixed vector binary-reading and vector mathematical operations for Tcl 9 size semantics.

  • Added overflow checks before converting between Tcl_Size, size_t, integers and allocation sizes.

  • Added allocation-overflow guards for dynamically sized arrays throughout graph, spline and vector code.

This removes a large class of silent truncation and overflow problems that were possible with large datasets.

Modern Tk option handling

The graph subsystem previously relied heavily on the legacy Tk configuration API. Most of it has been replaced with the modern Tk option-table interface.

The following subsystems were migrated to Tk_OptionSpec/Tk_OptionTable based configuration:

  • graph widgets

  • axes

  • legends

  • grid

  • crosshairs

  • pens

  • markers

  • line elements

  • stripchart elements

  • bar elements

  • PostScript options

Legacy configuration tables and their associated processing functions were removed after the new implementations were activated.

Custom option types were added or rewritten for complex Rbc values such as:

  • vectors

  • axes

  • pens

  • tags

  • element styles

  • states

  • coordinates

  • padding and margins

  • format strings

  • graph-specific enumerations

Configuration processing was also made transactional where needed. Invalid option changes now preserve the previous valid object state instead of leaving partially modified graph objects.

The EPS canvas item remains on Tk_ConfigSpec/Tk_ConfigureWidget() because the public Tk 9 Tk_ItemType canvas-item interface still exposes the legacy configuration mechanism.

Component option-database lookup

Graph components such as elements, markers, pens, axes, the legend, grid, crosshairs, and PostScript settings are not ordinary Tk child widgets, but they participate in Tk’s option database using component resource names and classes.

The previous implementation created and destroyed a temporary Tk child window whenever a pseudo-component was initialized so that Tk_InitOptions() could perform the appropriate name/class lookup. With large numbers of objects, particularly markers or elements, this made component creation dominated by Tk window-management overhead.

Each graph now owns a single anonymous Tk option-proxy window which is reused for pseudo-component option lookup. Before initializing a component, the proxy is temporarily given the component’s resource name and class and is then passed to Tk’s normal option-table machinery.

This preserves the existing option-database hierarchy and precedence, including both class-wide resources such as:

option add *Graph.TextMarker.Foreground darkgreen

and resources for particular named components such as:

option add *Graph.annotation.foreground purple

without constructing a real Tk child for every component.

The proxy is anonymous and is not entered into Tk’s pathname hierarchy. It exists only for the lifetime of its graph and is destroyed with the graph. Explicit component options continue to override option-database defaults normally.

Removing the temporary-window creation path substantially reduces the cost of creating large numbers of graph objects while retaining the established option-database behavior.

Graph object and resource lifetime

Configuration modernization exposed a number of ownership and rollback problems that were corrected at the same time.

  • Reworked creation, configuration and destruction paths for graph components.

  • Improved rollback after failed configurations.

  • Corrected ownership of dynamically allocated option values.

  • Fixed temporary Tk image creation and destruction.

  • Corrected resource cleanup for legends, markers, pens and elements.

  • Hardened vector mathematical operations against ownership and aliasing problems.

  • Removed dead initialization and cleanup paths inherited from the old configuration system.

  • Removed substantial amounts of obsolete and unreachable compatibility code.

Axis and coordinate-system modernization

The axis implementation received extensive numerical and behavioral cleanup.

  • Hardened coordinate mapping against integer overflow and extreme coordinate ranges.

  • Widened intermediate geometry calculations before converting to Tk/X11 integer coordinates.

  • Added safe handling for rotated-axis dimensions.

  • Added validation for non-finite configuration values.

  • Hardened generated tick calculations against invalid or degenerate ranges.

  • Added guards against ill-defined tick spacing and numerical overflow.

  • Corrected axis scrolling and -scrollcommand behavior.

  • Corrected tick-label command callback handling.

  • Improved option validation and rollback for axis configuration.

  • Hardened user-controlled formatting options and removed unsafe formatting operations.

Logarithmic axes were also substantially corrected:

  • Logarithmic axes now use well-defined positive-domain semantics.

  • Invalid/non-positive points are excluded instead of being mapped through invalid logarithms.

  • Line elements are split at invalid log-domain points instead of drawing across discontinuities.

  • Bar charts correctly handle logarithmic axis mapping.

  • Stacked bars were corrected for logarithmic scales.

  • Extreme logarithmic ranges are handled more safely.

  • Smoothing no longer bridges invalid log-domain gaps.

Line, bar and element processing

The internal representation and processing of graph elements were extensively updated.

  • Modernized element option handling.

  • Reworked vector association using object-based APIs.

  • Updated element state, tag, pen, style and axis handling.

  • Fixed error-bar mapping and data-index tracking.

  • Corrected closest-point and closest-segment calculations.

  • Corrected element binding/tag behavior.

  • Fixed line-reduction routines and their index handling.

  • Hardened construction of mapped point arrays and drawing traces.

  • Replaced internal uses of narrow X11 rectangle structures where larger internal coordinates were required.

  • Added explicit handling of NaN and infinite values in plotted data.

  • Corrected bar handling of non-finite data.

  • Corrected several element formatting and -valueformat paths.

  • Fixed step-generation behavior.

  • Corrected line -trace behavior.

  • Added optional screen-density decimation for large line and strip elements through -decimate none|auto.

  • Automatic line and strip decimation preserves the first, minimum, maximum, and last source representatives associated with each physical data-X pixel rather than averaging dense waveform data.

  • Added density-based selection so sparse data continues through the normal mapping path while dense eligible traces can avoid mapping and drawing millions of redundant screen points.

  • Added a persistent data-domain decimation cache for finite, monotonic-X line and strip data. The cache survives ordinary axis remapping and supports increasing, decreasing, and equal X coordinates as well as -invertxy and descending axes.

  • Added conservative fallback paths for discontinuities, logarithmically invalid data, non-monotonic paths, symbols, value labels, error data, multiple styles, and other combinations that require full source geometry.

  • Added incremental maintenance of persistent line and strip decimation summaries for ranged Y-vector updates and monotonic tail growth.

  • Vector append and index ++end now provide exact ranged tail notifications, allowing persistent display caches and cached real-vector extrema to be extended without rescanning the unchanged source prefix.

  • Pure tail growth can preserve the decimation cache even when vector capacity growth relocates its backing storage. Shrinkage, changes inside the cached X prefix, non-monotonic growth, arbitrary storage replacement, and unknown/full changes retain conservative invalidation semantics.

  • Accelerated exact ELEMENT closest -interpolate no searches for decimated monotonic-X lines while preserving exact source-point results.

  • Screen-density decimation affects window rendering only. PostScript output continues to use full source resolution.

Spline subsystem

The spline subsystem was subsequently extended with direct complex-vector parametric interpolation.

  • Added spline parametric natural source result samples.

  • Added spline parametric quadratic source result samples.

  • Parametric source and result vectors are first-class complex Rbc vectors.

  • Parametric splines use cumulative two-dimensional chord length and do not require either Cartesian coordinate to be monotonic.

  • Vertical paths, reversing paths and loop-like trajectories are supported.

  • Consecutive duplicate source points are rejected because they create a zero-length parameter interval.

  • Parametric generation is transactional and supports source/result aliasing.

  • The original scalar real-vector spline interface remains unchanged.

Vector subsystem

Vector handling was updated substantially beyond the minimum required for compilation with Tcl 9.

  • Added Tcl 9 sized vector indexing.

  • Converted sorting indexes to Tcl_Size.

  • Removed global state from vector sorting operations.

  • Updated vector arithmetic and mathematical operations.

  • Corrected vector duplication and overwrite behavior.

  • Corrected binary-read operations.

  • Hardened allocation and capacity calculations.

  • Improved parsing and const-correctness.

  • Removed legacy parser dependencies.

  • Improved ownership of temporary data used by mathematical operations.

  • Added stricter handling of invalid and non-finite input.

  • Added first-class complex vectors, complex expression arithmetic and mathematical functions, typed public C API access, and precise ranged in-place update notification while retaining compatibility with the existing real-vector interface.

  • Added cached real-vector range maintenance so repeated clients do not rescan unchanged vectors merely to obtain minimum and maximum values.

  • Added ranged vector-change notifications. Mapped Tcl-array writes and ordinary vector index writes report the affected source interval when an existing real-vector range is modified in place.

  • Added Rbc_VectorChangedRange(Rbc_Vector *, Tcl_Size first, Tcl_Size last) to the public C/stubs API so C producers can report precise in-place source modifications without changing vector storage or ownership.

  • Ranged real-vector updates maintain cached extrema incrementally when possible and conservatively fall back to a complete range scan when a modified interval invalidates an existing global extremum.

  • Retained Rbc_VectorChanged() as the full/unknown-range notification interface for callers that cannot provide a precise changed interval.

Geometry, text and drawing

Many drawing routines inherited assumptions from old X11 APIs where coordinates and dimensions were commonly stored in narrow integer types.

The modernization:

  • widened internal geometry calculations;

  • added checked conversion at native drawing boundaries;

  • hardened rotated text and bounding-box calculations;

  • updated text-layout processing;

  • hardened shadows, halos and graph-layout coordinate arithmetic;

  • corrected clipping and projection calculations;

  • updated polygon, rectangle and region processing;

  • added guards around fixed-size polygon and symbol allocations;

  • corrected point-in-region and projection helpers;

  • fixed drawing-boundary issues discovered on both Windows and Linux.

The intent is to keep high-precision or wide internal calculations for as long as possible and narrow them only when calling APIs that inherently require integer coordinates.

Image and bitmap handling

The image-processing code was significantly updated for current Tk behavior.

  • Updated photo-image access and processing.

  • Corrected temporary image lifetime management.

  • Updated bitmap rotation and scaling code.

  • Unified bitmap-region operations across platforms.

  • Corrected Unix image handling.

  • Corrected Windows bitmap representation and mask generation.

  • Hardened bitmap allocation and size calculations.

  • Isolated native bitmap-handle access behind Rbc Windows helper functions.

Named-font changes

Graph widgets now register Tk’s world-change callback. Reconfiguring a named font refreshes cached text-marker geometry, title and axis layout, legends and value-label drawing resources, including external legends and named/active pens. Redraws are scheduled through the normal idle mechanism. Explicit pixel sizes and configured axis limits are preserved. See Named fonts and display scaling.

SVG output

Graph widgets now provide svg cget, svg configure, and svg output, with independent width, height and decoration settings. SVG export uses vector geometry and editable text and does not require Cairo. Solid area opacity and photo-marker alpha are preserved. Photos are embedded as PNG; bitmap markers and symbols use vector shapes. Stipple fills use vector patterns, and photo area tiles embed repeating PNG images with alpha. Non-photo Tk images are rasterized with recovered transparency; mapped window markers embed native pixel snapshots. Capture failures report an error before opening a file. See ::rbc::SVG for commands, options and capture limitations.

PostScript output

The PostScript subsystem was modernized and hardened alongside the graph code.

PostScript drawing now passes through the common renderer interface: strokes, fills, symbols, text, images, bitmap masks, window snapshots, relief borders, plot backgrounds and clipping. This includes normal/active elements, markers, legends, axis labels, and polar/Smith labels. The existing PostScript backend, prolog, font/color mapping and public command are retained; export does not require Cairo. Page setup, orientation, EPS metadata and file output remain in the PostScript subsystem. Screen text rendering is unchanged.

This is an internal refactoring, not a new export format. PostScript still ignores area opacity; image-tiled line areas export only their configured background. Existing bitmap masks, drawing order and window-snapshot fallback behavior are preserved.

  • Updated PostScript option handling.

  • Updated image generation and bitmap handling.

  • Corrected PostScript writer behavior.

  • Replaced unsafe formatting paths.

  • Hardened formatting and buffer handling.

  • Updated coordinate and size handling for the modernized graph structures.

Windows drawing backend

The Windows drawing implementation received a substantial cleanup.

Obsolete compatibility code and dead drawing implementations were removed, and the remaining native drawing code was reorganized behind an Rbc-owned Windows abstraction.

Rbc code outside the Windows backend no longer accesses Tk’s private Windows drawable structures directly.

Operations such as:

  • acquiring and releasing a drawable HDC;

  • extracting an HBITMAP from a Tk pixmap;

  • wrapping an HBITMAP as a pixmap;

  • wrapping a native HDC;

  • mapping an HWND to the required drawable representation;

  • setting Windows GDI raster operations

are now isolated behind Rbc_Win* helper functions.

This reduced the former widespread dependency on:

  • TkWinDrawable

  • TkWinBitmap

  • TkWinDC

  • TkWinDCState

  • TkWinGetDrawableDC()

  • TkWinReleaseDrawableDC()

  • TWD_*

to a single Windows implementation unit, rbcWinDraw.c.

Windows rendering performance

The Windows graph renderer was also optimized to reduce repeated GDI setup for large drawing workloads.

Several older drawing paths performed native device-context, pen, brush, or bitmap setup separately for every individual primitive. This was particularly expensive for large scatter plots, bar charts, error bars, and collections of graph markers.

The Windows backend now batches or shares native drawing state where the original drawing semantics permit it:

  • independent line and error-bar segments are grouped into PolyPolyline() calls;

  • polygon-based symbols use batched native polygon and polyline drawing;

  • square and circle symbol rendering uses dedicated Windows fast paths;

  • raised bars share an acquired drawable HDC and precomputed 3-D border colors instead of repeatedly entering Tk’s per-rectangle drawing path;

  • opaque stippled bars use a reusable monochrome pattern brush and PatBlt() instead of creating temporary bitmap state for every rectangle;

  • compatible consecutive line-marker and polygon-outline markers are combined into segment batches while preserving marker display-list order;

  • solid fill-only polygon markers share their destination HDC and brush across a compatible run;

  • compatible bitmap markers share both source and destination device-context setup;

  • compatible text-marker runs reuse an acquired destination HDC while continuing to use Tk’s font renderer.

The fast paths are deliberately conservative. XOR drawing, dashed or otherwise incompatible line styles, marker arrowheads, masked or rotated bitmap cases, mixed polygon fill/outline cases, and other operations whose semantics depend on individual drawing order continue through the normal drawing paths.

Batching never reorders visible graph objects. A pending batch is flushed whenever an incompatible object or another visible marker class is encountered, preserving the graph’s existing stacking and overlap semantics.

These changes affect the Windows native renderer only. The X11 drawing paths and PostScript output retain their existing behavior.

Remaining private Tk dependencies

Complete elimination of private Tk interfaces is currently not practical without either changes in Tk itself or a significant drawing-backend redesign.

The remaining dependencies are deliberately isolated:

  • rbcText.c uses Tk’s internal TkDrawAngledChars() functionality for rotated text. Tk does not currently provide equivalent functionality through its public API.

  • rbcWinDraw.c uses tkWinInt.h to bridge between Tk drawables and native Windows GDI objects.

No other graph, element, image or general Rbc code needs to know about Tk’s Windows-private drawable structures.

Compiler diagnostics caused by known attribute mismatches inside the private Tk headers are suppressed only around those specific includes; warnings remain enabled for Rbc’s own source code.

C safety and hardening

The Tcl/Tk 9 work also included a wider C safety audit.

  • Replaced unsafe sprintf() usage with bounded or object-based formatting.

  • Added validation for user-controlled numeric formatting strings.

  • Added multiplication and allocation-overflow checks.

  • Added range checks before narrowing floating-point or wide integer values.

  • Added explicit handling of NaN and infinity.

  • Removed undefined or questionable integer-overflow paths.

  • Improved const correctness.

  • Removed mutations through pointers to constant data.

  • Fixed uninitialized index/data-mapping cases.

  • Removed obsolete K&R-era and compatibility code where encountered.

  • Removed dead #ifdef notdef and otherwise unreachable implementations where they no longer served a purpose.

Bindings and tags

The graph binding infrastructure was corrected and expanded during the migration.

  • Fixed axis bindings.

  • Fixed element bindings.

  • Fixed marker bindings.

  • Fixed legend binding activation.

  • Corrected graph tag generation and lookup.

  • Updated bind-tag handling after the option-system migration.

  • Added regression tests for the affected binding paths.

Regression testing

The modernization was accompanied by a substantial expansion of the test suite.

New regression coverage was added for areas including:

  • option configuration and rollback;

  • axes and axis bindings;

  • markers;

  • element options and bindings;

  • legends;

  • vectors and vector arithmetic;

  • splines;

  • non-finite values;

  • logarithmic axes;

  • bar charts;

  • PostScript output;

  • bitmap and image handling;

  • formatting options;

  • error bars;

  • closest-point calculations;

  • coordinate and integer-boundary cases.

Manual graph tests and demos were also repeatedly exercised during the migration.

The resulting code has been tested on both Windows and Linux with Tcl/Tk 9.x.

Remaining compatibility interfaces

The remaining use of legacy or private Tk interfaces is intentional:

  • the EPS canvas item uses the configuration API still required by Tk’s public custom-canvas-item interface;

  • rotated text uses Tk’s unsupported angled-text API;

  • the Windows drawing adapter uses Tk’s private drawable representation.

These remaining interfaces can only be removed cleanly if equivalent public Tk APIs become available or if the relevant rendering code is replaced with an independent backend.


Copyright (c) George Yashin