0

The code below is throwing error:

System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x80072EE4

on code line:
Dim bdDecoder As BitmapDecoder = BitmapDecoder.Create(streamPhoto, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None)

Why? The requested URL exists and returns a 200. Google is not helping on this one.

Private Sub ResizeAndSave(ByVal imageURL As String)
    Dim imgRequest As WebRequest = WebRequest.Create(imageURL)
    Dim imgResponse As WebResponse = imgRequest.GetResponse()

    Dim strThumbnail As String = "success.png"
    Dim streamPhoto As Stream = imgResponse.GetResponseStream()
    Dim memStream As New MemoryStream
    streamPhoto.CopyTo(memStream)
    Dim bfPhoto As BitmapFrame = ReadBitmapFrame(memStream)
    Dim nThumbnailSize As Integer = 200, nWidth As Integer, nHeight As Integer
    If bfPhoto.Width > bfPhoto.Height Then
        nWidth = nThumbnailSize
        nHeight = CInt(bfPhoto.Height * nThumbnailSize / bfPhoto.Width)
    Else
        nHeight = nThumbnailSize
        nWidth = CInt(bfPhoto.Width * nThumbnailSize / bfPhoto.Height)
    End If
    Dim bfResize As BitmapFrame = FastResize(bfPhoto, nWidth, nHeight)
    Dim baResize As Byte() = ToByteArray(bfResize)

    Dim saveToPath As String = Server.MapPath(ConfigurationManager.AppSettings("products_photospath")) + "\49\" + strThumbnail

    File.WriteAllBytes(saveToPath, baResize)
    Console.WriteLine("Resize done!!!")
End Sub

Private Shared Function FastResize(bfPhoto As BitmapFrame, nWidth As Integer, nHeight As Integer) As BitmapFrame
    Dim tbBitmap As New TransformedBitmap(bfPhoto, New ScaleTransform(nWidth / bfPhoto.Width, nHeight / bfPhoto.Height, 0, 0))
    Return BitmapFrame.Create(tbBitmap)
End Function

Private Shared Function ToByteArray(bfResize As BitmapFrame) As Byte()
    Using msStream As New MemoryStream()
        Dim pbdDecoder As New PngBitmapEncoder()
        pbdDecoder.Frames.Add(bfResize)
        pbdDecoder.Save(msStream)
        Return msStream.ToArray()
    End Using
End Function

Private Shared Function ReadBitmapFrame(streamPhoto As Stream) As BitmapFrame
    Dim bdDecoder As BitmapDecoder = BitmapDecoder.Create(streamPhoto, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None)
    Return bdDecoder.Frames(0)
End Function


Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    ResizeAndSave("http://cdn2.emobassets.eu/media/catalog/product/1/1/1116220.jpg")


End Sub

UPDATE 1

Ok, images are now saved. But the resize only works correctly on the example .png file, and NOT on the .jpg file. The Dell logo is saved in 200x199 px preserving transparancy, which is perfect. The other file 1116220.jpgis saved in 625x441px...why is that not respecting the desired max width/height of 200px?

I checked into the code and the only difference I can spot is that for the png file the dimensions are a round number:

bfPhoto.Width   2000
bfPhoto.Height  1994

After FastResize executes that becomes

bfResize.Width  200
bfResize.Height 199

Where for the jpg the dimensions are

bfPhoto.Width   982,719970703125
bfPhoto.Height  695,039978027344

After FastResize executes that becomes

bfResize.Width  200
bfResize.Height 141,119995117188

So I tried to see if it was related to that image and tried with another jpg file: https://upload.wikimedia.org/wikipedia/commons/d/d8/Square-1_solved.jpg

Where for the jpg the dimensions are

bfPhoto.Width   600
bfPhoto.Height  600

After FastResize executes that becomes

bfResize.Width
bfResize.Height

That does work, so now I know it's not related to the file being a .jpg. It seems to be related to the dimensions of image 1116220.jpg, but I don't know if I can work around that by scaling differently or in some other way...

My code:

Private Sub ResizeAndSave(ByVal maxWidth As Integer, ByVal maxHeight As Integer, ByVal imageURL As String)
    Dim imgRequest As WebRequest = WebRequest.Create(imageURL)
    Dim imgResponse As WebResponse = imgRequest.GetResponse()

    Dim streamPhoto As Stream = imgResponse.GetResponseStream()
    Dim memStream As New MemoryStream
    streamPhoto.CopyTo(memStream)
    memStream.Position = 0
    Dim bfPhoto As BitmapFrame = ReadBitmapFrame(memStream)

    Dim newWidth, newHeight As Integer
    Dim scaleFactor As Double
    If bfPhoto.Width > maxWidth Or bfPhoto.Height > maxHeight Then
        If bfPhoto.Width > maxWidth Then
            scaleFactor = maxWidth / bfPhoto.Width
            newWidth = Math.Round(bfPhoto.Width * scaleFactor, 0)
            newHeight = Math.Round(bfPhoto.Height * scaleFactor, 0)
        End If
        If newHeight > maxHeight Then
            scaleFactor = maxHeight / newHeight
            newWidth = Math.Round(newWidth * scaleFactor, 0)
            newHeight = Math.Round(newHeight * scaleFactor, 0)
        End If
    End If
    Dim bfResize As BitmapFrame = FastResize(bfPhoto, newWidth, newHeight)

    Dim baResize As Byte() = ToByteArray(bfResize)

    Dim strThumbnail As String = "success" + Date.Now.Second.ToString + ".png"
    Dim saveToPath As String = Server.MapPath(ConfigurationManager.AppSettings("products_photospath")) + "\49\" + strThumbnail

    File.WriteAllBytes(saveToPath, baResize)

