Skip to content

Markers: Render without using DrawString() #1668

@swharden

Description

@swharden

In #1660 @BambOoxX created an optimized marker rendering system that does not use DrawString().

This may require extensive testing (#1666/#1667), so in an effort to keep #1660 moving forward I'm cutting this code out of that PR and pasting it here so it can be possibly added as a future PR.

full code
public static void DrawMarkersNew(Graphics gfx, ICollection<PointF> pixelLocations, MarkerShape shape, float size, Color color)
{
	if (size == 0 || shape == MarkerShape.none)
		return;

	Pen pen = new Pen(color);

	Brush brush = new SolidBrush(color);

	float halfsize = size / 2;
	float halfsqrt3size = (float)0.866 * size;
	float halfsqrt2size = (float)0.707 * size;

	// adjust marker offset to improve rendering on Linux and MacOS
	float markerOffsetX = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? 0 : 1;
	switch (shape)
	{
		case MarkerShape.filledCircle:
			foreach (PointF point in pixelLocations)
			{
				gfx.FillEllipse(brush: brush, x: point.X - halfsize + markerOffsetX, y: point.Y - halfsize, width: size, height: size);
			}

			break;
		case MarkerShape.openCircle:
			foreach (PointF point in pixelLocations)
			{
				gfx.DrawEllipse(pen: pen, x: point.X - halfsize + markerOffsetX, y: point.Y - halfsize, width: size, height: size);
			}

			break;
		case MarkerShape.filledSquare:
			foreach (PointF point in pixelLocations)
			{
				gfx.FillRectangle(brush: brush, x: point.X - halfsize + markerOffsetX, y: point.Y - halfsize, width: size, height: size);
			}

			break;
		case MarkerShape.openSquare:
			foreach (PointF point in pixelLocations)
			{
				gfx.DrawRectangle(pen: pen, x: point.X - halfsize + markerOffsetX, y: point.Y - halfsize, width: size, height: size);
			}

			break;
		case MarkerShape.filledDiamond:
			foreach (PointF point in pixelLocations)
			{
				// Create points that define polygon.
				PointF point1 = new PointF(point.X + markerOffsetX, point.Y + halfsize);
				PointF point2 = new PointF(point.X + markerOffsetX - halfsize, point.Y);
				PointF point3 = new PointF(point.X + markerOffsetX, point.Y - halfsize);
				PointF point4 = new PointF(point.X + markerOffsetX + halfsize, point.Y);

				PointF[] curvePoints = { point1, point2, point3, point4 };

				//Fill polygon to screen
				gfx.FillPolygon(brush, curvePoints);
			}

			break;
		case MarkerShape.openDiamond:
			foreach (PointF point in pixelLocations)
			{
				// Create points that define polygon.
				PointF point5 = new PointF(point.X + markerOffsetX, point.Y + halfsize);
				PointF point6 = new PointF(point.X + markerOffsetX - halfsize, point.Y);
				PointF point7 = new PointF(point.X + markerOffsetX, point.Y - halfsize);
				PointF point8 = new PointF(point.X + markerOffsetX + halfsize, point.Y);

				PointF[] curvePoints2 = { point5, point6, point7, point8 };

				//Draw polygon to screen
				gfx.DrawPolygon(pen, curvePoints2);
			}

			break;
		case MarkerShape.asterisk:
			//Font drawFont = new Font("CourierNew", size * 3);
			//SizeF textSize = Drawing.GDI.MeasureString(gfx, "*", drawFont);
			//float halfWidth = textSize.Width / 2;
			//float quarterHeight = textSize.Height / 4;
			foreach (PointF point in pixelLocations)
			{
				//gfx.DrawString("*", drawFont, brush, point.X - halfWidth, point.Y - quarterHeight);
				// horizontal line of the *
				gfx.DrawLine(pen, point.X + markerOffsetX - halfsize, point.Y, point.X + markerOffsetX + halfsize, point.Y);
				// vertical line of the *
				gfx.DrawLine(pen, point.X + markerOffsetX, point.Y - halfsize, point.X + markerOffsetX, point.Y + halfsize);
				// bottom-left / top-right line of the *
				gfx.DrawLine(pen, point.X + markerOffsetX - halfsqrt2size, point.Y - halfsqrt2size, point.X + markerOffsetX + halfsqrt2size, point.Y + halfsqrt2size);
				// top-left / bottom-right line of the *
				gfx.DrawLine(pen, point.X + markerOffsetX - halfsqrt2size, point.Y + halfsqrt2size, point.X + markerOffsetX + halfsqrt2size, point.Y - halfsqrt2size);
			}

			break;
		case MarkerShape.hashTag:
			Font drawFont2 = new Font("CourierNew", size * 2);
			SizeF textSize2 = Drawing.GDI.MeasureString(gfx, "#", drawFont2);
			float halfWidth2 = textSize2.Width / 2;
			float quarterHeight2 = textSize2.Height / 4;
			foreach (PointF point in pixelLocations)
			{
				gfx.DrawString("#", drawFont2, brush, point.X - halfWidth2, point.Y - quarterHeight2);
			}

			break;
		case MarkerShape.cross:
			//Font drawFont3 = new Font("CourierNew", size * 2);
			//SizeF textSize3 = Drawing.GDI.MeasureString(gfx, "+", drawFont3);
			//float halfWidth3 = textSize3.Width / (5 / 2);
			//float quarterHeight3 = textSize3.Height / 2;
			foreach (PointF point in pixelLocations)
			{
				//gfx.DrawString("+", drawFont3, brush, point.X - halfWidth3, point.Y - quarterHeight3);
				// horizontal line of the +
				gfx.DrawLine(pen, point.X + markerOffsetX - halfsize, point.Y, point.X + markerOffsetX + halfsize, point.Y);
				// vertical line of the +
				gfx.DrawLine(pen, point.X + markerOffsetX, point.Y - halfsize, point.X + markerOffsetX, point.Y + halfsize);
			}

			break;
		case MarkerShape.eks:
			//Font drawFont4 = new Font("CourierNew", size * 2);
			//SizeF textSize4 = Drawing.GDI.MeasureString(gfx, "x", drawFont4);
			//float halfWidth4 = textSize4.Width / (5 / 2);
			//float quarterHeight4 = textSize4.Height / 2;
			float sqrt2size = (float)1.414 * size;
			foreach (PointF point in pixelLocations)
			{
				//gfx.DrawString("x", drawFont4, brush, point.X - halfWidth4, point.Y - quarterHeight4);
				// bottom-left / top-right line of the x
				gfx.DrawLine(pen, point.X + markerOffsetX - sqrt2size, point.Y - sqrt2size, point.X + markerOffsetX + sqrt2size, point.Y + sqrt2size);
				// top-left / bottom-right line of the x
				gfx.DrawLine(pen, point.X + markerOffsetX - sqrt2size, point.Y + sqrt2size, point.X + markerOffsetX + sqrt2size, point.Y - sqrt2size);
			}

			break;
		case MarkerShape.verticalBar:
			//Font drawFont5 = new Font("CourierNew", size * 2);
			//SizeF textSize5 = Drawing.GDI.MeasureString(gfx, "|", drawFont5);
			//float halfWidth5 = textSize5.Width / (5 / 2);
			//float quarterHeight5 = textSize5.Height / 2;
			foreach (PointF point in pixelLocations)
			{
				//gfx.DrawString("|", drawFont5, brush, point.X - halfWidth5, point.Y - quarterHeight5);
				// vertical line of the *
				gfx.DrawLine(pen, point.X + markerOffsetX, point.Y - halfsize, point.X + markerOffsetX, point.Y + halfsize);
			}

			break;
		case MarkerShape.triUp:
			foreach (PointF point in pixelLocations)
			{
				// Create points that define polygon.
				PointF point9 = new PointF(point.X + markerOffsetX, point.Y - size);
				PointF point10 = new PointF(point.X + markerOffsetX, point.Y);
				PointF point11 = new PointF(point.X + markerOffsetX - halfsqrt3size, point.Y + halfsize);
				PointF point12 = new PointF(point.X + markerOffsetX, point.Y);
				PointF point13 = new PointF(point.X + markerOffsetX + halfsqrt3size, point.Y + halfsize);

				PointF[] curvePoints3 = { point12, point9, point10, point11, point12, point13 };

				//Draw polygon to screen
				gfx.DrawPolygon(pen, curvePoints3);
			}

			break;
		case MarkerShape.triDown:
			foreach (PointF point in pixelLocations)
			{
				// Create points that define polygon.
				PointF point14 = new PointF(point.X + markerOffsetX, point.Y + size);
				PointF point15 = new PointF(point.X + markerOffsetX, point.Y);
				PointF point16 = new PointF(point.X + markerOffsetX - halfsqrt3size, point.Y - halfsize);
				PointF point17 = new PointF(point.X + markerOffsetX, point.Y);
				PointF point18 = new PointF(point.X + markerOffsetX + halfsqrt3size, point.Y - halfsize);

				PointF[] curvePoints4 = { point17, point14, point15, point16, point17, point18 };

				//Draw polygon to screen
				gfx.DrawPolygon(pen, curvePoints4);
			}

			break;
		case MarkerShape.none:
			break;
		default:
			throw new NotImplementedException($"unsupported marker type: {shape}");
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions