Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

Resizing images from the server using WPF/WIC instead of GDI+

(c) 2009 Bertrand Le Roy I and many others have written about resizing images using GDI+, or rather its .NET façade, System.Drawing. It works. But there is just this one sentence at the bottom of the documentation:

Caution:

Classes within the System.Drawing namespace are not supported for use within a Windows or ASP.NET service. Attempting to use these classes from within one of these application types may produce unexpected problems, such as diminished service performance and run-time exceptions.

Kind of scary, isn’t it? Nobody likes diminished performance and run-time exceptions. But when you need to generate thumbnails from managed code, what other choices do you have?

There used to be two: using interop with native APIs (which won’t work in medium trust) or writing your own image manipulation library from scratch. There might already be some purely managed image manipulation components out there that could replace System.Drawing, but I don’t know of one. If you do, by all means drop me a comment and I’ll update the post. I’m also not sure hoe fast managed code could do this sort of heavy pixel lifting.

Since WPF was introduced into the .NET framework, there has been a third possibility that will be the topic of this post.

Works in medium trustBefore we look at that, let’s put things in perspective: there are LOTS of applications and components out there that are using GDI+ (or more accurately its System.Drawing managed code expression) and they work just fine. My own photo album uses it and I’ve never had a problem with it. Most of the problems I’ve seen were due to improper resource management (not freeing handles and similar bugs) or to abuse of the API (resizing gigapixel images for example). But used reasonably and correctly, it’s an API that really doesn’t pose any serious problem and that is fairly safe. If you use it today and are satisfied with it, there probably isn’t any reason why you should change your code. And as we’ll see, System.Drawing works in medium trust whereas System.Windows.Media.Imaging does not.

So why would you want to use that fancy WPF stuff then? Well, first, it uses Windows Imaging Components, which means that you benefit from the same extensible imaging infrastructure that displays media in the Windows Explorer. This means that if you have your camera manufacturer’s raw codec or an Adobe DNG codec installed on the server, you’ll be able to resize photos using those formats without changing a single line of code. Pretty sweet. You also get a much more complete API. For example, there is support for reading and writing meta-data, which is extremely useful for gallery types of applications.

So let’s go and resize images.

As you know, images on the web are most of the time served from a different request than the page that shows them, through img tags on the page that point back to the server. For that reason, a dynamic image is not computed on the server by the page, but by a separate handler to which the img tag points and to which the page must communicate enough information through the querystring in order to construct the image.

In our case, that information is just the name of the image to resize. I’d like at this point to make a recommendation. I’ve seen many such handlers take the size of the thumbnail as a parameter. I think this is a security flaw as an attacker can easily generate requests for many different sizes, resulting in a flooded cache and/or using up lots of processing power. There are of course other, more brutal ways to launch a DoS attack but why make it easy? You will usually need at most a couple of image sizes, so it’s better to keep that information off the handler’s querystring and to code it as a setting of your application that never leaves the server. In our case, the thumbnail size is a constant that I arbitrarily set to 150 pixels.

The first thing you’ll need to do in order to resize images is to import the references to WPF into your web site. You’ll need the following assemblies:

  • PresentationCore
  • WindowsBase

These go under configuration/system.web/compilation/assemblies in web.config if you’re in a web site, or in project references in a WAP or library project.

There are actually several different ways that you can resize an image using the WPF API.

The fastest way is to create a BitmapImage from the file on disk and to specify the target width and height as part as image decoding:

BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = photoPath;
bi.DecodePixelWidth = width;
bi.DecodePixelHeight = height;
bi.EndInit();

This is very efficient because the codec can scale while decoding and render only those pixels that will be in the final image.

Unfortunately, this won’t do here because in order to compute the size of the thumbnail, we need to know the dimensions of the original image: we want the thumbnail to have the same aspect ratio as the image it represents. So we need to read the image -or at least some of the image- before we can determine the size we want for the target.

