System.Drawing.Imaging trouble  
Author Message
LonelyPixel





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

Hi,

where's the best place to ask a question about trouble with System.Drawing.Imaging I couldn't find any special forum for this.



.NET Development30  
 
 
element109





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

What's the problem Include a sample.
 
 
LonelyPixel





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

Okay, the problem is that I want to save a Bitmap as a JPEG file. I've found several how-tos on the net that describe how it works, with finding the right Encoder and setting parameters like for compression quality, but the Bitmap.Save() call throws an Invalid Argument exception. I have no idea what's wrong and there's no result when searching the web for this problem.

 
 
nobugz





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

That exception is not documented for Image.Save(). Start by not tinkering with the encoder parameters. If that works, post the code that sets the encoder parameters and a call stack trace.


 
 
LonelyPixel





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

I don't know what you mean. I want to set encoding parameters and there is no similar solution with not setting those parameters. Does anyone have a working sample for saving a resampled JPEG image with a specified quality I don't get it. It looks highly complicated.

 
 
nobugz





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

You didn't post your code, can't tell what's wrong with it. This worked well for me:

public static void SaveAsJPeg(Image img, int quality, string path) {
if (quality < 0 || quality > 100) throw new ArgumentException("Quality out of range");
// Locate the JPEG encoder
ImageCodecInfo jpeg = null;
foreach (ImageCodecInfo encoder in ImageCodecInfo.GetImageEncoders())
if (encoder.FormatDescription == "JPEG") {
jpeg = encoder;
break;
}
// Setup to give the encoder the quality parameter
EncoderParameters parms = new EncoderParameters(1);
EncoderParameter qparam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
parms.Param[0] = qparam;
// Now save the image
img.Save(path, jpeg, parms);
}



 
 
LonelyPixel





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

Sorry for the long delay. I have now simplified my code and here it is: (Syntax highlighting function doesn't work here.)

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Open image
Image img_in = Image.FromFile(args[0]);

// Write image
ImageCodecInfo codec = null;
foreach (ImageCodecInfo i in ImageCodecInfo.GetImageEncoders())
{
if (i.MimeType == "image/jpeg")
{
codec = i;
break;
}
}
if (codec == null) throw new Exception("Jpeg codec not found");

EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(Encoder.Quality, 80);

img_in.Save(Path.GetFileNameWithoutExtension(args[0]) + ".out.jpg", codec, ep);
// This throws an ArgumentException with no further description of the problem
}
}
}


 
 
nobugz





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

Too bad you didn't use my code, you would have been done a while ago. To get yours to work, change it like this:

ep.Param[0] = new EncoderParameter(Encoder.Quality, (Int64)80);



 
 
LonelyPixel





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

I can't see a difference in your code above. You didn't use Int64 (long) earlier, too. Also other code resources only use an Int32 (I assume, that is VB). But thanks for pointing this out, using long instead of int makes it work finally. I'm wondering why this pettiness isn't documented anywhere. I'd assume a value from 0 to 100 could be passed as int (which still seems to be the usual type for integer numbers) and doesn't require the double-sized long type.

 
 
nobugz





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

The EncoderParameter class doesn't have a constructor that takes an Int32. It has got others that take Byte, Int16 and Int64. When you pass the quality as an "int", the compiler will use the constructor that takes Int64, the only one that can pass an "int" without truncating. When you pass the constant value "80", it will use the constructor that takes a Byte. GDI+ doesn't like that one.

Not very intuitive but GDI+ has lots of problems like that.


 
 
LonelyPixel





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

Now I see that Int32 isn't on the list. But today I temporarily had the code with an int variable instead of a literal number. That one didn't work, too. Strange.

 
 
fookfulohan





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

I used a similar code in VB.NET but did mot specify INT64.

Howcome it work in VB.NET and not in C#

I find that C# is very unforgiving. Not very good for RAD.


 
 
nobugz





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

VB.NET would do exactly the same. It is a subtle problem.


 
 
fookfulohan





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

This code work in VB.NET w/o an explicitly casting it to INT64.

Dim imgCodec As ImageCodecInfo = GetEncoderInfo("image/jpeg")

Dim encParm As EncoderParameters = New EncoderParameters(1)

.

.

.

encParm.Param(0) = New EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 45)

Dim msImage As New MemoryStream()

pbImage.Image.Save(msImage, imgCodec, encParm) ' codec for image/jpeg, compression 45:1

I had the exact same code in C# (pbImage.Image.Save(msImage, imgCodec, encParm) and it won't work an exception occured.

Until I followed your suggestion and cast the encoder quality to int64.


 
 
Jason Pryor





PostPosted: .NET Base Class Library, System.Drawing.Imaging trouble Top

Speaking of quality issues, I'm trying to find out how to set the capture resolution (i.e. 300 dpi instead of 96 dpi). I've already posted the details of my question and the program background in this thread. Any help would be appreciated.

-Jason