Git Product home page Git Product logo

Comments (7)

Kaiido avatar Kaiido commented on August 24, 2024

Disclaimer, I know nothing about GDI.

What you describe applies only to strokes. The actual issue is that strokes in the Canvas2d API just like in many graphics APIs can only be "centered", i.e they'll spread from both inside and outside of the path.
This means that drawing a line from 10,10 to 20,10 with a 1px lineWidth would indeed fill a rectangle with coords 10,9.5 20,10.5.
But the coordinate system isn't to blame here, it's just how stroking works.
There could be a point to allow either inner or outer stroking, but this raises some issues as pointed by SVG specs where this was raised already: https://svgwg.org/specs/strokes/#SpecifyingStrokeAlignment.

from canvas2d.

gitspeaks avatar gitspeaks commented on August 24, 2024

How do you know that this a matter of how “stroking” works ? I’m asking seriously. Are you aware of any authoritative reference defining “stroking”. I actually tried several searches and as I understand it "stroking" is about “materializing” the abstract line in a path and I can see how the choice of alignment comes into play when working with a wireframe you can actually see like in adobe illustrator and I’m not dismissing this. However, I’m not quite sure "alignment" is a “feature” of stroke. Like stroke may very well mean “Pen” so I disagree that the actual issue is "stroke" in fact it’s not an issue at all if one chooses to think about the canvas as Cartesian system with infinite points on either axis and stroke having alignment, but this does not necessarily exclude an additional approach where the canvas is viewed as what it is which is grid of pixels where each pixel can be identified by intersection of row, column. IMHO This one-to-one correspondence can very much simplifies things thus I suggested an additional mode of operation that is restricted to a whole number Cartesian system.

I think a side-by-side comparison shows it all:

Here is 50x50 grid cell using the canvas Api.

Note that for some reason the bottom right pixel is slightly gray in color

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

ctx.beginPath();

// should y be 0 or 0.5?
ctx.moveTo(0.5, 0);
ctx.lineTo(0.5, 49.5); 

ctx.moveTo(49.5, 0);
ctx.lineTo(49.5, 49.5); 

// should x be 0 or 0.5?
ctx.moveTo(0, 0.5);
ctx.lineTo(49.5, 0.5);

ctx.moveTo(0, 49.5);
ctx.lineTo(49.5, 49.5);

ctx.stroke();

Here is 50x50 grid cell using Gdi.

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <stdlib.h>

#define WINDOW_CLASS_NAME  "WINCLASS1"
#define WINDOW_W 1024
#define WINDOW_H 768


LRESULT CALLBACK WindowProc(
	HWND hwnd,
	UINT msg,
	WPARAM wparam,
	LPARAM lparam)
{
	PAINTSTRUCT ps;
	HDC hdc;

	switch (msg) {

	case WM_CREATE:
	{
		return 0;
	} break;

	case WM_PAINT:
	{
		hdc = BeginPaint(hwnd, &ps);

		HPEN pen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
		HPEN oldPen = (HPEN)SelectObject(hdc, pen);

		// LineTo draws from the current position up to but not including the position.
		MoveToEx(hdc, 0, 0, NULL);
		LineTo(hdc, 0, 50);

		MoveToEx(hdc, 49, 0, NULL);
		LineTo(hdc, 49, 50);

		MoveToEx(hdc, 0, 0, NULL);
		LineTo(hdc, 50, 0);

		MoveToEx(hdc, 0, 49, NULL);
		LineTo(hdc, 50, 49);

		DeleteObject(oldPen);
		DeleteObject(pen);
		ReleaseDC(hwnd, hdc);
		EndPaint(hwnd, &ps);
		return(0);

	} break;

	case WM_DESTROY:
	{
		PostQuitMessage(0);
		return 0;
	} break;

	default: break;

	}

	return (DefWindowProc(hwnd, msg, wparam, lparam));
}