The second way to resize is to apply a ScaleTransform on the image. In order to do that, we need to first read the image and grab its first (and usually only) frame:

var photoDecoder = BitmapDecoder.Create(
    photoStream,
    BitmapCreateOptions.PreservePixelFormat,
    BitmapCacheOption.None);
var photo = photoDecoder.Frames[0];

Once we have that frame, we can apply the ScaleTransform using a TransformedBitmap. ScaleTransform is a representation of –you guessed it– a scale transformation: it has horizontal and vertical scales, and an optional center offset. To resize an image, all we have to do is this:

var target = new TransformedBitmap(
    photo,
    new ScaleTransform(
        width / photo.Width * 96 / photo.DpiX,
        height / photo.Height * 96 / photo.DpiY,
        0, 0));
var thumbnail = BitmapFrame.Create(target);

We compute the scale by dividing the desired width (resp. height) by the original width (resp. height) and then multiplying the results by a DPI factor. That DPI factor, the division of the DPI of most screens by the original image’s DPI, is quite an unfortunate hack. Ideally, you’d be able to specify what DPI you want for the target image. Unfortunately, using this method you can’t, and the default is that it uses the original photo’s DPI and applies it to the target. In other terms, if you were asking for a target 150 pixels wide, and the original image was 600 pixels wide, you’d assume that a scale of 0.25 would get you the desired result: a thumbnail 150 pixels wide. But if the image was 240DPI, what you’ll actually get is a thumbnail 2.5 times bigger (in pixels) at 375 pixels wide. If in a context where the pixel dimension is what counts, this works, but examining the file in Photoshop or Paint.NET will reveal that it is 240DPI and not 96 DPI.

Using this method, you also don’t get a chance to affect the algorithm used to resize the image. Fortunately, the defaults give a pretty good quality with good performance (see below for a comparison in quality and performance).

The last resize method that I want to talk about is using a fuller drawing pipeline, giving us lots of control and additional options, at the price of performance. It is also the only one you can use if you want to do more to the image than just resize it (such as add vector graphics or watermark text).

public static BitmapFrame Resize(
BitmapFrame photo, int width, int height,
BitmapScalingMode scalingMode) {
var group = new DrawingGroup(); RenderOptions.SetBitmapScalingMode(
group, scalingMode); group.Children.Add(
new ImageDrawing(photo,
new Rect(0, 0, width, height))); var targetVisual = new DrawingVisual(); var targetContext = targetVisual.RenderOpen(); targetContext.DrawDrawing(group); var target = new RenderTargetBitmap( width, height, 96, 96, PixelFormats.Default); targetContext.Close(); target.Render(targetVisual); var targetFrame = BitmapFrame.Create(target); return targetFrame; }

Notice that this time, we were able to specify the DPI (96), and also the algorithm to use to resize the image.

One thing I noticed when testing those different algorithms is that the enumeration that WPF uses does not have as many values as it seems:

public enum BitmapScalingMode {
    Unspecified = 0,
Linear = 1, LowQuality = 1,
HighQuality = 2, Fant = 2, NearestNeighbor = 3, }

The explanation I got from the team on that is that at first they were vaguer “unspecified, low and high” despite the fact that the algorithms behind these names were known ones. People kept asking for specific algorithms despite them being already there, so they added the new aliases for the same values, to make it more explicit what algorithm is being used.

In all cases, I’m saving the resized bitmap as PNG as it’s simply the best format to get quality results for small thumbnails (jpg never looks very good at these scales, and GIF doesn’t have enough colors for photos):

byte[] targetBytes = null;
using (var memoryStream = new MemoryStream()) {
    var targetEncoder = new PngBitmapEncoder();
    targetEncoder.Frames.Add(targetFrame);
    targetEncoder.Save(memoryStream);
    targetBytes = memoryStream.ToArray();
}

I haven’t mentioned spitting out the image to the output stream and caching of the image, as this is virtually identical to what you’d do with GDI. You can also check out the code.

