6

Take the following two images:

Dev Version - IIS7 Windows 7 Pro 64bit Machine

Dev Copy

Live Version - IIS7 Windows Server 2008 64bit Machine

Live Copy

Note how the Live Version is "pixelly" & looks low quality, the Dev Version however is smooth, anti-aliased & looks fine. These are both generated by identical code:

' Settings
Dim MaxHeight As Integer = 140
Dim MaxWidth As Integer = 140
Dim WorkingFolderPath As String = "\\server\share\bla\"
Dim AllowedFileExtensions As New ArrayList
AllowedFileExtensions.Add(".jpg")
AllowedFileExtensions.Add(".jpeg")

' Select an image to use from the WorkingFolder
Dim ImageFileName As String = ""
Dim WorkingFolder As New IO.DirectoryInfo(WorkingFolderPath)
Dim SourceImages As IO.FileInfo() = WorkingFolder.GetFiles()

For Each SourceImage As IO.FileInfo In SourceImages
    If AllowedFileExtensions.Contains(SourceImage.Extension.ToLower) = True Then
        ImageFileName = SourceImage.Name
    End If
Next

' Determine path to selected image (if no image was found use a placeholder)
Dim PhysicalPath As String = ""
If ImageFileName = "" Then
    ' No Image was found, use the filler image
    PhysicalPath = Server.MapPath("ProductOfTheMonthMissing.jpg")
Else
    ' An Image was found, Redirect to it / build path for Thumnailing
    If Request.QueryString("FullSize") = "true" Then
        Response.Redirect("../share/bla/" & ImageFileName)
    Else
        PhysicalPath = WorkingFolderPath & ImageFileName
    End If
End If

' Load image and output in binary (resizing if necessary)
Using ProductImage As System.Drawing.Image = System.Drawing.Image.FromFile(PhysicalPath)
    Dim newWidth As Integer = ProductImage.Width
    Dim newHeight As Integer = ProductImage.Height

    ' Check if selected size is too big, if so, determine new size
    If ProductImage.Width > MaxWidth Or ProductImage.Height > MaxHeight Then
        Dim ratioX As Double = CDbl(MaxWidth) / ProductImage.Width
        Dim ratioY As Double = CDbl(MaxHeight) / ProductImage.Height
        Dim ratio As Double = Math.Min(ratioX, ratioY)

        newWidth = CInt(ProductImage.Width * ratio)
        newHeight = CInt(ProductImage.Height * ratio)
    End If

    ' Create a new bitmap from the image with new size
    Dim Codecs As ImageCodecInfo() = ImageCodecInfo.GetImageEncoders()
    Dim CodecInfo As ImageCodecInfo = Nothing
    Dim ProductOfTheMonth As New Bitmap(ProductImage, newWidth, newHeight)
    Dim ReSizer As Graphics = Graphics.FromImage(ProductOfTheMonth)

    ReSizer.InterpolationMode = InterpolationMode.HighQualityBicubic
    ReSizer.SmoothingMode = SmoothingMode.HighQuality
    ReSizer.PixelOffsetMode = PixelOffsetMode.HighQuality
    ReSizer.CompositingQuality = CompositingQuality.HighQuality

    ' Ensure the encoder uses the best quality settings
    Dim EncoderParams As New EncoderParameters(3)
    EncoderParams.Param(0) = New EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L)
    EncoderParams.Param(1) = New EncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod, CInt(EncoderValue.ScanMethodInterlaced))
    EncoderParams.Param(2) = New EncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod, CInt(EncoderValue.RenderProgressive))

    ' Set jpeg as the output codec
    For Each Codec As ImageCodecInfo In Codecs
        If Codec.MimeType = "image/jpeg" Then
            CodecInfo = Codec
        End If
    Next

    ' Ready a memory stream and byte array
    Dim MemStream As New MemoryStream()
    Dim bmpBytes As Byte()

    ' Save the image the the memory stream & prep ContentType for HTTP reasponse
    Response.ContentType = "image/jpeg"
    ProductOfTheMonth.Save(MemStream, CodecInfo, EncoderParams)

    ' Flush memory stream into byte array & flush to browser
    bmpBytes = MemStream.GetBuffer()
    Response.BinaryWrite(bmpBytes)

    ' Cleanup
    ProductOfTheMonth.Dispose()
    MemStream.Close()
    ProductImage.Dispose()
End Using

What is the reason behind this & how do I address the issue? Presumably its a GD issue on the live web server - but I have no idea what - I tried to be as thorough as possible in setting graphic and codec settings but its still different?

Edit: Source Image is identical in both examples too (Located on a central unc share) - copy of source image here

HeavenCore
  • 7,533
  • 6
  • 47
  • 62
  • Can you reduce the code used to reproduce the issue? What codecs are present and used on dev and live? Do they operate on the same source image? – CodeCaster Mar 03 '14 at 15:35
  • @CodeCaster Source image is identical in both scenarios - I've also edited my question with a link to the source image. I'll try and re-create this with a smaller code snippet and update shortly. – HeavenCore Mar 03 '14 at 15:51
  • If the code is identical then it can only come down to the version of GDI+ you are using, check the versions on each machine. – James Mar 03 '14 at 15:51
  • What about the codecs? Are they the same on both machines? – AJ. Mar 03 '14 at 15:52
  • @James - I thought that, but then check out this question: http://stackoverflow.com/questions/13144466/gdi-antialiasing-not-working-well-on-server-2008 - they're able to prevent this same issue by setting options on a different object regardless of GDI+ version? – HeavenCore Mar 03 '14 at 15:55
  • @HeavenCore that's interesting, however, still tells you that the problem really lies with GDI+ and not the environment it's running under. You are effectively running GDI+ under a 64-bit machine - it should, in theory, produce the same results. Is there a difference in versions? – James Mar 03 '14 at 15:58
  • @James - Not sure how to verify GD version, bear with me. – HeavenCore Mar 03 '14 at 16:04
  • Have you tried producing the image on a different production server to see if you still get the more pixelated results? – Adam Zuckerman Mar 07 '14 at 22:48
  • @AdamZuckerman We have two production servers running Win 2008 Standard SP2 - they both render in the low quality - only the windows 7 dev machines render in high quality. – HeavenCore Mar 10 '14 at 09:39

