1650/2078

Scaling a Bitmap for the Large and Small Image Icons

Originally Published in

Every time I created a ribbon button, I was faced with the task of creating appropriately scaled icons for it to populate the PushButton large and small image icon properties LargeImage and Image.

They seem to expect a 32 x 32 and 16 x 16 icon, respectively.

I finally solved that once and for all by implementing a couple of methods to perform automatic bitmap scaling:

Here they are one by one:

BitmapImageToBitmap

/// <summary>
/// Convert a BitmapImage to Bitmap
/// </summary>
static Bitmap BitmapImageToBitmap
  BitmapImage bitmapImage )
{
  //BitmapImage bitmapImage = new BitmapImage(
  // new Uri("../Images/test.png", UriKind.Relative));

  usingMemoryStream outStream = new MemoryStream() )
  {
    BitmapEncoder enc = new BmpBitmapEncoder();
    enc.Frames.Add( BitmapFrame.Create( bitmapImage ) );
    enc.Save( outStream );
    Bitmap bitmap = new Bitmap( outStream );

    return new Bitmap( bitmap );
  }
}

BitmapToBitmapSource

[System.Runtime.InteropServices.DllImport"gdi32.dll" )]
public static extern bool DeleteObjectIntPtr hObject );

/// <summary>
/// Convert a Bitmap to a BitmapSource
/// </summary>
static BitmapSource BitmapToBitmapSourceBitmap bitmap )
{
  IntPtr hBitmap = bitmap.GetHbitmap();

  BitmapSource retval;

  try
  {
    retval = Imaging.CreateBitmapSourceFromHBitmap(
      hBitmap, IntPtr.Zero, Int32Rect.Empty,
      BitmapSizeOptions.FromEmptyOptions() );
  }
  finally
  {
    DeleteObject( hBitmap );
  }
  return retval;
}

ResizeImage

/// <summary>
/// Resize the image to the specified width and height.
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
static Bitmap ResizeImage(
  Image image
  int width,
  int height )
{
  var destRect = new System.Drawing.Rectangle
    00, width, height );

  var destImage = new Bitmap( width, height );

  destImage.SetResolution( image.HorizontalResolution, 
    image.VerticalResolution );

  usingvar g = Graphics.FromImage( destImage ) )
  {
    g.CompositingMode = CompositingMode.SourceCopy;
    g.CompositingQuality = CompositingQuality.HighQuality;
    g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    g.SmoothingMode = SmoothingMode.HighQuality;
    g.PixelOffsetMode = PixelOffsetMode.HighQuality;

    usingvar wrapMode = new ImageAttributes() )
    {
      wrapMode.SetWrapMode( WrapMode.TileFlipXY );
      g.DrawImage( image, destRect, 00, image.Width, 
        image.Height, GraphicsUnit.Pixel, wrapMode );
    }
  }
  return destImage;
}

ScaledIcon

ScaledIcon simply calls the three helper methods defined above to return a scaled version of the input image:

/// <summary>
/// Scale down large icon to desired size for Revit 
/// ribbon button, e.g., 32 x 32 or 16 x 16
/// </summary>
static BitmapSource ScaledIcon
  BitmapImage large_icon,
  int w,
  int h )
{
  return BitmapToBitmapSourceResizeImage
    BitmapImageToBitmap( large_icon ), w, h ) );
}

Usage Sample

Within the external application PopulatePanel method, simply read the embedded resource icon image and apply ScaledIcon to it to populate the large and small image properties with appropriately scaled images:

  BitmapImage bmi = new BitmapImagenew Uri
    "icons/cmdx.png", UriKind.Relative ) );

  PushButton pb = p.AddItemnew PushButtonData
    "Command""Command", path, "CmdX" ) ) as PushButton;

  pb.ToolTip = "Do something fantastic";
  pb.LargeImage = ScaledIcon( bmi, 3232 );
  pb.Image = ScaledIcon( bmi, 1616 );

Embedded icon resource