At this point, we have a wide number of options, so how do we choose between them?

I wrote a little benchmark application (which you can find attached to the bottom of this post). The application compares the rendering times and sizes for 30 jpg images, each one being 12 megapixels. It also generates different resized versions of two images that are particularly difficult to resize because of their high tendency for moiré using all the different quality settings that we have at our disposal, for GDI and WPF.

First, let’s look at the quality results:

GDI nearest neighbor GDI low GDI bicubic
GDI nearest neighbor GDI low GDI bicubic
GDI bilinear GDI default GDI high
GDI bilinear GDI default GDI high
GDI high quality bilinear GDI high quality bicubic Fast WPF
GDI high quality bilinear GDI high quality bicubic Fast WPF
WPF nearest neighbor WPF linear WPF Fant
WPF nearest neighbor WPF linear WPF Fant
GDI nearest neighbor GDI low GDI bicubic
GDI nearest neighbor GDI low GDI bicubic
GDI bilinear GDI default GDI high
GDI bilinear GDI default GDI high
GDI high quality bilinear GDI high quality bicubic Fast WPF
GDI high quality bilinear GDI high quality bicubic Fast WPF
WPF nearest neighbor WPF linear WPF fant
WPF nearest neighbor WPF linear WPF Fant

I’ve shown before how for GDI, there is no big difference in performance between the modes that look acceptable (the ugly ones run three times as fast, but ew). So for the perf benchmark, I’ll use HQ bicubic for GDI.

For WPF, amazingly the one that looks best is the fast one. Out of the slower ones, Fant/High looks best so that’s what I’ll use to test perf.

Using these best settings, here are the results:

  Read Resize Encode Total Size
WPF 0.05s 7.80s 0.17s 8.0s 964kB
Fast WPF 0.05s ~0s 3.2s 3.3s 864kB
GDI 6.02s 5.65s 0.12s 11.8s 1,250kB

The time spent reading, resizing and encoding might seem weird until you realize that WPF doesn’t do the actual operations until it has to, à la Linq. This explains why reading the image or resizing it looks instantaneous whereas encoding seems to take longer than with GDI.

If we look at what exactly happens, in the fast WPF case reading does almost nothing except extract basic meta-data such as image dimensions. Then we resize, and still nothing really happens. It’s only when we ask for encoding that image data is read, resized and then encoded, resulting in encoding times that look longer than they should, but really that’s the entire operation and overall it’s wickedly fast: more than 3.5 times faster than high quality GDI. On average, it spent about a tenth of a second to resize each twelve megapixel image. That's more than a hundred million pixels processed per second.

In the regular WPF case, the bulk of the work is being done during the resize operation, which does both the decoding and the resizing, but still does so in two thirds of the time it takes GDI to do the same thing. One can see that the encoding time, which this time is only encoding (no catchup from previous operations) is in the same ballpark as GDI. Overall, this case is still 30% faster than GDI.