3 Answers3

1

I've had and, answered a similar question here: Graphics wrong image interpolation in .Net, but in short it seems like different platforms use different internal algorithms (or perhaps it's an internal rounding problem in GDI).

Anyway, the problem is in the settings. So try the following:

Using s As Bitmap = DirectCast(Bitmap.FromFile(PhysicalPath), Bitmap)
    Dim scale As Double = Math.Min(140.0 / s.Width, 140.0 / s.Height)
    Using d As New Bitmap(CInt(Math.Floor(scale * s.Width)), CInt(Math.Floor(scale * s.Height)), System.Drawing.Imaging.PixelFormat.Format24bppRgb)
        Using dg As Graphics = Graphics.FromImage(d)
            dg.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
            dg.SmoothingMode = SmoothingMode.HighQuality
            dg.PixelOffsetMode = PixelOffsetMode.HighQuality
            dg.CompositingQuality = CompositingQuality.HighQuality
            dg.Clear(Color.White)
            dg.DrawImage(s, New Rectangle(0, 0, d.Width, d.Height), New Rectangle(0, 0, s.Width, s.Height), GraphicsUnit.Pixel)
        End Using

        Dim jpegArgs As New EncoderParameters(3)
        jpegArgs.Param(0) = New EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L)
        jpegArgs.Param(1) = New EncoderParameter(System.Drawing.Imaging.Encoder.ScanMethod, CInt(EncoderValue.ScanMethodInterlaced))
        jpegArgs.Param(2) = New EncoderParameter(System.Drawing.Imaging.Encoder.RenderMethod, CInt(EncoderValue.RenderProgressive))

        Dim Codecs As ImageCodecInfo() = ImageCodecInfo.GetImageEncoders()
        Dim jpegParams As ImageCodecInfo = Nothing

        '#### Set jpeg as the output codec
        For Each Codec As ImageCodecInfo In Codecs
            If Codec.MimeType = "image/jpeg" Then
                jpegParams = Codec
            End If
        Next

        Response.Clear()
        Response.ContentType = "image/jpeg"

        d.Save(Response.OutputStream, jpegParams, jpegArgs)
    End Using
End Using

Good luck!

Community
  • 1
  • 1
Fredrik Johansson
  • 3,477
  • 23
  • 37
  • Sorry, but I was already using `PixelOffsetMode.HighQuality` (See code in my question) & setting `InterpolationMode` to `InterpolationMode.Bilinear` seemed to make no difference :'( – HeavenCore Mar 10 '14 at 15:46
  • Ah, now I see. You're actually never using the ReSizer graphics... Anyway I'm changing my sample to a complete one with resizing and outputting to the client (sorry about the C#, but you should be able to easily translate that to VB.NET) – Fredrik Johansson Mar 10 '14 at 15:58
  • Great conversion, have you tried it out yet? I think the issue you had before was that you never called Graphics().DrawImage() [thus never utilizing the settings in the Graphics]... Anyway the "Using" clauses should also save you from future problems of Bitmap never releasing filehandles or memory :) – Fredrik Johansson Mar 10 '14 at 17:14
  • Not tried it yet, the use of `.Where()` wont compile in .net 2 - just tweaking it now. – HeavenCore Mar 10 '14 at 17:34
  • Ah, you can just iterate over the collection in "ImageCodecInfo.GetImageEncoders()" to find the correct item: foreach enc in ImageCodecInfo.GetImageEncoders() if enc.FormatID = ImageFormat.Jpeg.Guid then jpegParams = enc – Fredrik Johansson Mar 10 '14 at 17:39
  • That works great now, bounty awarded! I’ve edited your answer with my final code (runs fine in .net 2.0 and image quality is now consistent across all platforms) – HeavenCore Mar 10 '14 at 17:50
  • Really glad to hear that! One less problem in the world, and beautiful image resizing as a result :) – Fredrik Johansson Mar 10 '14 at 17:54
0

You should compare all dependencies and check IIS are using same versions. Mor info here: How to compare if the configuration is different for IIS?

If the same configuration are used, then it could be somthing related to graphic card, usually servers don't have specific graphic cards for image proccessing. In that case, you should configure GDI+ in your develop machine to not use acceleration or card specific instructions. Using Graphics Card instead of GDI+ for Image Manipulation

May be using another engine, and change the code help as a workaround: Replace GDI+ DrawImage with PInvoked GDI and transparent PNG's

Community
  • 1
  • 1
gavioto
  • 1,695
  • 2
  • 17
  • 38
-1

Are you using a proxy-cache web server in your production enviroment like Apache, Nginex, Varnish... between server and client?

It's very common that proxy server having modules like google page speed or similar, making compression over images to improve download speed. I that case, it should be trivial to write an exception rule.

gavioto
  • 1,695
  • 2
  • 17
  • 38
  • Nope, the image in question is served straight from IIS7 to the users browser - no proxy is involved. I can also confirm that the low quality image occurs if we visit the page locally on the web server itself. – HeavenCore Mar 10 '14 at 14:44