GetBitmapBits ()

By Shamus Posted Friday Oct 24, 2008

Filed under: Projects 56 comments

This is just venting about some project annoyances that I needed to get off my chest. No gaming news or rants about DRM today. Read on to be bored by trivialities…

I have no less than a week’s worth of posts sitting in the queue, in much the same way that wet leaves might sit in your downspout, preventing efficient operation. All of them need that once-over in different places to weed out different places where I re-use words and the bad sentence structure in different places.

Next week I’m going to put up my long-delayed survival horror posts up for Halloween, so some of these posts will be stale by the time we reach them. Which do you prefer, unfinished, or irrelevant? No worries, I can do both!

I should have spent tonight (Thursday) on Friday’s noontime post, but instead I got caught up in a side project…

I’ve mentioned elsewhere that I build my comics using a homebrew program I came up with. It lets me throw down word bubbles and drag the tails around and generally do stuff that would be tedious and time consuming if done by hand.

But for months I’ve been irritated at how long it takes to update a bubble. When I type text into the dialog box, it takes a full second for the program to rebuild the bubble. This makes editing really annoying. At the heart of the problem are the Windows API calls I’m making. I draw the text into an off-screen Bitmap using common Windows text-drawing calls. The same ones used to draw the tooltips and menus in front of you right now if you’re on a Windows machine. But for whatever perverse reason, Windows takes preposterous amount of time to give me the data.

It goes like this:

Me: Hey Windows. You know that bitmap with all the words I just drew?

Windows: Oh yeah. Got it right here. You want me to save that to disk? Or maybe I should draw it on screen for you? Or maybe-

Me: Actually, could you just give it to me?

Windows: You… what? What do you want it for? I can save it or draw it. What else could you possibly…

Me: Please? Just hand over the raw data. I know what I’m doing. Just let me GetBitmapBits ().

Windows: Ok. Geeze. Hang tight. Give me some time to get all that together for you.

Me: But I just need access to data you’ve already got. You could just give me a pointer to…

Windows: Ngh. Huff huff huff. Oh this hurts.

Me: What?

Windows: Oh geeze. Here… it… comes…

Me: We’re talking about 256k of data here. That’s peanuts. This is ridiculous…

Windows: Gasp! Whew. That was a toughie. Here you go. Please don’t ask me to do that again.

Me: Actually, you took so long on that that the user has typed another letter by now. We’ve gotta do it all again.

Windows: Okay. Just give me a second to psyche myself up for this.

Me: Do you understand?!? YOU ARE OPERATING SLOWER THAN A HUMAN BEING CAN TYPE! This is NOT 1987! Pull yourself together!

Windows: Ngh. Huff huff huff. Oh this hurts.

I realized that I could do the job in under a millisecond (about 200 to 500 times faster, depending on the size of the bubble) if I dump the Windows crap and do all my font drawing in OpenGL. (Which is what I’m using to render the comics.) I can eliminate this entire mess by simply drawing into a texture map. The only cost is that I have to re-write all the font-drawing code myself.

This is a terrible time to do something like this, when I’m already behind on my webcomic and I have posts that need my attention. But inspiration (which in this case looks a lot like exasperation) is a capricious master and there’s no use in trying to write jokes when my mind wants to write code.

My program takes text and arranges it in an oval of variable size and aspect. (Never taller than it is wide.) The aesthetic arranging of words in a circular region is insanely complex. This is a job usually done by some sort of “artist” who uses a lot of “intuition”, which is just the sort of thing you want to avoid trying to turn into code.

The only two things I needed from windows is for it to tell me how big a word was (how big of a rectangle it occupies) and for it to draw that word. My program is experimenting with hundreds and hundreds of layouts before it finds just the right one, and then calculates how tight it needs to make the oval to contain the contents. It’s doing all of this complex stuff and then gags on blitting a medium-sized block of data, which should, in a sane world, be so fast I should have trouble measuring it with my millisecond-resolution timer.

I shudder to imagine what madness Windows must be working behind the scenes for it to spend that much time on it.

This is classic programmer behavior: To spend two hours writing code so that I can save a half-second per letter. Why, after 14,400 letters I’ll have saved enough time to justify the work I put into it! (It’s not so much the time that’s motivating me anyway. It’s the desire to Do It Right.)

