April 09, 2011
On 2011-04-08 22:31:11 -0400, Matthias Pleh <jens@konrad.net> said:

> For the drawingt part. I'm currently working on a rednerer with scanline algorithm. This will be completly OS-independent. It's in a single file with 2kLOC, and rather low level drawing. I just render on a simple ubyte-array, but it already support polygon-rendering, antialiasing and different color-structs.

Oh! really nice.


> This is how it looks like for now:
> http://img683.imageshack.us/i/testawa.jpg/
> this takes currently 62ms to render, don't know if this is fast enough,
> but can definitley improved.

The important thing is to have an API. Once it works we can concern ourselves with speed. What's the API like?


-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

April 09, 2011
Am 09.04.2011 13:11, schrieb Michel Fortin:
> On 2011-04-08 22:31:11 -0400, Matthias Pleh <jens@konrad.net> said:
>
>> For the drawingt part. I'm currently working on a rednerer with
>> scanline algorithm. This will be completly OS-independent. It's in a
>> single file with 2kLOC, and rather low level drawing. I just render on
>> a simple ubyte-array, but it already support polygon-rendering,
>> antialiasing and different color-structs.
>
> Oh! really nice.
>
>
>> This is how it looks like for now:
>> http://img683.imageshack.us/i/testawa.jpg/
>> this takes currently 62ms to render, don't know if this is fast enough,
>> but can definitley improved.
>
> The important thing is to have an API. Once it works we can concern
> ourselves with speed. What's the API like?
>
>


Currntly, it's rather low level API.
It looks like this:

void draw()
{
    buf = new ubyte[width * height * 3];
    // create render buffer, just a ubyte-arrray (no color info)
    PixelBuffer pixBuff = new PixelBuffer(buf, width, height, width*3);

    auto ren= new Renderer!(Rgb24)(pixBuff);  // -> Rgb24
 	                              // manages the correct Color
    Rasterizer ras = new Rasterizer;

    // create the path
    // per primitive min. 3 vertexes needed
    ras.movTo(20,40);  // -> moves the point without drawing
    ras.lineTo(30,80); // -> moves the point with drawing
    [...snip...]

    // finally render it to the PixelBuffer
    ras.render(ren, Color(255,0,0));
}

on this we could add more abstraction to directly render lines, rectangles and circles ... (the posted image is completly renderd
whit the API above)

I hope, I will find time this weekend, clean it up and post it here.
Maybe Adam then could add it to his source.