End Sub

Private Shared Function FastResize(bfPhoto As BitmapFrame, nWidth As Integer, nHeight As Integer) As BitmapFrame
    Dim tbBitmap As New TransformedBitmap(bfPhoto, New ScaleTransform(nWidth / bfPhoto.Width, nHeight / bfPhoto.Height, 0, 0))
    Return BitmapFrame.Create(tbBitmap)
End Function

Private Shared Function ToByteArray(bfResize As BitmapFrame) As Byte()
    Using msStream As New MemoryStream()
        Dim pbdDecoder As New PngBitmapEncoder()
        pbdDecoder.Frames.Add(bfResize)
        pbdDecoder.Save(msStream)
        Return msStream.ToArray()
    End Using
End Function

Private Shared Function ReadBitmapFrame(streamPhoto As Stream) As BitmapFrame
    Dim bdDecoder As BitmapDecoder = BitmapDecoder.Create(streamPhoto, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None)
    Return bdDecoder.Frames(0)
End Function


Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    ResizeAndSave(200, 200, "https://upload.wikimedia.org/wikipedia/commons/8/82/Dell_Logo.png")
    ResizeAndSave(200, 200, "http://cdn2.emobassets.eu/media/catalog/product/1/1/1116220.jpg")
End Sub
Adam
  • 6,041
  • 36
  • 120
  • 208
  • Why not use BitmapImage instead of BitmapFrame and set DecodePixelWidth or DecodePixelHeight instead of transforming the bitmap? – Clemens Feb 02 '16 at 21:48
  • As a note, when you create a BitmapFrame or BitmapImage from a Stream, you should always set `BitmapCacheOption.OnLoad` to make sure that the bitmap is created immediately, i.e. before the Stream is closed. – Clemens Feb 02 '16 at 21:53
  • @Clemens: thanks, that would be a performance tweak and not a fix for my issue right? Anyway, where would I do that specifically in my code? – Adam Feb 02 '16 at 22:15
  • Replace the ReadBitmapFrame method by creating a BitmapImage. Btw, a solution to your specific problem might be to copy the WebResponse Stream to a MemoryStream, then create the bitmap from that MemoryStream. – Clemens Feb 02 '16 at 22:28
  • @Clemens: Thanks! I tried both your suggestions (see my updated post) but am still stuck...what am I missing? – Adam Feb 02 '16 at 22:44
  • 1
    Copying the responseStream into a memorystream would mean to create a new MemoryStream, then call `streamPhoto.CopyTo(memoryStream)` – Clemens Feb 02 '16 at 22:48
  • @Clemens: Thanks, I did not know that method. I now get the error `The image header is unrecognized. (Exception from HRESULT: 0x88982F61)` though...I was reading here and it might be a bug: http://code.logos.com/blog/2008/08/image_format_error_when_loading_from_a_stream.html Any suggestions on working around this one? – Adam Feb 02 '16 at 22:58
  • You certainly didn't create the bitmap from the MemoryStream, but still from the response stream. It looks pretty much like this is not your code and you have just copied it from somewhere and don't know at all how it works, right? I'd strongly recommend that you try to understand in detail what exactly your code does before trying to fix it. – Clemens Feb 02 '16 at 23:03
  • @Clemens: Thank you. I didn't copy code, my explanation to you was unclear I guess. First I tried the `CopyTo` method, leaving my initial code intact. What I tried was replacing `Dim streamPhoto As Stream = imgResponse.GetResponseStream()` with your suggestions. For clarification I've now added that to the initial code. That code throws the `image header` error (I think you can test this as the image I use is public). Next I tried rewriting all code (not using memory stream at all) and that's what you see in update 1. Update 1 has errors because of type conversions I'm unsure how to resolve... – Adam Feb 03 '16 at 00:39
  • 1
    In addition to `CopyTo` you should also set the MemoryStream's `Position` property to `0`, or call `memoryStream.Seek(0, SeekOrigin.Begin)`. before passing it to ReadBitmapFrame. – Clemens Feb 03 '16 at 06:53
  • Besides that, my initial suggestion to use DecodePixelWidth or DecodePixelHeight would only make sense if you would know in advance if the image is portrait or landscape mode. As you want to resize the image to a width or height of 200, whatever is smaller, doing this with DecodePixelWidth or -Height would need to first set one of both, then decode the bitmap from the MemoryStream and if the aspect ratio is not what you expected, set the other DecodePixel property and decode the bitmap once again. – Clemens Feb 03 '16 at 07:00
  • @Clemens Thanks! Images are now saved..but the resizing only works on .png files and not on .jpg files...what is that? Please see my new code under update 1. – Adam Feb 03 '16 at 16:09
  • @Clemens, I'm getting off topic from the initial question, which actually has already been resolved. I'll upvote your very(!) helpful suggestions and have started a post more specific to the new issue here: http://stackoverflow.com/questions/35191841/wpf-image-resizing-and-storing-works-on-most-images-but-fails-to-resize-or-save – Adam Feb 04 '16 at 03:15

0 Answers0