And I realize I just blew even more time typing this up, but if you’re a programmer you’ll understand why I needed to do this.

 


From The Archives:
 

56 thoughts on “GetBitmapBits ()

  1. Jeremiah says:

    And I realize I just blew even more time typing this up, but if you're a programmer or engineer you'll understand why I needed to do this.

    Fixed :)

  2. Kel'Thuzad says:

    Well… that went quite far above my head.

  3. Merle says:

    If it can’t be done Right, it shouldn’t be done at all.
    Good luck! And jeebus, but that reflects badly on Windows. What the heck is it DOING, sending each bit parcel post?

  4. LintMan says:

    I totally understand – but I’d probably also blow 5-6 hours trying to understand and/or fix why the current way was so slow before spending the two hours to fix it the different way. So then just 57600 character later, I’d be breaking even!

  5. Robert says:

    At an average word length of six letters, that’s 2400 words… Doesn’t sound like a lot if you’re writing a paper, but for caption text, you’re probably going to realize a net gain sometime in 2015…

    :-)

  6. Tim Skirvin says:

    The thing is, our entire careers are based around the fact that we’re willing to spend a week fixing something that wastes us ten seconds a day. It takes a while for it to pay off, sure, but soon we’ve saved a little time here, a little time there… and usually it’s all actual *working* time. I think of it like starting a new city in Civ IV; it’ll be an annoying drain on my resources for a while, but it’ll eventually pump out units and research for me.

  7. Rubes says:

    Wouldn’t it be easier to just use something like Comic Life?

    Of course, I’m guessing that someone who’s dealing with bitmaps, Windows API, and OpenGL probably has needs greater than this program can fill. Still, it’s a really cool program.

  8. henebry says:

    Yeah, I’d actually been assuming that you were using Comic Life (or some Windows equivalent).

    We mac people are so used to windows people telling us how much more great software is available for windows that we tend to assume that, if I have it as an option on my Mac, you must have 10 options with similar feature sets on Windows.

    And, as Rubes’ post makes clear, ComicLife is indeed available for Widows.

  9. briatx says:

    Windows: Oh yeah. Got it right here. You want me to save that to disk? Or maybe I should draw it on screen for you? Or maybe-

    You make it sound like Windows *wants* to help you. Doubtful.

  10. King of Men says:

    It’s not just about saving time. It’s about showing the dang computer who’s boss. They are our slaves, and by God they’ll stay that way as long as shit like this keeps happening!

    Now post me this comment, or I’ll fire up that program with all the segfaults again. Schnell!

  11. mephane says:

    “And I realize I just blew even more time typing this up, but if you're a programmer you'll understand why I needed to do this.” So true… so true.

  12. Shinjin says:

    Have you considered triggering the crappy API call only after N seconds of time has passed between keypresses? Would adding support copy/paste help?

  13. Shamus says:

    Shinjin: I considered a cooldown timer, but I decided to just Fix It.

    It actually does support copy / paste. I write comics in notepad and then drop the text into bubbles. Still, when I do have to edit text (I’m always trimming for excess verbosity) it can get pretty yucky.

  14. Shamus says:

    Comic Life for windows is new – it was Mac-only for a long time. I used Comic Book Creator for all but the last few DMotR comics. Both it and Comic life are template-based, and neither one appears to support bubble clipping (not letting the edges of a bubble reach outside the panel it occupies) and they have annoying limits on how you can chain bubbles together. All of those are pretty crucial when dealing with immutable space constraints like I do.

  15. Rubes says:

    Shamus…not to belabor the point, but Comic Life lets you use templates if you want — but you can place separate, individual panels however you desire. Each panel also does support bubble clipping (place a bubble, right-click, and select “clip”) — not to mention image clipping. Can’t say much about chaining bubbles together, though; I know it works, but I don’t know what your requirements are.

  16. Gary says:

    This is why I do ALL of my webcomic in one graphics program. I personally use GIMP because I am poor and cheap. (I’d switch to Photoshop in a heartbeat if I could find a cheap version) But I find it much easier to insert text, align text, and then create my own bubbles using the circle select/stroke tools. It goes very quickly and overlapping bubbles is never a problem.

  17. Vegedus says:

    “This is classic programmer behavior: To spend two hours writing code so that I can save a half-second per letter.”

    I’m not a programmer (yet), but I do know the feeling. Many tasks at a computer in general, such as customising the interface or finding specific programs for specific tasks instead of using the defaults are a bit time consuming, without much pay-off. But somehow, it’s worse to endure 30 times ten seconds of wait time than it is to spend half an hour fixing it.

  18. krellen says:

    This is why I’m no longer a programmer.

  19. Cuthalion says:

    As a hobbyist programmer, I know how you feel.

  20. K says:

    I’m impressed that you write your own software to do a comic. Then again, I am a Software Engineer and it does not really surprise me on second thought. And I would be astonished if you had not changed that thing, even if it took way too much time. Really, there is no replacement for proper code.

  21. Lonster says:

    …and, this is why I love working with computers.

    They ALWAYS do what you tell them to. You might not be telling them what you think you’re telling them, but that’s what they’re gonna do!

  22. Carra says:

    I had the same problem while trying to create a little C# program that draws fractals. The image would take bloody minutes to render and was useless at a full-screen resolution. Apparently it’s a common problem…

    I fixed it by using a “FastBitMap”.

    http://blogs.msdn.com/windowsmobile/archive/2008/04/15/faster-c.aspx gives a general idea. It basically forces you to use dirty code to “lock the bitmap” and get the job done.

  23. Scott says:

    We don’t have downspouts (or deciduous trees to drop leaves into them) here in sunny Southern California. Your analogy is strange and foreign to me.

  24. Illiterate says:

    In a previous life I did support for mobile devices. I remember a two-hour support call with a gentleman trying to fix something like this, trvial yet irritating.

    I had a rule in those days. The challenge in fixing a problem is inversely proportional to it’s severity.

    “My phone won’t turn on.”
    “Ok, let’s pull and re-set the battery, test if it’ll turn on without the battery in it, go through the motions of a factory default even though I know that won’t fix it, and then we’ll setup a replacement as long as you don’t admit to having water damaged it.”
    “Oh, water damage isn’t covered? Um.. Let’s do this later.”

    “On every other thursday for fifteen minutes after noon my email won’t come in automatically. It doesn’t happen to anyone else in the office, and the email works when I do a manual send-receive. We put another phone on my account and it worked ok. Can you fix this?”
    “…..”

    The guy I spent two hours with, I think we eventually cracked that nut. He had a great line “I’ve spent days saving minutes lately.”

  25. Merle says:

    @Krellen: No such thing! A programmer is a programmer is a programmer.

    (And now I’m wondering if my brain just used a recursive routine to think that. Reading Stross is bad for me…)

  26. Loneduck3 says:

    To quote Homestar Runner (via their legal page) “Booooring.” Anyway, sorry that Windows is probably programmed sloppily.

  27. MadTinkerer says:

    Merle, King of Men, Vegedus, and K are right on the money.

    Even before I was capable of writing code, I expected my computer to do as I want it to. If it doesn’t, I tweak and tweak until it does.

    When it’s my program, I write and rewrite until I give up on it as a lost cause or until it’s something I wouldn’t mind Jesus using. There are exceptions, but only for truly minor things I won’t use much at all. When I write code, it’s perfection or death, with no room for compromise.

    This includes minor class assignments like printing out ASCII snowmen.

    Speaking of which, I need to get back to working on my Awesome Game Engine mk I.

  28. Kevin says:

    Yeah… I’m using Comic Life, and it pretty much does all of that for me. I can tell though that you went to a lot of trouble to create the system you have, so I don’t think it’d be right for me to compare it to what I’m using, even if it works really well, since it just came free with my computer.

    There is a Pro version of Comic Life called Magiq out now that is supposed to add a great deal of functionality and customization. (Don’t know about bubble clipping. I might look it up.) I’ve been cool with what I have, so I never really looked into it too much. Hang on and I’ll check.

    Okay, lots of cool stuff, but I didn’t see that. (Of course I may buy it now!)

  29. Luke Maciak says:

    Been there, done that! I totally know the feeling.

    I especially love when the Fix quietly breaks 2 other things but you don’t know about it until 2 days later cause it’s just a home-grown app and it’s not like you do rigorous testing or code review on it or anything. Fun times. :)

    Out of curiosity, what language do you usually work in?

    I write comics in notepad and then drop the text into bubbles.

    Notepad? :( Really? Like regular notepad that comes with windows? Most programmers I know replace it with something nicer. I’m a Vim person myself, but I know a lot Windows people who use Notepad+, Edit+, TextPad and etc. It’s just Notepad is so bleh.

  30. SEGEEK says:

    Ouch, I just recently had to write some software for custom font rendering / layout as a test application for work (Testing if OCRs can read text / barcodes if they get tiny or start to encroach on one another). It wound up being pretty slick, could drag the text around on the display on screen and then it would render everything again at whatever the desired DPI is.

    I started with a piece of MFC crap that some other developer did before and thankfully I was allowed to write it from scratch in WinForms with C# so it wound up not being too bad. If it takes windows that long to return the data I would just keep a cached copy of it somewhere (although I don’t know how difficult it is to do that with your current API). GDI should handle it fine, but when you said “pointer” earlier I am guessing its a C app.

    If you are considering re-writing part of that code and are planning on doing it in C# (or just GDI ) let me know and I can tell you about a couple pitfalls I ran into (i.e. which font engine measures text size correctly, how to account for clear space around a font (MSDN flat out is wrong on some font metrics articles) (oh yeah and nested parenthesis FTW!))

  31. Kilmor says:

    If I understand the problem correctly…have you considered rendering the text yourself with something like Freetype ?

  32. Mari says:

    That’s not because you’re a programmer, it’s because you’re a perfectionist. Of course, most programmers are perfectionists. The ones that aren’t go to work for Microsoft. But perfectionism knows no professional boundaries.

  33. Cuthalion says:

    @henebry “And, as Rubes' post makes clear, ComicLife is indeed available for Widows”

    It’s nice to know that they’re cared for….

  34. Matt says:

    Since the API is a deprecated/compatibility API, I wonder if GetDIBits would be faster.

  35. UtopiaV1 says:

    As a fellow programmer (C++, DirectX and Java, among others) I totally understand…

    Nerds will do anything for prestige, and you my friend are prestiged!

  36. Shamus says:

    Luke Maciak:

    I’m pretty old-school. Cplusplus , with MS Dev Studio 6. I think that turns 10 this year.

    And by “notepad” I mean: ConTEXT for scripting (PHP HTML, etc) and AbiWord when I need spllechekr.

  37. Claire says:

    God, Abiword. Damn thing crashed on me mid-save in 2004 and ate the paper I was planning to use to apply to grad school… and this was of course the incident that imbued me with the necessary paranoia to create backups in different locations, so I got the pleasure of pounding the whole thing out again from memory.

    Fortunately, the re-writing went relatively quickly, and it was probably a better paper for having sprung fully formed from my mind than it was before (no doubt a messy, disorganized agglomeration of crap brutalized by countless edits.) But, still. Bad Abiword. Don’t eat my papers.

  38. Bobby says:

    Are you really explicitly calling something like GetBitmapBits(), or is this what you surmise may be happening behind the scenes? Because it’s really not a function that’s meant to be called too often, you should use the device context (or current equivalent, my GDI experience is a bit rusty) and GDI operations to draw stuff.

  39. Binks says:

    ConTEXT all the way, that’s what I use for coding.

    I’m seriously curious as to what windows could be doing in the background to make it that slow, you have a pretty good computer I’m assuming (from the fact that you can plan CoH which lags a little on my machine) so it would have to be either doing a tremendous amount of work (how much can you do on 256Kb of data? Even if you clone it completely before returning and test each byte for consistency it shouldn’t take that long, and that would be bad practice in and of itself) or a small amount of work with a small amount of power. Have you checked how much processing power your API calls are using? Could be that it’s intentionally limiting the processing power used by the API call for some absurd reason.

    And I totally know the feeling :P

  40. ryanlb says:

    Crimson Editor for all your text and programming needs, built-in spellchecker, word wrap (or not), built-in syntax highlighting for many languages, easy-peasy editing of syntax highlighting, very lightweight, ftp based remote editing, optional bash-like key commands (single biggest plus for me)

    I used emacs for 5-ish years, learned elisp to customize it to my heart’s content, I could do anything and everything with emacs, but I dumped after only a few days of trying Crimson Editor.

    As for your strange personality quirk, it’s gotta be a programmer/engineer trait. I’ve spent much more time trying to improve my ability to work on a project then I’ll ever spend actually working on the project. Or code reformatting, I can be pretty uptight about that.

  41. RichardB says:

    Shamus: programmer to programmer, the reason GetBitmapBits() is so slow because it’s a deprecated Win16 API that under Win32’s GDI has to do an expensive set-up and tear-down of “compatible” device contexts and bitmaps each time it’s called, in order to cater properly for device independence. By maintaining and re-using your own device contexts and bitmaps you can cut this overhead out and regain bit-blitting speed.

    I’ll mail you with some pointers rather than boring the whole list.

    PS Another vote for Notepad plus-plus here.

  42. Bryan says:

    Ah — I figured it was horrifically slow because the “please render this font” operation wrote out to video RAM, and it’s faster to copy video RAM to the framebuffer than to copy system RAM to the framebuffer. (So a subsequent “please draw this bitmap on the screen” would be a lot faster.) If this was true, then the “please give me the bits back” operation had to read from video RAM. And video RAM is *horrifically* slow to read from.

    But if it’s setting up and tearing down a compatible DC every time it’s called, that would also explain it being so slow. Since this is a 16-bit compatibility function, I bet that’s actually what’s happening.

    And actually, now I see a flaw with my “it’s stored in video RAM” explanation — if that was true, then saving the file out would be just as slow as getting the bitmap, because it would have to be read from video RAM and then written to disk. (Well, written to the FS cache hopefully, so it wouldn’t actually involve the disk until later — because if it did involve the disk, that would make it even slower. But in any case, it wouldn’t be any faster than a “please give me the bits” operation.)

  43. RichardB says:

    @42: agreed. From the MSDN entry for GetBitmapBits():

    Note This function is provided only for compatibility with 16-bit versions of Windows. Applications should use the GetDIBits function.

    Which is a bit too terse. There are other things you can do besides GetDIBits().

    Anyhow, I’ve emailed Shamus my brain dump and I hereby give him permission quote it in here as he sees fit. I didn’t want to go into full-on Win32 coding mode in here; it’s a gaming blog after all.

    Edit: on re-reading, noticed Bobby saw this first in #38 so he gets the trophy and Malibu timeshare…

    Edit 2: Talk about scratching my programmer itch, Shamus, it’s 3:44am here and I’m still typing. Going to bed NOW!

  44. Sharpie says:

    You imply that Windows is inefficient? Blasphemer!

  45. Dix says:

    So basically you have written a program that puts bubbles on images, or is the image editing done elsewhere? Either way, I eye this space for possible sign of a release. >.>

  46. Veylon says:

    I know how this is. Fiddling with all those dang Services to make Windows run some tiny percentage faster…

    But still, I get a warm glow when something works better because of something I did.

  47. Jabor says:

    We feel your pain, Shamus.

    I find it hard to sleep at night if I know that somewhere, a piece of code I’ve written is Not Right.

  48. Bobby says:

    @Sharpie: Windows can actually be surprisingly efficient provided you let it do its thing, which GetBitmapBits doesn’t.

    It’s because it has to contend with so many drivers and proprietary stuff, you see, but the user shouldn’t have to bother about that. Because of that everything is more or less heavily astracted and discourages direct access to the hardware but behind the scenes the calls can actually be driver-optimized for each graphics card, like using managed texture surfaces for bitmaps and built-in polygon drawing or blitting capabilities. So the key is to use device contexts and standardized GDI functions like BitBlt or drawText or whatever they’re called these days.

    GetBitmapBits is slow because it essentially locks a resource that may be in whatever format the actual video device needs and “translates” it in a standard format intelligible by the user and a memory adress accessible to him, then translates it back in one of the video device’s format when you send it the other way. It’s the kind of stuff you only really should use when loading your resources because you don’t do it often enough for its lack of speed to be a problem. But it’s not meant for repeated access, for that there’s the standardized functions, which don’t need to translate anything for the user. In this case normally Shamus shouldn’t need to access the bits, there should be a text drawing function somewhere that just takes the device context (the abstract handle to said bits) as a parameter. That is, if my memory doesn’t fail me.

  49. Petr says:

    Hi Shamus, there is the CreateDIBSection Windows API which lets you create a bitmap handle and also gives you a pointer to the bitmap’s data. Happy coding :)

  50. EvilGod says:

    Maybe that will explain your problem ;)

    http://www.irregularwebcomic.net/288.html

    no solution though…

  51. Fieari says:

    I personally use TextPad, which runs fast and without a speck of lag, which is more than I can say for some other text editors I’ve tried. Unfortunately, its grep doesn’t support newlines, which I need it to do with shocking regularity. Anyone know of a text editor whose grep has \n support?

  52. kat says:

    I use ComicLife for my comic, and I like it, but at the level of control you’re talking about I suspect it would only annoy you more with its tricks. There’s a point of expertise at which the automatic response becomes “Screw this, it’ll be easier to write my own.” The rest of us will watch you in… um… envy! Yeah! This is envy!

    However, if you’re doing much of anything at all on Windows besides using it, you have my undying respect. I find Windows helpful rather in the way Golden Retrievers are helpful: they’re cute in a dopey kind of way, and you know they *think* they’re doing the right thing, but really, it’s just another dead animal on the carpet.

  53. Terrible says:

    Makes sense to me.

  54. lowlymarine says:

    Heh, I’ve mercifully avoided ever having to use Windows-specific calls in my programs (though it hasn’t stopped Windows from chuffing up a few C programs that work perfectly in *nix), but I probably can’t dodge the bullet forever. And given my history with MS products, I’m sure everything’ll be peachy when the time comes.

  55. ryanlb says:

    I hate to ask this question here for a couple of reasons (it might not be seen, it’s not terribly relevant to this post or this site), but since it is tangentially related I’ll ask it anyway.

    Based on the several recommendations in these comments I’m trying out Notepad++, and while I’ve been using Crimson Editor for 3 years and loving it, I’m already almost ready to switch to N++, but it has one glaring shortcoming, no built-in JSP support. Since JSP/Struts is 50% of my job that’s a pretty big issue.

    Does anyone here already have a User Language Definition for JSP so I don’t have to build my own from scratch? I can, and I will if I have to (I expect to have to add Struts support even if I do find a JSP definition), but it would be nice to have a starting place.

    Feel free to contact me through ryan at ryanlb dot com so we don’t derail these comments more than they already have been.

    Much thanks to all (and to Shamus, feel free to delete this if it’s too off topic).

  56. stringycustard says:

    Why not try Adobe AIR? I use it to knock up quick prototypes for apps. But you can do “proper” stuff in it too in the barely code-like world it inhabits (barely because it uses the worst implementation of c-like structure you ever did see, with things like variable scope just not working the way every other language works it).

    Anyway, fast to dev stuff up in it, basically a straightfoward drop it in gui system. But the new code engine’ll let you do more intense things.

    Go on, try it, I can’t wait for yet another person to discover just how awful the compiler is, i mean awesome, yes awesome. C’mon.

    But really, it is very simple and quick to use – and it was designed specifically for graphical stuff.

Thanks for joining the discussion. Be nice, don't post angry, and enjoy yourself. This is supposed to be fun. Your email address will not be published. Required fields are marked*

You can enclose spoilers in <strike> tags like so:
<strike>Darth Vader is Luke's father!</strike>

You can make things italics like this:
Can you imagine having Darth Vader as your <i>father</i>?

You can make things bold like this:
I'm <b>very</b> glad Darth Vader isn't my father.

You can make links like this:
I'm reading about <a href="http://en.wikipedia.org/wiki/Darth_Vader">Darth Vader</a> on Wikipedia!

You can quote someone like this:
Darth Vader said <blockquote>Luke, I am your father.</blockquote>

Leave a Reply to kat Cancel reply

Your email address will not be published.