°Matthias
April 09, 2011
On 04/09/2011 02:42 AM, Adam D. Ruppe wrote:
>> OK. (But for this module I think usage simplicity is more
>> >  important than raw speed.
> The struct is at least equal in simplicity:
>
> image[x, y] = Color(r, g, b);
>
> vs
>
> image[x, y] = tuple(r, g, b);

Indeed; and then:
    image[x, y].g
versus
    image[x, y][1]

Denis
-- 
_________________
vita es estrany
spir.wikidot.com

April 09, 2011
On 04/09/2011 06:50 AM, Cliff Hudson wrote:
> So is the objective to create a windowing library, or a drawing library for
> (for example) games?  The two are rather different - though you can build a
> windowing library on top of a drawing library, doing so is pointless given
> the plethora of GUI libraries already out there, several with D bindings.
>   If the bindings are hard to use, I'd address that directly rather than
> creating a whole new one.
>
> If you are building a drawing library, I'd definitely co-opt an existing one
> (SDL would be a good place, in particular since its license seems favorable
> and the API is well known.)  In fact, a good game-oriented library for D I
> think would be a fabulous idea.  D has a lot to offer game developers, a
> great mix of the high level and the low.  I don't know if it is popular with
> this crowd, but Microsoft DirectX or the XNA Framework might be a good place
> to go for pattern ideas as well.
>
> But generally I'd strive to not create new APIs, just new implementations
> (as necessary.)

I guess you are simply missing the point. You should re-read start-of-thread messages.

Denis
-- 
_________________
vita es estrany
spir.wikidot.com

April 09, 2011
Matthias Pleh:
> I hope, I will find time this weekend, clean it up and post it here. Maybe Adam then could add it to his source.

Cool!
April 09, 2011
Yeah, I'm certainly not suggesting you necessarily rip off implementation,
just possibly API design.  But to be fair it has been a while since I used
that API - maybe the design is crap by comparison to others these days.
 OpenGL or DirectX for quick rendering is definitely the way to go under the
covers.

On Sat, Apr 9, 2011 at 8:05 AM, Adam D. Ruppe <destructionator@gmail.com>wrote:

> On Fri, Apr 08, 2011 at 09:50:11PM -0700, Cliff Hudson wrote:
> > So is the objective to create a windowing library, or a drawing library
> for
> > (for example) games?
>
> Drawing. I have a windowing library in the works too, but it's much much
> more effort (even building off existing ones!) so it won't be ready to
> share
> for a long time still.
>
> > If you are building a drawing library, I'd definitely co-opt an existing
> one
> > (SDL would be a good place, in particular since its license seems
> favorable
> > and the API is well known.)
>
> I actually find SDL's drawing facilities to be almost useless... in my D1 game lib, even doing NES type games, I found it was simply too slow and redid the graphics with OpenGL.
>
> It's image loaders and sound/music parts are good though.
>
> > I don't know if it is popular with
> > this crowd, but Microsoft DirectX or the XNA Framework might be a good
> place
> > to go for pattern ideas as well.
>
> I've heard good things about XNA, but I've never used it...
>
>


April 09, 2011
bearophile wrote:
> http://rosettacode.org/wiki/Animate_a_pendulum

Here's the D version using the direct to screen functions. I ported the C# version, since the APIs were most alike. (Not really surprising... C# and I both thinly wrapped GDI.)

===

import std.math;
import simpledisplay;

void main() {
    auto win = new SimpleWindow(512, 512);

    real angle = PI / 2;
    real angleVelocity = 0;
    immutable real dt = 0.1;
    int length = 150;

    win.eventLoop(50,
        () {
            auto anchorX = win.width / 2 - 12;
            auto anchorY = win.height / 4;
            auto ballX = anchorX + cast(int) (sin(angle) * length);
            auto ballY = anchorY + cast(int) (cos(angle) * length);

            auto angleAcceleration = -9.81 / length * sin(angle);
            angleVelocity += angleAcceleration * dt;
            angle += angleVelocity * dt;

            auto painter = win.draw();

            painter.fillColor = Color(255, 255, 255);
            painter.drawRectangle(0, 0, win.width, win.height);

            painter.outlineColor = Color(0, 0, 0);
            painter.drawLine(anchorX, anchorY, ballX, ballY);
            painter.fillColor = Color(0, 0, 0);
            painter.drawEllipse(anchorX - 3, anchorY - 4, anchorX + 3, anchorY + 4);

            painter.fillColor = fromHsl(30, 0.8, 0.4);
            painter.drawEllipse(ballX - 7, ballY - 7, ballX + 7, ballY + 7);
        }
    );
}

===

Exit the program by closing the window.

Some comments. First, you'll see that the pulse timer is implemented, allowing the animation to easily happen.

Then, there's a dozen lines to actually do the drawing, starting with

auto painter = win.draw();

win.draw (name might change) returns a ScreenPainter struct, which gives access to the drawing directly in the window, via a handful of native api calls. This struct is necessary to handle some state and cleanup that the native APIs require. (It's destructor does it all for you though, so no need to think about it here.)

You can draw images, text, pixels (slowly), lines, rectangles, arcs, and polygons with solid colors for filling and outline. It's the lowest common denominator between Windows GDI and Xlib.

I agree with what Michel said earlier about it being a lot of fairly pointless effort to go further than this. Instead, we'll offer a rich set of cross-platform functions to draw to an image, and simply copy those images to the windows when we want to get fancy. (That's easy to do here: painter.drawImage(x, y, image); )

Moving on, I first clear the screen by drawing a large, white rectangle over the window.

fillColor and outlineColor are settable properties that are implicitly used in the drawing functions. (This reflects how pens and brushes are tied to the HDC on Windows and foreground and background colors are tied to the GC on X - you don't pass colors to the individual functions on either native API either.)

Following Windows' lead, all the functions use the two separate colors, one for the outline of the shape and one for filling it in, hence their names. Note that only outline is used for drawline lines and pixels.

If you want to draw a shape without filling it in, first set the fill color to transparent:

painter.fillColor = Color.transparent;

(Color.transparent is a static method to conveniently return a
color with alpha = 0.)


Also in there, you can see me using the fromHsl helper function to return a Color specifying HSL values instead of RGB.


Finally, I said this draws directly to the screen, but it actually doesn't... it double buffers underneath. No need to call a buffer.flip function though. ScreenPainter's destructor does it for you. You don't have to think about it.

The buffer is preserved across calls and used to automatically handle WM_PAINT/Expose messages.



======

There's still a handful of little things and a lot of error checking needed, but I'm pretty happy with this as the screen drawing base. It covers all the easy stuff. The hard stuff will be in platform independent images.

Anyway, here's the updated simpledisplay.d:

http://arsdnet.net/dcode/simpledisplay.d
April 09, 2011
Adam D. Ruppe:

> Here's the D version using the direct to screen functions.

On Windows Vista the window often doesn't close when I hit the close window click at the top right.

Probably quite more people will have to comment and think about this module and its API before adopting it.

The length=150 too is better to be set as immutable. Most of the auto variables (but "painter") are better set to immutable.

The delegate inside eventLoop doesn't need the ().

Color(255, 255, 255) ==> Color.white
Color(0, 0, 0) ==> Color.black

painter.drawRectangle(0, 0, win.width, win.height) painter.clearAll(), or painter.clearAll(Color.white) or something similar.

auto win = new SimpleWindow(512, 512);
==>
auto win = new SimpleWindow(512, 512, "Pendulum");

win.eventLoop(10, {...})  does this semantics work with more than one window too?

        auto painter = win.draw();
        painter.fillColor = Color(255, 255, 255);
==> sometimes a bit better:
        auto painter = win.draw();
        with (painter) {
            fillColor = ...
            ...

drawRectangle => rectangle or even "box"
drawLine ==> line
drawEllipse ==> ellipse
circle
fillColor ==> filling?
outline => pen?


> This struct is necessary to handle some state
> and cleanup that the native APIs require.

This is not so nice...


> I agree with what Michel said earlier about it being a lot of fairly pointless effort to go further than this.

Reading keyboard chars, and mouse clicks & movements is very useful.
Another useful thing is to plot a matrix of rgb or int or ubyte in grey shades, to speed up some basic usage. In PyGame/Numpy there is surfarray for this.

Bye,
bearophile
April 10, 2011
bearophile wrote:
> The length=150 too is better to be set as immutable.

Yeah, I made that change after copy/pasting the code into my newsgroup post. win, painter, angle, and angular velocity were the only things left mutable in the end.

> The delegate inside eventLoop doesn't need the ().

I put it there for consistency with other events. While there are no other handlers in this example, there can be, and all the other ones take arguments.

> Color(255, 255, 255) ==> Color.white
> painter.clearAll(Color.white)
> auto win = new SimpleWindow(512, 512, "Pendulum");

Yeah, those are reasonable.

> win.eventLoop(10, {...})  does this semantics work with more than
> one window too?

I believe so, yes, but I haven't tried it and wouldn't mind if it didn't. If you need to use multiple windows at the same time, it'd probably be better to use DFL, DWT, GTK, Qt, etc. etc.

> drawRectangle => rectangle or even "box"

Heh, I used to call it box in my DOS programs. But, the functions
it forwards to are called Rectangle() and XDrawRectangle(), so
I want to be consistent with them so it's not a surprise to
someone already familiar with with the other APIs.

The reason I went with drawShape instead of just Shape is that functions are verbs, so generally, I like their names to be verbs too. There's exceptions, of course, but I don't these fit.

> outline => pen?

Pens do more than just color. I guess the real question is how complex should it be able to get?

In the underlying APIs, you can make a pen with a color, a width, and a style (solid, dashed, etc). Since they both can do it, I think I will add it.

(Some more advanced APIs can even do things like gradient pens. I hope the image manipulation functions we write will eventually be able to do this too, but since plain old X can't, it won't happen for the display painter.)

Perhaps:

painter.pen = Pen(Color(r, g, b), 1, Pen.Style.solid);

(Or maybe new Pen()... have to think about that.)

And offer an overload for the simple case where you only care about color. (or keep the current fillColor property.)

Then, of course, something similar for Brush, which is used to fill the background.

But, here, the idea was to keep it simple, so I went with just color. I guess that might be too simple.

> This is not so nice...

I'm sympathetic, but I don't agree losing the struct would be good.

Even if the struct proves to be unnecessary (which it might be - I think you can hold on to a window HDC as long as you like, but it's not something I see often so I think it's discouraged), I'd still keep it.

Having an automatic constructor and destructor available gives some flexibility on the implementation side without inconveniencing the user beyond a single function.

For example, the implicit double buffering done here. You don't have to think about flipping it. It just works. Better yet, the implementation can disable it at will. BitBlt is slow over Remote Desktop on Windows, making double buffering laggy. But, you, the user, don't really need to know that, since it wasn't your problem in the first place. The ScreenPainter can make it's own decision about it.

> Reading keyboard chars, and mouse clicks & movements is very useful.

For that, you can pass other handlers to eventLoop. I still intend to define a few more you can use.

> Another useful thing is to plot a matrix of rgb or int or ubyte in grey shades, to speed up some basic usage. In PyGame/Numpy there is surfarray for this.

Sounds like what you'd use the Image class for, so I think we're good on that too.


====

A note: since I posted last, I changed my mind on something, and bearophile, I rather doubt you'll like it...

Before, it was drawRectangle(int x1, int y2, int width, int height);

Now, it is drawRectangle(Point upperLeft, Size size);

So, at the usage, you now need to put in some more parenthesis and say what you mean:

painter.drawRectangle(Point(0, 0), Size(win.width, win.height));


Why did I do this? Well, consider the following:

drawRectangle(int x1, int y1, int x2, int y2);

(This is, in fact, the signature in Windows GDI, whereas Xlib
 user width/height.)


The types there are no different than the above, but meaning has changed. I'd be ok with saying "RTFM" to avoid having to use the structs.

But, RTFM doesn't let you overload it, whereas the types do. Now, it can offer both

drawRectangle(Point upperLeft, Size size);

*and*

drawRectangle(Point upperLeft, Point lowerRight);

I'm sure you were thinking about named arguments for a moment there about width/height vs x2/y2. I did too. But, named arguments don't really exist in D (you can do them via library magic but not without) so it's moot, and I don't believe named arguments would offer the overloading possibilities, which I like. It can save some math while being quite natural.

The Point struct also makes drawPolygon look a lot better, so it's of general use.
April 10, 2011
One thing to consider, since I was impacted by this when writing a WPF app a while back, is the difference between a 2-Vector and a Point.  In particular, Vectors typically have some very useful methods on them, like addition, rotation, transformations, etc. which frequently are not associated with the Point type.  I don't know if you have or plan to support this data type, but at least having simple transforms to/from Vector would be nice.

- Cliff

On Sat, Apr 9, 2011 at 5:58 PM, Adam D. Ruppe <destructionator@gmail.com>wrote:

> bearophile wrote:
> > The length=150 too is better to be set as immutable.
>
> Yeah, I made that change after copy/pasting the code into my newsgroup post. win, painter, angle, and angular velocity were the only things left mutable in the end.
>
> > The delegate inside eventLoop doesn't need the ().
>
> I put it there for consistency with other events. While there are no other handlers in this example, there can be, and all the other ones take arguments.
>
> > Color(255, 255, 255) ==> Color.white
> > painter.clearAll(Color.white)
> > auto win = new SimpleWindow(512, 512, "Pendulum");
>
> Yeah, those are reasonable.
>
> > win.eventLoop(10, {...})  does this semantics work with more than
> > one window too?
>
> I believe so, yes, but I haven't tried it and wouldn't mind if it didn't. If you need to use multiple windows at the same time, it'd probably be better to use DFL, DWT, GTK, Qt, etc. etc.
>
> > drawRectangle => rectangle or even "box"
>
> Heh, I used to call it box in my DOS programs. But, the functions
> it forwards to are called Rectangle() and XDrawRectangle(), so
> I want to be consistent with them so it's not a surprise to
> someone already familiar with with the other APIs.
>
> The reason I went with drawShape instead of just Shape is that functions are verbs, so generally, I like their names to be verbs too. There's exceptions, of course, but I don't these fit.
>
> > outline => pen?
>
> Pens do more than just color. I guess the real question is how complex should it be able to get?
>
> In the underlying APIs, you can make a pen with a color, a width, and a style (solid, dashed, etc). Since they both can do it, I think I will add it.
>
> (Some more advanced APIs can even do things like gradient pens. I hope the image manipulation functions we write will eventually be able to do this too, but since plain old X can't, it won't happen for the display painter.)
>
> Perhaps:
>
> painter.pen = Pen(Color(r, g, b), 1, Pen.Style.solid);
>
> (Or maybe new Pen()... have to think about that.)
>
> And offer an overload for the simple case where you only care about color. (or keep the current fillColor property.)
>
> Then, of course, something similar for Brush, which is used to fill the background.
>
> But, here, the idea was to keep it simple, so I went with just color. I guess that might be too simple.
>
> > This is not so nice...
>
> I'm sympathetic, but I don't agree losing the struct would be good.
>
> Even if the struct proves to be unnecessary (which it might be - I think you can hold on to a window HDC as long as you like, but it's not something I see often so I think it's discouraged), I'd still keep it.
>
> Having an automatic constructor and destructor available gives some flexibility on the implementation side without inconveniencing the user beyond a single function.
>
> For example, the implicit double buffering done here. You don't have to think about flipping it. It just works. Better yet, the implementation can disable it at will. BitBlt is slow over Remote Desktop on Windows, making double buffering laggy. But, you, the user, don't really need to know that, since it wasn't your problem in the first place. The ScreenPainter can make it's own decision about it.
>
> > Reading keyboard chars, and mouse clicks & movements is very useful.
>
> For that, you can pass other handlers to eventLoop. I still intend to define a few more you can use.
>
> > Another useful thing is to plot a matrix of rgb or int or ubyte in grey shades, to speed up some basic usage. In PyGame/Numpy there is surfarray for this.
>
> Sounds like what you'd use the Image class for, so I think we're good on that too.
>
>
> ====
>
> A note: since I posted last, I changed my mind on something, and bearophile, I rather doubt you'll like it...
>
> Before, it was drawRectangle(int x1, int y2, int width, int height);
>
> Now, it is drawRectangle(Point upperLeft, Size size);
>
> So, at the usage, you now need to put in some more parenthesis and say what you mean:
>
> painter.drawRectangle(Point(0, 0), Size(win.width, win.height));
>
>
> Why did I do this? Well, consider the following:
>
> drawRectangle(int x1, int y1, int x2, int y2);
>
> (This is, in fact, the signature in Windows GDI, whereas Xlib
>  user width/height.)
>
>
> The types there are no different than the above, but meaning has changed. I'd be ok with saying "RTFM" to avoid having to use the structs.
>
> But, RTFM doesn't let you overload it, whereas the types do. Now, it can offer both
>
> drawRectangle(Point upperLeft, Size size);
>
> *and*
>
> drawRectangle(Point upperLeft, Point lowerRight);
>
> I'm sure you were thinking about named arguments for a moment there about width/height vs x2/y2. I did too. But, named arguments don't really exist in D (you can do them via library magic but not without) so it's moot, and I don't believe named arguments would offer the overloading possibilities, which I like. It can save some math while being quite natural.
>
> The Point struct also makes drawPolygon look a lot better, so it's of general use.
>