For completeness and to do your own comparisons based on the quality that you choose to use, here are the same numbers for all quality settings with each technology (numbers differ a little from those above, I haven't computed the statistical uncertainty of those results, but it seems to be roughly +-0.5s; sizes are exact):

AlgorithmTimeSize
GDI Nearest neighbor6.9s1,213kB
GDI Low8.1s1,213kB
GDI HQ bilinear10.5s1207kB
GDI HQ bicubic10s1,250kB
GDI High10.1s1,250kB
GDI Bilinear7.9s1,213kB
GDI Bicubic8.1s1,230kB
GDI Default8.4s1,213kB
WPF Nearest neighbor6.6s1,121kB
WPF Low / linear6.9s1,118kB
WPF High / Fant7.7s964kB
WPF Unspecified6.9s1,118kB
Fast WPF3.2s864kB

On the size front, things look good as well. The quality of the output in all three cases is roughly equivalent, but the fastest method is also the one that gives the most compact results: the fast WPF method gives files that are on average 30% smaller than the same images resized by GDI and that are also about 10% smaller than the ones produced in the full WPF case.

Here are a few resized images that I used for the benchmark so that you can judge the quality for yourself:

IMG_2734IMG_2744IMG_2228IMG_2235IMG_2300IMG_2311IMG_2317IMG_2318IMG_2325IMG_2330IMG_2332IMG_2346IMG_2351IMG_2363IMG_2398IMG_2443IMG_2445IMG_2446IMG_2452IMG_2462IMG_2504IMG_2505IMG_2525

So except for the DPI problem, fast WPF is full of win and the one I’d choose for simple resizing.

But of course in some cases you won’t even have the option to use WPF because you just don’t have full trust. In those case, it’s OK to use GDI. But in all other cases, WPF is just faster, more efficient and it doesn’t have the known problems that led Microsoft to display a scary message on the API documentation.

UPDATE: added perf. numbers for all quality settings.

UPDATE 2: I contacted the WPF team to have the final word on whether this is supported. Unfortunately, it's not, and the documentation is being updated accordingly. I apologize about any confusion this may have caused. We're looking at ways to make that story more acceptable in the future.

Follow-up: Resizing to JPEG.

Comments

foobar said:

YES!  Thank you for this.  Resizing using GDI+ was never really acceptable.  This is great.

# December 10, 2009 9:22 PM

Jerry said:

Nice writeup Bertrand.  Thanks!

# December 10, 2009 11:17 PM

Robert said:

Great article!

Too bad that WPF Fant doesn't look as good as GDI HQ Bicubic, so your benchmarks are not exactly comparing the same thing.

# December 11, 2009 3:27 AM

Mukesh said:

Really this is great solution. Thank you...:)

# December 11, 2009 3:48 AM

Joe Chung said:

I don't think that a fully managed code image manipulation library could ever compare to native code (for performance) until there were managed code equivalents/aliases for SIMD/vector instructions.

Anyways, beautiful pictures!

# December 11, 2009 5:30 AM

Adam Tibi said:

I am actually maintaining a GDI+ resizing solution, when that needs to be updated, I will definitely use WPF.

Thank you for the highlight.

# December 11, 2009 8:59 AM

Miguel de Icaza said:

The WPF approach will not work on platforms other that Windows.

# December 11, 2009 10:19 AM

alessandro said:

ahh fantastic. I use GDI+ too. Time to switch I suppose.

Thanks

# December 11, 2009 10:20 AM

Bertrand Le Roy said:

Ah, @Miguel, great that you chimed in, I was wondering about that and didn't have time to try it on Mono yet. So why is that? I thought that with the great work you've done on Moonlight, you'd have all those APIs implemented as they seem pretty fundamental for WPF/Silverlight?

# December 11, 2009 1:02 PM

Klaus Graefensteiner said:

Great research! This article saves me a ton of time in my current Silverlight project.

Klaus

# December 12, 2009 2:02 AM

Matt Sherman said:

Do you have any way of knowing if the the processing is going to the GPU? If so, a GPU on the server would be interesting...

# December 13, 2009 1:35 PM

Matt Sherman said:

And one more question -- I don't see where "Fast WPF" is specified as an option in the code. Do all the examples get the advantage of Linq-y deferred execution?

For example, when calling photo.Width, how much of the image is read off disk at that point? Just metadata? That would be great, I'd love to do a bit of math prior to resizing the image without incurring the I/O overhead.

# December 13, 2009 2:09 PM

Bertrand Le Roy said:

@Matt: I'm not sure. CPU usage is a little lower during WPF resizing but not much, so I'd be surprised if the GPU was involved. I didn't measure GPU usage though.

Fast WPF is what's used in the handler. If you want to see all usages, look in the benchmark project.

When reading width or height, you are only reading meta-data, which is super-fast.

# December 14, 2009 1:06 AM

Gerhard Schneider (WPF) said:

