GraphiteEditor/Graphite · error

transform should be able to be set to be able to account for

Error message

transform should be able to be set to be able to account for DPI

What it means

start_dpi_aware_transform pushes a uniform scale matrix built from self.viewport.scale() via ctx.set_transform(...). The JS setTransform throws when any matrix entry is non-finite or the matrix is non-invertible. Because the matrix is a pure scale, the only realistic trigger is a non-finite viewport scale (NaN or Inf zoom) — and since every DPI-aware overlay primitive funnels through this helper, one bad scale panics on the first overlay draw of the frame.

Source

Thrown at editor/src/messages/portfolio/document/overlays/utility_types_web.rs:613

	}

	pub fn skew_handles(&mut self, edge_start: DVec2, edge_end: DVec2) {
		let edge_dir = (edge_end - edge_start).normalize();
		let mid = edge_end.midpoint(edge_start);

		for edge in [edge_dir, -edge_dir] {
			self.draw_triangle(mid + edge * (3. + SKEW_TRIANGLE_OFFSET), edge, SKEW_TRIANGLE_SIZE, None, None);
		}
	}

	/// Transforms the canvas context to adjust for DPI scaling
	///
	/// Overwrites all existing tranforms. This operation can be reversed with [`Self::reset_transform`].
	fn start_dpi_aware_transform(&self) {
		let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(self.viewport.scale())).to_cols_array();
		self.render_context
			.set_transform(a, b, c, d, e, f)
			.expect("transform should be able to be set to be able to account for DPI");
	}

	/// Un-transforms the Canvas context to adjust for DPI scaling
	///
	/// Warning: this function doesn't only reset the DPI scaling adjustment, it resets the entire transform.
	fn end_dpi_aware_transform(&self) {
		self.render_context.reset_transform().expect("transform should be able to be reset to be able to account for DPI");
	}

	pub fn square(&mut self, position: DVec2, size: Option<f64>, color_fill: Option<&str>, color_stroke: Option<&str>) {
		let size = size.unwrap_or(MANIPULATOR_GROUP_MARKER_SIZE);
		let color_fill = color_fill.unwrap_or(COLOR_OVERLAY_WHITE);
		let color_stroke = color_stroke.unwrap_or(COLOR_OVERLAY_BLUE);

		let position = self.snap_to_physical_pixel_center(position);
		let corner = position - DVec2::splat(size) / 2.;

		self.start_dpi_aware_transform();

View on GitHub (pinned to c507b35645)

Solutions

  1. Guard the scale at its computation site: clamp zoom to a finite positive range and skip updates when viewport size is zero.
  2. Skip overlay drawing entirely while the viewport has zero or non-finite dimensions.
  3. Degrade the .expect to .ok() with a warn so one bad frame logs instead of crashing.

Example fix

// before
let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(self.viewport.scale())).to_cols_array();
self.render_context
	.set_transform(a, b, c, d, e, f)
	.expect("transform should be able to be set to be able to account for DPI");

// after
let scale = self.viewport.scale();
if !scale.is_finite() || scale <= 0. {
	log::warn!("Non-finite viewport scale; skipping DPI-aware transform");
	return;
}
let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(scale)).to_cols_array();
let _ = self.render_context.set_transform(a, b, c, d, e, f);
Defensive patterns

Strategy: validation

Validate before calling

fn viewport_scale_drawable(scale: f64) -> bool {
	scale.is_finite() && scale > 0.
}
// check before start_dpi_aware_transform runs

Type guard

fn is_finite_positive(scale: f64) -> bool {
	scale.is_finite() && scale > 0.
}

Try / catch

if let Err(err) = self.render_context.set_transform(a, b, c, d, e, f) {
	log::warn!("failed to set DPI transform: {:?}", err);
}

Prevention

When it happens

Trigger: Viewport scale computed as NaN/Inf — e.g., dividing by a zero-size viewport during window resize or teardown, or a zoom animation hitting a division by zero — followed by any overlay draw calling start_dpi_aware_transform (square, manipulator, pivot, compass).

Common situations: Resizing the editor pane to 0x0 or during window teardown; zoom animation edge cases; hi-DPI scale computations dividing by zero dimensions.

Related errors


AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16). Data as JSON: /api/errors/be09bcf6e618c831. Report an issue: GitHub.