int WINAPI WinMain(
	_In_ HINSTANCE hInstance,
	_In_opt_ HINSTANCE hPrevInstance,
	_In_ LPSTR lpCmdLine,
	_In_ int nCmdShow)
{
	WNDCLASSEX winclass;
	HWND hwnd;
	MSG msg;
	HDC hdc;

	int screenX = GetSystemMetrics(SM_CXSCREEN);
	int screenY = GetSystemMetrics(SM_CYSCREEN);

	winclass.cbSize = sizeof(WNDCLASSEX);
	winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW, CS_VREDRAW;
	winclass.lpfnWndProc = WindowProc;
	winclass.cbClsExtra = 0;
	winclass.cbWndExtra = 0;
	winclass.hInstance = hInstance;
	winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	winclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	winclass.lpszMenuName = NULL;
	winclass.lpszClassName = WINDOW_CLASS_NAME;
	winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

	if (!RegisterClassEx(&winclass))
		return 0;

	if (!(hwnd = CreateWindowEx(
		NULL,
		WINDOW_CLASS_NAME,
		"Gdi Window",
		WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		(screenX - WINDOW_W) / 2,
		(screenY - WINDOW_H) / 2,
		WINDOW_W, WINDOW_H,
		NULL,
		NULL,
		hInstance,
		NULL
	)))
		return(0);


	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (msg.message == WM_QUIT)
			break;

		TranslateMessage(&msg);
		DispatchMessage(&msg);

	} // end while

	return(msg.wParam);
}

from canvas2d.

Kaiido avatar Kaiido commented on August 24, 2024

Are you aware of any authoritative reference defining “stroking”.

https://html.spec.whatwg.org/multipage/canvas.html#trace-a-path

Create a new path that describes the edge of the areas that would be covered if a straight line of length equal to style's lineWidth was swept along each subpath in path while being kept at an angle such that the line is orthogonal to the path being swept, [...]


IMHO This one-to-one correspondence can very much simplifies things

You have this "one-to-one correspondence" with fill(). An horizontal or vertical line is just a rectangle with lineWidth height or width. If you want to trace an horizontal line from coords (10,10) (60,10) with 1px lineWidth, you can do ctx.fillRect(10, 10, 50, 1) and with the identity matrix transform, it will fill the bitmap as you wish.
Strokes are defined as overlapping over both sides of the path. That's how it is. Changing how the coordinate system works wouldn't solve that.

from canvas2d.

gitspeaks avatar gitspeaks commented on August 24, 2024

I understand what you’re saying, and if “trace-a-path” == “stroke” then so be it. I’m not arguing with the the standard (although it would be clearer if they used the word “stroke” there). I’m not arguing about the definition of “stroke”. The only reason I alluded to it is because that's how you display a LineTo (not a rectangle of width 1). I’m not proposing to redefine what stroke means but rather add a mode/api without stroke, modeled after a discrete Cartesian grid where coordinates refer to pixels which are rectangles with width and height of unit length (not points).

Example:

const ctx = canvas.getContext('2d-discrete');


ctx.moveTo(0, 0);
ctx.lineTo(0, 49); 

ctx.moveTo(49, 0);
ctx.lineTo(49, 49); 

ctx.moveTo(0, 0);
ctx.lineTo(49, 0);

ctx.moveTo(0, 49);
ctx.lineTo(49, 49);

ctx.paint();

I’m not saying this is “better” but can be simpler to reason about when working with straight lines as apposed to thinking about the interval between points in a continuous Cartesian Grid when stroking lines. Like if I want to display a line - on the screen - from 0,0 to 0,9 then I think its more intuitive to think about drawing a line from the first pixel down 9 pixels as apposed to thinking about how filling an area between imaginary points map to display pixels.

from canvas2d.

Kaiido avatar Kaiido commented on August 24, 2024

And would that be simpler to reason about for the 99.9% of cases where paths aren't a grid on cartesian coordinates? I mean, what does this mean for a circle? For an oblique line? Etc.
Are you really proposing a new canvas context whose only purpose is to draw a 1px lineWidth grid?

(Also, the trace a path algorithm I linked to is part of the stroke-steps one).

from canvas2d.

gitspeaks avatar gitspeaks commented on August 24, 2024

https://docs.microsoft.com/en-us/windows/win32/gdi/path-creation

from canvas2d.

gitspeaks avatar gitspeaks commented on August 24, 2024

BTW, it is even suggested not use floating-point coordinates for performance reasons.

"Avoid floating-point coordinates and use integers instead"

https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Optimizing_canvas#avoid_floating-point_coordinates_and_use_integers_instead

I assume this is not strictly about drawImage since I can clearly see that anti aliasing is applied when I draw a tilted line between whole coordinates.

from canvas2d.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.