WIC Decoders/Encoders and the RenderTargetBitmap pipeline are not using the GPU.

# December 14, 2009 1:02 PM

Bertrand Le Roy said:

Thanks Gerhard.

# December 14, 2009 1:26 PM

Jens Christian Mikkelsen said:

Originally, I was pretty scared when you mentioned the System.Drawing warning. But then I found this blog post by cherylws, who wrote the documentation:

blogs.msdn.com/.../what-does-not-supported-mean.aspx

She was asked to add this statement, because no-one had tested the ASP.NET scenario and MS didn't want to support it. (For free.) So there are no known issues with using System.Drawing from ASP, as I understand it.

Are there any guarantees that WPF will work from an ASP.NET application? Is it a supported scenario? :-)

# December 15, 2009 9:23 AM

Bertrand Le Roy said:

@Jens: thanks for the link. It does help to know it is just untested in this context, but as pretty much anything untested, there are real problems caused by it. Cheryl doesn't say by the way that there are no known problems. Granted, problems are very very rare and I consider it safe enough to use System.Drawing on the kind of application I build. And if you only have medium trust, you don't really have an alternative.

Now for some developers, it is a necessity to have support backing what you do, even if it works.

And of course, there is the performance, which in my book is reason enough to move to WPF.

Yes, you can call support about that and they should help you. To be clear, *any* API in .NET is supported on the server unless specified otherwise.

# December 15, 2009 2:31 PM

Michael A. Cochran said:

There is a big problem with GDI+ if you are trying to modify images, say for redaction or to add a stamp.

The problem is that GDI+ can only modify 32-bit bitmaps.  If your image is not a color image - like most scanned images in an ECM repository - you have a whole conversion process to go through.  WPF does not seem to have that same requirement and it is much easier.  I did a quick prototype this afternoon and I can easily modify a 1-bit per pixel CCITTG4 TIFF with multiple pages with no conversion to 32-bpp.

As a bonus WPF is supported in a web application.

NICE!  Thanks for the quick start!

MAC

# December 16, 2009 7:45 PM

Aamir Hasan said:

imgPhoto = ScaleByPercent(imgPhotoVert, 50);

imgPhoto.Save(WorkingDirectory +

   @"\images\imageresize_1.jpg", ImageFormat.Jpeg);

imgPhoto.Dispose();

....

static Image ScaleByPercent(Image imgPhoto, int Percent)

{

   float nPercent = ((float)Percent/100);

   int sourceWidth = imgPhoto.Width;

   int sourceHeight = imgPhoto.Height;

   int sourceX = 0;

   int sourceY = 0;

   int destX = 0;

   int destY = 0;

   int destWidth  = (int)(sourceWidth * nPercent);

   int destHeight = (int)(sourceHeight * nPercent);

   Bitmap bmPhoto = new Bitmap(destWidth, destHeight,

                            PixelFormat.Format24bppRgb);

   bmPhoto.SetResolution(imgPhoto.HorizontalResolution,

                           imgPhoto.VerticalResolution);

   Graphics grPhoto = Graphics.FromImage(bmPhoto);

   grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;

   grPhoto.DrawImage(imgPhoto,

       new Rectangle(destX,destY,destWidth,destHeight),

       new Rectangle(sourceX,sourceY,sourceWidth,sourceHeight),

       GraphicsUnit.Pixel);

   grPhoto.Dispose();

   return bmPhoto;

}

# December 20, 2009 9:54 AM

Bertrand Le Roy said:

@Aamir: yes, that's GDI+ resizing. Not sure what your point was, seeing that you didn't write a comment besides the code.

# December 20, 2009 2:41 PM

Gilad Kapel said:

Thanks..

I've made changes in my code to use WPF functions for previewing the manipulated image and use GDI+ only when saving. Now working with the images it twice as fast.

Too bad the quality of WPF isn't good enough.

Gilad.

# January 6, 2010 8:58 AM

