2

I try to display a cached Bitmap in Winforms (for performance reasons). I have a problem cause I cant draw it. The exaple in this answer https://stackoverflow.com/a/6474581/1676819 says that there should be something like graphics.DrawCachedBitmap(bitmap, 0, 0); I cant find it.

What I've done so far:

  • I added Presentationcore.dll as reference
  • I created a CachedBitmap

CachedBitmap tempCBm = new CachedBitmap(new BitmapImage(new Uri(@"D:\test.bmp")),BitmapCreateOptions.None, BitmapCacheOption.OnLoad);

  • I tried to draw it with the standard method (causes error)

    private void CustomPaint(object sender, PaintEventArgs e)
    {
            e.Graphics.DrawImage(tempCBm, 0,0);//error
    }
    

Can somebody tell me what am I´m doing wrong? Many thanks in advance.

Community
  • 1
  • 1
RandomDude
  • 109
  • 2
  • 9

2 Answers2

3

CachedBitmap is not available through .NET. It is a feature of GDI+. Use Bitmap instead. If you need to optimize for performance, then you can use the unsafe context in C# for faster bitmap access.

A nice tutorial is available here: http://www.codeproject.com/Tips/240428/Work-with-bitmap-faster-with-Csharp.

Andrew Morton
  • 24,203
  • 9
  • 60
  • 84
Igor Ševo
  • 5,459
  • 3
  • 35
  • 80
  • Does that improves also the time the bitmap need for getting painted? I thought this improves only get/setpixel... – BudBrot Feb 23 '14 at 16:30
  • If you are using the unsafe approach, then you will need to implement your own drawing functions. If you want to draw using the `Graphics` object, then you will have to use the slower, `Bitmap`, version. – Igor Ševo Feb 23 '14 at 22:03
2

It is possible to use cached bitmap - but for some reason it is not available in standard C# api. You can circumvent around that however - make a managed C++ library that will encapsulate methods you want to expose to C#.

See my github repo - https://github.com/svejdo1/CachedBitmap

C++ utility class to expose cached bitmap

#include <windows.h>
#include <objidl.h>
#include <gdiplus.h>

using namespace Gdiplus;

namespace CachedBitmapUtility {
    public ref class BitmapUtility {
        public:
            static void* CreateCachedBitmapPtr(void* bitmapPtr, void* graphicsHdc) {
                Graphics graphics((HDC)graphicsHdc);
                CachedBitmap* result = new CachedBitmap((Bitmap*)bitmapPtr, &graphics);
                return result;
            }
            static void DisposeCachedBitmap(void* cachedBitmapPtr) {
                delete (CachedBitmap*)cachedBitmapPtr;
            }
            static void DisposeBitmap(void* bitmapPtr) {
                delete (Bitmap*)bitmapPtr;
            }
            static void* GetBitmapPtrFromHICON(void* hicon) {
                return (void*)Bitmap::FromHICON((HICON)hicon);
            }
            static void DrawCachedBitmap(void* hdc, void* cachedBitmapPtr, int x, int y) {
                Graphics graphics((HDC)hdc);
                graphics.DrawCachedBitmap((CachedBitmap*)cachedBitmapPtr, x, y);

            }
    };
}

Example usage from WinForm application:

public partial class MainForm : Form {
  IntPtr m_BitmapPtr;
  IntPtr m_CachedBitmapPtr = IntPtr.Zero;

  public MainForm() {
    InitializeComponent();

    Bitmap bitmap;
    using (var stream = typeof(MainForm).Assembly.GetManifestResourceStream("FormApplication.character.png")) {
      bitmap = (Bitmap)Bitmap.FromStream(stream);
    }
    unsafe {
      m_BitmapPtr = (IntPtr)BitmapUtility.GetBitmapPtrFromHICON((void*)bitmap.GetHicon());
    }
  }

  protected override void OnClosed(EventArgs e) {
    // TODO: refactor - dispose should happen in Dispose event
    unsafe {
      BitmapUtility.DisposeBitmap((void*)m_BitmapPtr);
      BitmapUtility.DisposeCachedBitmap((void*)m_CachedBitmapPtr);
    }
  }

  protected override void OnPaint(PaintEventArgs e) {
    var graphics = e.Graphics;
    IntPtr hdc;
    if (m_CachedBitmapPtr == IntPtr.Zero) {
      hdc = graphics.GetHdc();
      unsafe {
        m_CachedBitmapPtr = (IntPtr)BitmapUtility.CreateCachedBitmapPtr((void*)m_BitmapPtr, (void*)hdc);
      }
      graphics.ReleaseHdc(hdc);
    }

    hdc = graphics.GetHdc();
    unsafe {
      BitmapUtility.DrawCachedBitmap((void*)hdc, (void*)m_CachedBitmapPtr, 0, 0);
    }
    graphics.ReleaseHdc(hdc);
  }
}
Ondrej Svejdar
  • 21,349
  • 5
  • 54
  • 89
  • Hi Ondrej - Do you have a compiled DLL from that c++ code? – hynsey May 12 '15 at 11:37
  • I have just implemented your code on one of my programs it has improved its performance by about 400%! So, thank you. However, trying to use ScaleTransform (which I added to your `CachedBitmapUtility` class), produces no apparent output; that is, nothing appears to be rendered. Do you know why? – xfx Feb 18 '16 at 08:00
  • @xfx - I do - you can't use ScaleTransform or RotateTransform if you draw cached bitmap. Check out the value that DrawCachedBitmap function returns - I bet it returns WrongState. Only workaround here is to generate bitmap in various scales (or rotations, etc.) and cache the scaled/rotated/etc. version instead. – Ondrej Svejdar Feb 19 '16 at 05:46