Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. Other Discussions
  3. Clever Code
  4. Strange performance issues with large bitmap brushes

Strange performance issues with large bitmap brushes

Scheduled Pinned Locked Moved Clever Code
performancegraphicsregexquestion
11 Posts 3 Posters 5 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P Offline
    P Offline
    Paul Sanders the other one
    wrote on last edited by
    #1

    Here's a good one:

    HBRUSH hPatternBrush = CreatePatternBrush (hBitmap); // where hBitmap is large

    ...

    OnPaint (HDC hDC)
    {
    FillRect (hDC, &r, hPatternBrush);
    }

    The FillRect function runs 2-3 times faster (with large bitmaps only) if you use the pattern brush to paint into a memory DC before you do anything else with it. Weird or what? Same behaviour observed on both XP and Vista (without Aero, haven't tried it with), on two PC's with very different graphic cards. Had me going for a while, I can tell you. Hope this helps someone some day.

    Paul Sanders http://www.alpinesoft.co.uk

    G 1 Reply Last reply
    0
    • P Paul Sanders the other one

      Here's a good one:

      HBRUSH hPatternBrush = CreatePatternBrush (hBitmap); // where hBitmap is large

      ...

      OnPaint (HDC hDC)
      {
      FillRect (hDC, &r, hPatternBrush);
      }

      The FillRect function runs 2-3 times faster (with large bitmaps only) if you use the pattern brush to paint into a memory DC before you do anything else with it. Weird or what? Same behaviour observed on both XP and Vista (without Aero, haven't tried it with), on two PC's with very different graphic cards. Had me going for a while, I can tell you. Hope this helps someone some day.

      Paul Sanders http://www.alpinesoft.co.uk

      G Offline
      G Offline
      Gary R Wheeler
      wrote on last edited by
      #2

      The brush is a DDB (device dependent bitmap), I believe. If your original bitmap was a DIB, by converting to a brush early you're eliminating the conversion step from the paint operation.

      Software Zen: delete this;
      Fold With Us![^]

      P 1 Reply Last reply
      0
      • G Gary R Wheeler

        The brush is a DDB (device dependent bitmap), I believe. If your original bitmap was a DIB, by converting to a brush early you're eliminating the conversion step from the paint operation.

        Software Zen: delete this;
        Fold With Us![^]

        P Offline
        P Offline
        Paul Sanders the other one
        wrote on last edited by
        #3

        Nope, tried that. And a lot of other things, 'cos I couldn't believe what I was seeing. Good idea though, thanks for posting. I'm not sure exactly what goes on behind the scenes when you pass an hBitmap to CreatePatternBrush, but when I passed in a DDB in the 'wrong' format (just to see what would happen) the brush didn't paint properly, so my guess is that the hBitmap is just copied into the brush verbatim. But there is subsequently a step called 'realization' which (I think) happens the first time you select the brush into a DC, so the answer must lie in there somewhere. You would think though that, if anything, selecting a brush into a memory DC before selecting it into a 'real' DC would be more likely to screw things up than the other thing. But evidently not. As a sidenote, I have been testing various bits of slightly sneaky, subclassing code on both XP and Vista and it is uncanny how similarly the two systems behave. Microsoft clearly did not dare to change much, although a few things did break with Aero enabled and I did find a fairly serious bug (theirs, not mine) on Vista 64 bit.

        Paul Sanders http://www.alpinesoft.co.uk

        G 1 Reply Last reply
        0
        • P Paul Sanders the other one

          Nope, tried that. And a lot of other things, 'cos I couldn't believe what I was seeing. Good idea though, thanks for posting. I'm not sure exactly what goes on behind the scenes when you pass an hBitmap to CreatePatternBrush, but when I passed in a DDB in the 'wrong' format (just to see what would happen) the brush didn't paint properly, so my guess is that the hBitmap is just copied into the brush verbatim. But there is subsequently a step called 'realization' which (I think) happens the first time you select the brush into a DC, so the answer must lie in there somewhere. You would think though that, if anything, selecting a brush into a memory DC before selecting it into a 'real' DC would be more likely to screw things up than the other thing. But evidently not. As a sidenote, I have been testing various bits of slightly sneaky, subclassing code on both XP and Vista and it is uncanny how similarly the two systems behave. Microsoft clearly did not dare to change much, although a few things did break with Aero enabled and I did find a fairly serious bug (theirs, not mine) on Vista 64 bit.

          Paul Sanders http://www.alpinesoft.co.uk

          G Offline
          G Offline
          Gary R Wheeler
          wrote on last edited by
          #4

          Paul Sanders (AlpineSoft) wrote:

          realization

          I bet that's it. That includes a palette conversion step, which could be computationally intensive.

          Paul Sanders (AlpineSoft) wrote:

          selecting a brush into a memory DC before selecting it into a 'real' DC would be more likely to screw things up

          I don't think so. I've got a controls library that uses double-buffering with memory DC's quite a bit, and I've never had an issue with the memory DC behaving differently than drawing directly. My usual practice is to develop the control using direct drawing first, and then switch to double-buffered memory DC drawing when it's done. Of course, now that I'm switching to C# and WPF, I don't have to worry about drawing issues any more :rolleyes: ...

          Software Zen: delete this;
          Fold With Us![^]

          P 1 Reply Last reply
          0
          • G Gary R Wheeler

            Paul Sanders (AlpineSoft) wrote:

            realization

            I bet that's it. That includes a palette conversion step, which could be computationally intensive.

            Paul Sanders (AlpineSoft) wrote:

            selecting a brush into a memory DC before selecting it into a 'real' DC would be more likely to screw things up

            I don't think so. I've got a controls library that uses double-buffering with memory DC's quite a bit, and I've never had an issue with the memory DC behaving differently than drawing directly. My usual practice is to develop the control using direct drawing first, and then switch to double-buffered memory DC drawing when it's done. Of course, now that I'm switching to C# and WPF, I don't have to worry about drawing issues any more :rolleyes: ...

            Software Zen: delete this;
            Fold With Us![^]

            P Offline
            P Offline
            Paul Sanders the other one
            wrote on last edited by
            #5

            Gary R. Wheeler wrote:

            I don't think so. I've got a controls library that uses double-buffering with memory DC's quite a bit, and I've never had an issue with the memory DC behaving differently than drawing directly. My usual practice is to develop the control using direct drawing first, and then switch to double-buffered memory DC drawing when it's done.

            Well, it *behaves* the same sure enough, but drawing performance (FillRect, and PatBlt) is markedly different. It's clear from observed behaviour that the brush 'remembers' something the first time it is selected into a DC. It only kicks in if you use a large patterned brush (the size of the window, in my case, to get a pretty gradient fill) to fill a large area (the window background). What I don't get is what and why. Maybe it is some kind of alignment issue of the bitmap data in memory. Some byte-wise copies go faster when aligned on an 8-byte boundary, but I'm clutching at straws here. Is WPF more programmer-friendly than Win32? I have no experience of it at all. My main complaint about Win32 is the various shortcomings of the common controls. A combination of GDI and GDI+ is pretty effective unless perhaps you want to draw partially see-through objects or 3D gizmos.

            Paul Sanders http://www.alpinesoft.co.uk

            G 1 Reply Last reply
            0
            • P Paul Sanders the other one

              Gary R. Wheeler wrote:

              I don't think so. I've got a controls library that uses double-buffering with memory DC's quite a bit, and I've never had an issue with the memory DC behaving differently than drawing directly. My usual practice is to develop the control using direct drawing first, and then switch to double-buffered memory DC drawing when it's done.

              Well, it *behaves* the same sure enough, but drawing performance (FillRect, and PatBlt) is markedly different. It's clear from observed behaviour that the brush 'remembers' something the first time it is selected into a DC. It only kicks in if you use a large patterned brush (the size of the window, in my case, to get a pretty gradient fill) to fill a large area (the window background). What I don't get is what and why. Maybe it is some kind of alignment issue of the bitmap data in memory. Some byte-wise copies go faster when aligned on an 8-byte boundary, but I'm clutching at straws here. Is WPF more programmer-friendly than Win32? I have no experience of it at all. My main complaint about Win32 is the various shortcomings of the common controls. A combination of GDI and GDI+ is pretty effective unless perhaps you want to draw partially see-through objects or 3D gizmos.

              Paul Sanders http://www.alpinesoft.co.uk

              G Offline
              G Offline
              Gary R Wheeler
              wrote on last edited by
              #6

              Paul Sanders (AlpineSoft) wrote:

              the brush 'remembers' something the first time it is selected into a DC

              That sounds likely. Something's getting cached that really effects performance for larger bitmaps. Since "large bitmap brush" support wasn't added until later (Win32), maybe there are some optimizations underneath.

              Paul Sanders (AlpineSoft) wrote:

              Is WPF more programmer-friendly than Win32? I have no experience of it at all.

              I don't know yet; I'm just getting started with it.

              Paul Sanders (AlpineSoft) wrote:

              A combination of GDI and GDI+ is pretty effective unless perhaps you want to draw partially see-through objects

              That's precisely what I was doing on a contracting job last summer. I never did get the drawing speed up to what I wanted; the flicker was obnoxious. If I would have had more time (or the customer more money), I probably could have figured it out, but it was a small job and the performance thing wasn't a big issue for them.

              Software Zen: delete this;
              Fold With Us![^]

              P 1 Reply Last reply
              0
              • G Gary R Wheeler

                Paul Sanders (AlpineSoft) wrote:

                the brush 'remembers' something the first time it is selected into a DC

                That sounds likely. Something's getting cached that really effects performance for larger bitmaps. Since "large bitmap brush" support wasn't added until later (Win32), maybe there are some optimizations underneath.

                Paul Sanders (AlpineSoft) wrote:

                Is WPF more programmer-friendly than Win32? I have no experience of it at all.

                I don't know yet; I'm just getting started with it.

                Paul Sanders (AlpineSoft) wrote:

                A combination of GDI and GDI+ is pretty effective unless perhaps you want to draw partially see-through objects

                That's precisely what I was doing on a contracting job last summer. I never did get the drawing speed up to what I wanted; the flicker was obnoxious. If I would have had more time (or the customer more money), I probably could have figured it out, but it was a small job and the performance thing wasn't a big issue for them.

                Software Zen: delete this;
                Fold With Us![^]

                P Offline
                P Offline
                Paul Sanders the other one
                wrote on last edited by
                #7

                Gary R. Wheeler wrote:

                Since "large bitmap brush" support wasn't added until later (Win32), maybe there are some optimizations underneath.

                Or lack thereof!

                Gary R. Wheeler wrote:

                I never did get the drawing speed up to what I wanted; the flicker was obnoxious

                Yes, GDI / Win32 is sometimes as slow as a one-legged pig doing apparently simple things. I don't think themes help, they seem just kind of stuck on top of everything else. Flicker is most apparent in my app when resizing a window. I have a trick for eliminating it at the cost of some performance. This is probably totally irrelevant to WPF but I guess there's no harm in passing the idea on anyway. What I do is essentially this:

                void OnResize (HWND hWnd)
                {
                SendMessage (hWnd, WM_REDREAW, FALSE, 0);
                [resize all the controls in the window; nothing gets redrawn at this point]
                ValidateRect (hWnd, NULL);
                SendMessage (hWnd, WM_REDREAW, TRUE, 0);
                .
                [create a memory DC the size of the window]
                PrintWindow (hWnd, hMemDC)
                hWindowDC = GetDC (hWnd);
                BitBlt (hWindowDC, ..., hMemDC, ..., SRCCOPY);
                [clean up]

                }

                Nifty, eh? It was a fight to get it to work under Aero - there seem to be a number of subtle bugs in this area - but it was worth it. BTW, whatever happened to the WS_EX_DOUBLEBUFFER flag? There never was one of course, but then it could all be handled transparently (no pun intended) in BeginPaint and EndPaint (and all the standard controls would automatically 'do the right thing'). Just a thought. Email me (or post to this thread) about WPF sometime if you care to. I'd be interested to hear a hardcore WIN32 person's take on it. I'm sure I will take the leap sometime, probably fairly soon. I need to take a look at least.

                Paul Sanders http://www.alpinesoft.co.uk

                D 1 Reply Last reply
                0
                • P Paul Sanders the other one

                  Gary R. Wheeler wrote:

                  Since "large bitmap brush" support wasn't added until later (Win32), maybe there are some optimizations underneath.

                  Or lack thereof!

                  Gary R. Wheeler wrote:

                  I never did get the drawing speed up to what I wanted; the flicker was obnoxious

                  Yes, GDI / Win32 is sometimes as slow as a one-legged pig doing apparently simple things. I don't think themes help, they seem just kind of stuck on top of everything else. Flicker is most apparent in my app when resizing a window. I have a trick for eliminating it at the cost of some performance. This is probably totally irrelevant to WPF but I guess there's no harm in passing the idea on anyway. What I do is essentially this:

                  void OnResize (HWND hWnd)
                  {
                  SendMessage (hWnd, WM_REDREAW, FALSE, 0);
                  [resize all the controls in the window; nothing gets redrawn at this point]
                  ValidateRect (hWnd, NULL);
                  SendMessage (hWnd, WM_REDREAW, TRUE, 0);
                  .
                  [create a memory DC the size of the window]
                  PrintWindow (hWnd, hMemDC)
                  hWindowDC = GetDC (hWnd);
                  BitBlt (hWindowDC, ..., hMemDC, ..., SRCCOPY);
                  [clean up]

                  }

                  Nifty, eh? It was a fight to get it to work under Aero - there seem to be a number of subtle bugs in this area - but it was worth it. BTW, whatever happened to the WS_EX_DOUBLEBUFFER flag? There never was one of course, but then it could all be handled transparently (no pun intended) in BeginPaint and EndPaint (and all the standard controls would automatically 'do the right thing'). Just a thought. Email me (or post to this thread) about WPF sometime if you care to. I'd be interested to hear a hardcore WIN32 person's take on it. I'm sure I will take the leap sometime, probably fairly soon. I need to take a look at least.

                  Paul Sanders http://www.alpinesoft.co.uk

                  D Offline
                  D Offline
                  Dan Neely
                  wrote on last edited by
                  #8

                  Any idea how much hoop jumping would be involved in porting that to a .net app?

                  Today's lesson is brought to you by the word "niggardly". Remember kids, don't attribute to racism what can be explained by Scandinavian language roots. -- Robert Royall

                  P 1 Reply Last reply
                  0
                  • D Dan Neely

                    Any idea how much hoop jumping would be involved in porting that to a .net app?

                    Today's lesson is brought to you by the word "niggardly". Remember kids, don't attribute to racism what can be explained by Scandinavian language roots. -- Robert Royall

                    P Offline
                    P Offline
                    Paul Sanders the other one
                    wrote on last edited by
                    #9

                    I'm no .net expert, but on XP, and non-aero Vista, I would think it would be pretty straightforward. The relevant Win32 API calls are readily accessible I believe, albeit via PInvoke, and I assume you can get hold of an HWND to the window you are resizing. That *should* be all you need, but you never know. On an Aero-enabled Vista desktop it is a different story. To get it to work, you have to p*ss around with your window hierarchy. Specifically, you have to place all your controls in a window which is itself a child of the main window. Otherwise, calling PrintWindow causes Vista to redraw the window (with all the associated flicker) even in you call ValidateRect after resizing it. In my case this was not too irksome but in a .net / Windows Forms app I imagine it might be a killer. The other gotcha on Aero is that calling GetDC on a child window and then drawing into that DC does ... nothing! Strange but true. I get around this by getting a DC to the parent window and blting my 'composited' screen into that instead. This seems to work on all platforms (including Vista 64). Another, cleaner solution which I suspect would probably work would be to register your child window class with the CS_OWNDC class style, but I have not tested this as that option was not available to me. I can email you some code snippets if you are serious about this, but if you don't control your window hierarchy it's probably not going to help you much. As a programmer, doncha just *love* Vista? Actually, no! I like C# a lot though; it's what C++ should have been, pretty much. I like your sig by the way. Quite deep, in its way.

                    Paul Sanders http://www.alpinesoft.co.uk

                    D 1 Reply Last reply
                    0
                    • P Paul Sanders the other one

                      I'm no .net expert, but on XP, and non-aero Vista, I would think it would be pretty straightforward. The relevant Win32 API calls are readily accessible I believe, albeit via PInvoke, and I assume you can get hold of an HWND to the window you are resizing. That *should* be all you need, but you never know. On an Aero-enabled Vista desktop it is a different story. To get it to work, you have to p*ss around with your window hierarchy. Specifically, you have to place all your controls in a window which is itself a child of the main window. Otherwise, calling PrintWindow causes Vista to redraw the window (with all the associated flicker) even in you call ValidateRect after resizing it. In my case this was not too irksome but in a .net / Windows Forms app I imagine it might be a killer. The other gotcha on Aero is that calling GetDC on a child window and then drawing into that DC does ... nothing! Strange but true. I get around this by getting a DC to the parent window and blting my 'composited' screen into that instead. This seems to work on all platforms (including Vista 64). Another, cleaner solution which I suspect would probably work would be to register your child window class with the CS_OWNDC class style, but I have not tested this as that option was not available to me. I can email you some code snippets if you are serious about this, but if you don't control your window hierarchy it's probably not going to help you much. As a programmer, doncha just *love* Vista? Actually, no! I like C# a lot though; it's what C++ should have been, pretty much. I like your sig by the way. Quite deep, in its way.

                      Paul Sanders http://www.alpinesoft.co.uk

                      D Offline
                      D Offline
                      Dan Neely
                      wrote on last edited by
                      #10

                      Thanks. The particular customer the app in question is for has spent the last 7 years saying no to XP, so I'm not overly concerned about Vista issues with it yet. I have no idea when/if the app will be getting an update (my fault for delivering a bug free release :doh: ), but am still interested in examples to have on hand whenever the happy day of an update comes.

                      Today's lesson is brought to you by the word "niggardly". Remember kids, don't attribute to racism what can be explained by Scandinavian language roots. -- Robert Royall

                      P 1 Reply Last reply
                      0
                      • D Dan Neely

                        Thanks. The particular customer the app in question is for has spent the last 7 years saying no to XP, so I'm not overly concerned about Vista issues with it yet. I have no idea when/if the app will be getting an update (my fault for delivering a bug free release :doh: ), but am still interested in examples to have on hand whenever the happy day of an update comes.

                        Today's lesson is brought to you by the word "niggardly". Remember kids, don't attribute to racism what can be explained by Scandinavian language roots. -- Robert Royall

                        P Offline
                        P Offline
                        Paul Sanders the other one
                        wrote on last edited by
                        #11

                        OK, here's my collection of horrible hacks. The hWnd parameter is the window you have just resized. hPaintWnd is the window you are going to draw into. On XP, these can be the same (but not under Aero). gIsXP is false on Windows 2000 (where PrintWindow is not supported), true on XP and Vista. The rest you already know. GetRelativeWindowRect is the most useful helper function I ever wrote, by the way. Good luck!

                        #ifndef PW_CLIENTONLY
                        #define PW_CLIENTONLY 0x00000001
                        #endif
                        .
                        extern "C"
                        {
                        typedef WINUSERAPI BOOL (WINAPI * tPrintWindow) (HWND hwnd, HDC hdcBlt, UINT nFlags);
                        }
                        .
                        // GradientWindow::SmoothUpdate - repaint this window without flicker
                        void GradientWindow::SmoothUpdate (HWND hWnd, HWND hPaintWnd)
                        {
                        if (!IsWindowVisible (hWnd)) // but beware WM_SETREDRAW (TRUE)
                        return;
                        .
                        RECT wr;
                        wr.left = wr.top = 0;
                        if (hWnd != hPaintWnd)
                        GetRelativeWindowRect (hWnd, hPaintWnd, &wr);
                        hWnd = hPaintWnd;
                        .
                        RECT ur;
                        GetClientRect (hWnd, &ur);
                        ValidateRect (hWnd, &ur);
                        .
                        int width = ur.right;
                        int height = ur.bottom;
                        .
                        RECT r;
                        GetWindowRect (hWnd, &r);
                        POINT pt = { 0 };
                        ScreenToClient (hWnd, &pt);
                        int x_offset = -pt.x - r.left;
                        int y_offset = -pt.y - r.top;
                        width += x_offset;
                        height += y_offset;
                        .
                        for ( ; ; )
                        {
                        DWORD style = GetWindowLong (hPaintWnd, GWL_STYLE);
                        if ((style & WS_CHILD) == 0)
                        break;
                        HWND hParent = GetParent (hPaintWnd); // else Aero doesn't draw
                        if (hParent == NULL)
                        break;
                        hPaintWnd = hParent;
                        }
                        .
                        HDC hDC = GetDC (hPaintWnd);
                        assert (hDC);
                        wr.left = wr.top = 0;
                        if (hWnd != hPaintWnd)
                        GetRelativeWindowRect (hWnd, hPaintWnd, &wr);
                        .
                        HDC hMemDC = CreateCompatibleDC (hDC);
                        assert (hMemDC);
                        HBITMAP hBitmap = CreateCompatibleBitmap (hDC, width + wr.left, height + wr.top);
                        assert (hBitmap);
                        HBITMAP hOldBitmap = (HBITMAP) SelectObject (hMemDC, hBitmap);
                        .
                        // We prefer not to use WM_PRINT because:
                        // (a) it does not paint themed window borders correctly
                        // (b) URLControl's don't highlight the selected text
                        // (c) brush origin problems on Windows 2000 (but no gradients there anymore, so OK)
                        if (!gIsXP) // no PrintWindow () on Windows 2000
                        SendMessage (hWnd, WM_PRINT, (WPARAM) hMemDC,
                        PRF_ERASEBKGND | PR

                        1 Reply Last reply
                        0
                        Reply
                        • Reply as topic
                        Log in to reply
                        • Oldest to Newest
                        • Newest to Oldest
                        • Most Votes


                        • Login

                        • Don't have an account? Register

                        • Login or register to search.
                        • First post
                          Last post
                        0
                        • Categories
                        • Recent
                        • Tags
                        • Popular
                        • World
                        • Users
                        • Groups