Bertrand Le Roy said:

@Gilad: I'm surprised by what you're saying about the quality. Do you have a sample photo that I could look at?

# January 6, 2010 2:11 PM

Scott Galloway said:

It's not quite true to say that GDI+ just wasn't tested. There are some pretty bad memory leaks and handle leaks that can (and often do) happen when using GDI+ in ASP.NET. I did a fair bit of investigation into this when on the team. Really WPF is the only safe way to go.

# January 6, 2010 8:02 PM

Bertrand Le Roy said:

@Victor: Are you sure? I tried to save the same image from Paint.NET and here are the results:

- WPF PNG:        30kB

- Paint.NET PNG:  39kB

- Paint.NET TIFF: 38kB

- Paint.NET BMP:  44kB

- Paint.NET JPG:   9kB (95% quality)

It looks to me like the uncompressed size would be around the same as BMP but it's less than 70% of that size, and it's also smaller than what Paint.NET is producing with PNG.

For size, no widespread format comes close to JPG anyways, even with high quality settings. I used PNG in the article because it's lossless and I wanted to show quality differences with as few variables as possible, but now this is done, I can probably make a follow-up post that looks at JPG compression.

# January 18, 2010 2:20 PM

Victor said:

@Bertrand: I've checked with Paint.NET, which seems to go wrong in the same way. It too increases the file size of the original file (created in Adobe Fireworks CS3) by a few kilobytes. In fact, just like WPF, it doesn't apply DEFLATE compression to the IDAT chunk of the PNG file. Then, in addition, it adds various metadata which would explain the even larger file size. Perhaps it even skips the pre-compression stage.

It's a real shame, as PNG can be quite a powerful image format if used correctly. I think I'll have to apply the compression myself if I'm to use WPF.

# January 19, 2010 10:36 AM

Bertrand Le Roy said:

@Victor: I'll follow-up with the WPF team and get back to you. I'll also write that follow-up post using JPEG. Thanks for investigating.

# January 19, 2010 3:03 PM

Joel @ MMCC said:

You state:

“The fastest way is to create a BitmapImage from the file on disk and to specify the target width and height as part as image decoding:

[code]BitmapImage bi = new BitmapImage();

bi.BeginInit();

bi.UriSource = photoPath;

bi.DecodePixelWidth = width;

bi.DecodePixelHeight = height;

bi.EndInit();This is very efficient because the codec can scale while decoding and render only those pixels that will be in the final image.[/code]

“Unfortunately, this won’t do here because in order to compute the size of the thumbnail, we need to know the dimensions of the original image: we want the thumbnail to have the same aspect ratio as the image it represents. So we need to read the image — or at least some of the image — before we can determine the size we want for the target.”

This is [i]not[/i] true! You don’t have to specify [i]both[/i] the [b]DecodePixelWidth[/b] [i]and[/i] the [b]DecodePixelHeight[/b]! You [/i]only[/i] have to specify [i]one or the other[/i] to do auto-resizing in WPF on image decode! If you specify only one, [b]the other will [i]automatically[/i] be set to whatever value is needed to [i]preserve the aspect ratio!![/i][/b]

So, if you need your thumbnail to be, oh, say, 160 pixels wide and you don’t really care how tall or short that makes it so long as the aspect ratio is preserved, just do:

[code]bi.DecodePixelWidth = 160[/code]

and [/i]don’t[/i] set [b]bi.DecodePixelHeight[/b]! [i]Voila!![/i]

Conversely, if your layout requires a consistent height but the width can be anything, set [i]only[/i] the [b]DecodePixelHeight[/b] but [i]not[/i] the [b]DecodePixelWidth[/b]!

As for why WPF is so much faster than GDI+, I’m pretty sure that it’s because WPF leverages your GPU, including Pixel Shader up to 2.0 if using .NET Framework 3.5. .NET Framework 4.x and up will leverage Pixel Shader 3.0 if your graphics card or integrated motherboard graphics chipset supports it.

