Thread overview | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
April 08, 2011 simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
We discussed this first in the GUI library thread, but since it meandered so much, I decided to split off into a new subject. Much of what I say here will be old to anyone who saw the previous thread. There's some new stuff nearer to the bottom though. I, with input from others, have started writing a little module for simple uses of a display. You can write to a bitmap, display it to a window, and handle events all in an easy way. The basics are cross platform, but you can use native function calls too. http://arsdnet.net/dcode/simpledisplay.d It's still very much in progress, but I think it's now at the point where the direction I'm taking it is clear. First, the simplest usage: ==== import simpledisplay; void main() { auto image = new Image(255, 255); foreach(a; 0..255) foreach(b; 0..255) image.putPixel(a, b, Color(a, b, ((a + b) % 16) * 16)); image.display(); } === Compile: dmd test.d simpledisplay.d When run, it will pop up a window with a gradient thing with little bars through it. Press any key and the window closes, exiting the program. On Windows, it uses GDI functions. On other systems, it tries to use X Windows. Linux is the only one I've personally tested, but it should work on OSX and FreeBSD too (without changing the code). If you are making some data, then just want to display it, this works. But, what if you want some interactivity? Here's a simple HSL color picker: http://arsdnet.net/dcode/simpledisplay_test2.d Use these keys to pick your color: q/a change hue. s/w changes S. e/d changes L. Press ESC to exit. Your color in rgb is printed to stdout. Here's the relevant parts of the code: void main() { auto image = new Image(255, 255); auto win = new SimpleWindow(image); win.eventLoop(0, (dchar c) { writeln("Got a char event: ", c); if(c == 'q') h += 15; // ...snip... foreach(a; 0..255) foreach(b; 0..255) image.putPixel(a, b, fromHsl(h, s, l)); win.image = image; }, (int key) { writeln("Got a keydown event: ", key); if(key == KEY_ESCAPE) { auto color = fromHsl(h, s, l); writefln("%02x%02x%02x", color.r, color.g, color.b); win.close(); } }); } First, we create an image. Then, a window based on that image. At line two, your window will show up on the screen, just like image.display() did above. But, here, instead of blocking, it moves on to a user specified event loop. SimpleWindow.eventLoop was inspired by std.concurrency.receive. The first parameter is a pulse timer amount (not yet implemented). If you set one, you'll get one of your delegates called at the requested interval. The idea there is to make animations or games easy to get started. After that comes a list of delegates. They are matched to different events by their signature. (dchar c) {} means a key was pressed, and it passes you the character corresponding to that key. (int c) {} is almost certain to change, but right now it matches to key press events, and passes the platform-specific keycode. I'm planning to put symbolic constants in with the same name across platform to make that easier to use, but the values themselves are likely to be platform-specific. When we get a key event here, the image is redrawn with a new color. win.image = image tells it you want the image redrawn with the new image. It's a blunt instrument, but a simple and fairly effective one. The advantage of this approach is you can just have fun with those pixels and handle keys, all right there inside main() - no need to do subclasses just for a simple program. At the same time, you can move those event handlers around easily enough, or even change them at runtime, simply by using named delegates and assignment. I plan to add more event possibilities so you can actually make this do some good work for you. Ultimately, they'll also be an (XEvent) {} and (HWND, LPARAM, WPARAM) event for when what I provide isn't good enough, so you can always build more off it. It's meant to be simple, but not horribly limiting. ====== Since last time I posted, I've reorganized the code inside simpledisplay.d quite a bit. Instead of version Windows vs version linux, it's now Windows and X11, reflecting the fact that X is available on many operating systems. (Including Windows, actually, but meh). The Color struct is now rgba, but it doesn't use a. I like the suggestion to template on different color spaces, but haven't gotten around to that yet. Next, Image and SimpleWindow are done pretty differently than before. Now, the platform specific parts are put into a separate mixin template: NativeImageImplementation!() and NativeSimpleWindowImpl...(). The public methods forward to those. The reason for this is three fold: a) It makes the public code a lot easier on the eyes. It was getting into spaghetti version territory for a bit there... now it's nice and clean. b) The implementations can now be moved to their own modules. I haven't simply because a single file is easier to distribute and play with, but the final thing probably should split it up, and now it can. c) It should be more obvious when something isn't implemented for an operating system, while keeping the public ddoc all in one place. versions Before, Image was simply a byte array in a single format, meaning to get to the native OS format for blitting, it had to be copied. Now, the actual format is hidden inside impl. I'll add operator overloads or structs (I really like Nick's idea of a fast vs cropped access point) to hide it. In the mean time though, the putPixel function forwards to the OS implementation, which writes right to the image format expected there. It's still not ultimately efficient, but it's better than copying it twice every time we make a change. I still like the idea of a set binary format being available so we can save as a file, but that will be made a conversion function instead of the default use. Actually, probably some kind of forward range. Then the file save functions can accept those ranges and translate them into .bmp or .png files as it streams in. (probably image.byScanLine would be pretty useful for this!) My HSL -> RGB converter is also in simpledisplay.d. It's actually less than 500 lines, excluding the bindings pasted in at the bottom. Not too bad. |
April 08, 2011 Re: simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | Adam D. Ruppe: > I, with input from others, have started writing a little module for simple uses of a display. You can write to a bitmap, display it to a window, and handle events all in an easy way. The basics are cross platform, but you can use native function calls too. I'd like something like this in Phobos, or if this is not possible, a cabal-install-like command away. I sometimes use Python PyGame or Processing. > import simpledisplay; > > void main() { > auto image = new Image(255, 255); > > foreach(a; 0..255) > foreach(b; 0..255) > image.putPixel(a, b, Color(a, b, ((a + b) % 16) * 16)); > > image.display(); > } Instead of: image.putPixel(a, b, Color(a, b, ((a + b) % 16) * 16)); I suggest something simpler like: image[a, b] = std.typecons.tuple(a, b, ((a + b) % 16) * 16); Instead of: image.display(); I suggest something like this, to swap the two image buffers: image.flip(); Plus maybe some image.event(); reader with callbacks... Bye, bearophile |
April 08, 2011 Re: simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | simpledisplay.d - line 267: int lol, wtf; lol, wtf? :p Btw, an exception will be thrown on unhandled key events, e.g. just hit CTRL: object.Exception@.\simpledisplay.d(299): GetMessage failed Not a good thing if you use shortcut keys to move windows around. |
April 08, 2011 Re: simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | == Quote from Adam D. Ruppe (destructionator@gmail.com)'s article
> We discussed this first in the GUI library thread, but since it
> meandered so much, I decided to split off into a new subject. Much
> of what I say here will be old to anyone who saw the previous thread.
> There's some new stuff nearer to the bottom though.
> I, with input from others, have started writing a little module
> for simple uses of a display. You can write to a bitmap, display it
> to a window, and handle events all in an easy way. The basics are
> cross platform, but you can use native function calls too.
> http://arsdnet.net/dcode/simpledisplay.d
> It's still very much in progress, but I think it's now at the point
> where the direction I'm taking it is clear.
Can it render text? If so, I'll probably port Plot2kill to it if it becomes part of Phobos at some point. It would be kind of cool to have minimal Plot2kill functionality with zero third-party dependencies. If it's so basic that it can't even do text rendering, then I have my doubts about whether there are very many use cases for something so simple.
|
April 09, 2011 Re: simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | bearophile wrote: > I'd like something like this in Phobos Me too. It's still pretty far from good enough right now, but that's where I want ultimately want it. My only concern is I don't want Phobos to depend on Xlib unless the module is actually imported. I don't think it will be a problem, but if it means a hello world won't run on a text only machine, that won't be ok. > I suggest something simpler like: Yeah, moving to opIndex is something I plan to do. But, I don't really like using a tuple for this - a regular struct makes it a little clearer what's going on, and can be made more efficient. (In the other thread, Nick suggested making it a simple uint internally, using property functions to emulate other outside faces. I like that.) A struct might also have accessors with different color types too. > I suggest something like this, to swap the two image buffers That's not really what you're doing there - display actually pops up a new window and waits for the user to close it. > Plus maybe some image.event(); reader with callbacks... What would it do? |
April 09, 2011 Re: simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
Posted in reply to dsimcha | dsimcha wrote:
> Can it render text?
Not yet, but it's on the list. Anything that's reasonably easy
in both Windows API and Xlib should be supported here. At the least,
text, lines, rectangles - all the basics.
|
April 09, 2011 Re: simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | Andrej Mitrovic: > lol, wtf? :p My brilliant variable names! It reflects my feelings toward parts of the bmp format I forgot about when first writing it. I didn't account for the padding at first, then said "wtf" and added it... then "lol"'ed at myself for not doing it right the first time. You should have saw the original C version though: if(hbmp == NULL) goto fuck; :) > Btw, an exception will be thrown on unhandled key events, e.g. just hit CTRL: That's weird, I've never seen GetMessage fail... ever. I'll have to try it in real Windows later (I'm on linux / Wine right now). |
April 09, 2011 Re: simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | Adam D. Ruppe: > My only concern is I don't want Phobos to depend on Xlib unless > the module is actually imported. I don't think it will be a problem, > but if it means a hello world won't run on a text only machine, that > won't be ok. I agree. > But, I don't > really like using a tuple for this - a regular struct makes it > a little clearer what's going on, and can be made more efficient. OK. (But for this module I think usage simplicity is more important than raw speed. Users that need more performance or more features are going to use something else and better. The idea here is to keep simple the things that are simple). > (In the other thread, Nick suggested making it a simple uint internally, using property functions to emulate other outside faces. I like that.) OK. > > I suggest something like this, to swap the two image buffers > > That's not really what you're doing there - display actually pops up a new window and waits for the user to close it. Indeed, I was suggesting to open the window at the top of the program, and at the bottom of the program to swap the two buffers and show the drawn one. > > Plus maybe some image.event(); reader with callbacks... > > What would it do? Read the events, like window closing, key pressed, mouse movements, etc. Take a look at Processing API, for ideas. There are many situations where a simple built-in graphics is useful. Beside the example of Processing (that has anti-aliasing), there are many programs/small games that need just some 2D graphics: http://rosettacode.org/wiki/Animate_a_pendulum Bye, bearophile |
April 09, 2011 Re: simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
Posted in reply to bearophile | bearophile 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, I was suggesting to open the window at the top of the program, and at the bottom of the program to swap the two buffers and show the drawn one. Sure, that's what happens in my second example. Right now it uses "window.image = newImage;" to flip it but I think I want to change that. > Read the events, like window closing, key pressed, mouse movements, etc. Take a look at Processing API, for ideas. Yes, my window object does this too. See the example here: http://arsdnet.net/dcode/simpledisplay_test2.d win.eventLoop() does it. > http://rosettacode.org/wiki/Animate_a_pendulum Heh, I think D will be winning that by Monday! |
April 09, 2011 Re: simple display (from: GUI library for D) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | Adam D. Ruppe: > The struct is at least equal in simplicity: > > image[x, y] = Color(r, g, b); > > vs > > image[x, y] = tuple(r, g, b); A tuple is simpler, there is no new name to remember and use, new type to define, and 3-tuples come out of other generic computations, like zip: foreach (col; zip(reds, greens, blues)) image[x, y] = col; Generally D programmers probably need to learn to use tuples a bit more seriously and often :-) > Yes, my window object does this too. See the example here: http://arsdnet.net/dcode/simpledisplay_test2.d > > win.eventLoop() does it. I see. But those lambdas are a bit too much large put there. I suggest true named function moved elsewhere. Some other notes to your code: - generic imports like stdio are probably better before the most specific ones (this is done in well written Python code); - Inside the (dchar c) {} a switch seems better. - In Wrapped struct why are you using a checkLimits() instead of an invariant()? > > http://rosettacode.org/wiki/Animate_a_pendulum > > Heh, I think D will be winning that by Monday! Good, but Rosettacode site isn't a competition :-) Bye, bearophile |
Copyright © 1999-2021 by the D Language Foundation