With these in mind, it would be interesting to do benchmarks showing the use of the first WPF technique (BitmapImage with setting just DecodePixelHeight or DecodePixelWidth), and also how different graphics cards affect the results on all of the WPF techniques as opposed to GDI+.

I find it fascinating that a reasonably modern gaming graphics chipset can now have a measurable impact on the performance of a [i]web server![/i] Even if no monitor is connected to it!

# February 18, 2010 9:51 PM

Bertrand Le Roy said:

@Joel: you misunderstood me. You are right that if you specify only one of the two dimensions, the other will follow to keep the aspect ratio. That does not solve the problem at hand though, which is to constrain the thumbnail to a square of predetermined dimensions. I do care that it's not too tall AND not too wide.

For example, if the original image is 200 * 1000 and I set the resized width to be 150, the resulting thumbnail will be 750 pixels heigh and will destroy my layout.

If you want to do a benchmark using dimensions that are only constrained in one direction, sure, please do. I'm not super-interested in that case but apparently I'm not representative of 100% of the possible usage of these APIs. I suspect you'll find that WPF in that case is even faster (and dramatically so as it can apply the resize when _loading_ the image instead of loading it and then redimensioning it in memory, saving processing power and memory).

About hardware acceleration, I asked the WPF team, and their answer was that those APIs do NOT use hardware acceleration. Unfortunately.

# February 18, 2010 11:58 PM

Bertrand Le Roy said:

Oh, and one more thing. I remember when the first managed APIs for Direct 3D came out, I had the exact same thoughts as you: it would be fascinating that a graphics card could dramatically improve a web server's throughput. Unfortunately it never really happened, which is puzzling to say the least.

I guess somebody could build a graphics library for ASP.NET using DirectX, at least in principle. That would rock.

# February 19, 2010 12:00 AM

andrew said:

"not supported for use within a Windows or ASP.NET service." YES, but "ASP.NET service" == Webservice, so it's 100% to use it in asp.net application/website.

And i don't care if wpf is supported or not, if it works i will use it, great job!

# February 27, 2010 7:18 PM

Bertrand Le Roy said:

@andrew: well, they mean ASP.NET in general, not just web services.

# February 28, 2010 1:32 AM

Elvin said:

So is WPF supported in ASP.NET or not?

# March 20, 2010 9:33 PM

Bertrand Le Roy said:

@Elvin: no, it's not (see update 2)

# March 21, 2010 12:06 AM

Steve Wagner said:

If someone wants to give the new image a another DPI then 96. Be aware that you need to recaclulate the rect of the drawing this way:

var drawingWidth = (newWidth * 96.0) / destDpiX;

var drawingHeight = (newHeight * 96.0) / destDpiY;

group.Children.Add(new ImageDrawing(imageSource, new Rect(0, 0, drawingWidth, drawingHeight)));

# January 28, 2011 4:34 AM

Chaker Nakhli said:

Hello,

Thank you for your post!

In your benchmark you didn't mention memory usage for each option. Space complexity is important when you handle multiple request in parallel.

Cheers

# May 21, 2011 11:36 AM

dbjdbj said:

Hi BLR,

I think You simply *must* repeat this on Your W8 machine.

I bet in W8 this is *much* faster and easier.

W8 WinRT is using DirectX.

And of course, it would provoke many web visits to Your blog, just to check if image resizing on W8 is equaly fast using JavaScript, C# or WINC++ on W8?

Thanks ...

# October 24, 2011 3:33 AM

Bertrand Le Roy said:

@dbj: I don't have a Win8 machine, but I'll try to grab one.

# October 24, 2011 3:49 AM

Avie2 said:

I am on MS Vista, ASP 4.0, should I upgrade to Win7 to use WIC? Thanks.

# December 4, 2011 6:21 AM

Bertrand Le Roy said:

@Avie2: you should upgrade to Windows 7, period.

# December 4, 2011 9:32 PM

Avie2 said:

@Bertrand Le Roy, I checked everything thoroughly. I can use BitmapImage on Vista but in Silverlight applications. So as I understood, if I upgrade to Win7, then BitmapImage will be accessible from simple aspx web applications. If so, I will upgrade immediately :)

# December 5, 2011 1:24 AM

Bertrand Le Roy said:

As I said, you should upgrade in any case. I don't know about Vista support.

# December 5, 2011 2:20 AM

Avie2 said:

Bertrand Le Roy, It gives me hope :) Do you have any links to aspx pages with graphics that definitely use WIC and do not use GDI+ at all? Thank you.

# December 5, 2011 3:35 AM

Bertrand Le Roy said:

@Avie2: I'm pretty sure MSN uses WIC.

# December 5, 2011 3:43 PM

Avie2 said:

@Bertrand Le Roy, *LOL*

# December 5, 2011 3:59 PM

secgeek said:

Usage of WPF libraries in server side code is not supported nor advised.

# December 16, 2011 2:23 PM

Bertrand Le Roy said:

@secgeek: you could have bothered to read the post.

# December 18, 2011 9:16 PM

wadison said:

BitmapImage bi = new BitmapImage();

bi.BeginInit();

bi.UriSource = photoPath;

// **Set only one side** !!

bi.DecodePixelWidth = width;

bi.EndInit();

# January 16, 2012 5:38 AM

Bertrand Le Roy said:

@wadison: that's using GDI+

# January 16, 2012 4:49 PM

wadison said:

Ok, so I don't understand your post :

"""

There are actually several different ways that you can resize an image using the WPF API.

The fastest way is to create a BitmapImage from the file on disk and to specify the target width and height as part as image decoding:

BitmapImage bi = new BitmapImage();

bi.BeginInit();

bi.UriSource = photoPath;

bi.DecodePixelWidth = width;

bi.DecodePixelHeight = height;

bi.EndInit();

[...]

Unfortunately, this won’t do here because in order to compute the size of the thumbnail, we need to know the dimensions of the original image: we want the thumbnail to have the same aspect ratio as the image it represents

"""

we can keep the ratio with this way! ( Set only one side).

++

# January 17, 2012 3:57 AM

Bertrand Le Roy said:

Oh, I see. I had misunderstood your comment, as it had no actual words, just code out of context. That works for fixed width, but it won't do here, where the thumbnail is constrained within a width and a height.

# January 17, 2012 5:05 AM

Dan said:

DOes anyone know if it's possible to resize a digital camera photo with metadata that contains

the focal length the picture was taken at

the pixel dimensions

to 100% life size?

Thanks, Dan

# February 29, 2012 4:39 PM

€ric said:

I recently updated a WCF REST webservice that serves up images from using GDI to WPF.  In production on Windows Server 2008 R2 I started seeing a strange error that I didn't see during load testing:

System.ComponentModel.Win32Exception (0x80004005): The operation completed successfully

Stack trace (partial)

System.ComponentModel.Win32Exception (0x80004005): The operation completed successfully

  at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)

  at System.Windows.Media.MediaContextNotificationWindow..ctor(MediaContext ownerMediaContext)

  at System.Windows.Media.MediaContext..ctor(Dispatcher dispatcher)

  at System.Windows.Media.DrawingVisual.RenderOpen()

Google searches seem to indicate this might be caused by having too many handles open.  Windows taskmanager was showing ~8,500 handles at the time the errors were occurring.  I am disposing the Stream objects that I opened.  Sadly the WPF components don't implement IDisposable so I don't have control over that.

I worked around the error by failing back to GDI when that error occurs and increasing the recycling on the IIS app pool, but I'd love to fix it.

Anyone else seeing this error, or even better, know the fix?

# April 24, 2012 9:24 AM

Bertrand Le Roy said:

@Eric: contact support.

# May 1, 2012 11:40 PM