locked
Bitmap decode byte array. Skia Decoder returns false RRS feed

  • Question

  • User8355 posted

    Hi everybody,

    I'm having a problem when transforming a byte array image to a bitmap. Some images are not shown and others yes(always the same images). All images are RGB, have the same dimensions and are got from a Blob field in a SQLite database.

    Debugging the process of transforming the byte array, the following error message is logged out:

    [skia] --- decoder->decode returned false

    I'm trying to modify my code

    // Loads a Bitmap from a byte array
    public static Bitmap bytesToBitmap (byte[] imageBytes)
    {
        Bitmap bitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
    
        return bitmap;
    }
    

    However, I am unable to change some workarounds I've found here from java to C#. Could anyone help me?

    Thanks.

    Thursday, May 8, 2014 10:04 AM

All replies

  • User44709 posted

    If it's JPG, then it's probably this bug.

    If possible, you might want to pre-convert all your images to PNG. If that's not possible, you might try loading it as a .NET Image, save it to a byte array as PNG and then load it into the Android Bitmap. Like this maybe? (this was all off the top of my head, but should give you the idea if it doesn't compile.)

    System.Drawing.Image image = null;
    using (MemoryStream ms = new MemoryStream(imageBytes))
    {
        image = System.Drawign.Image.FromStream(ms);         
    }
    
    byte[] newData = null;
    using (MemoryStream ms = new MemoryStream())
    {
       image.Save(ms, ImageFormat.Png);
       newData = ms.ToArray();
    }
    
    Bitmap bitmap = BitmapFactory.DecodeByteArray(newData , 0, newData .Length);
    
    return bitmap;
    
    Thursday, May 8, 2014 12:52 PM
  • User8355 posted

    Thank you @PeterDavis,

    Regarding the first thing you mention, it would be difficult for me to pre-convert the images because I have them only as blob files into a database, not as files.

    In reference of the second possible solution, is the type System.Drawing.Image available in Xamarin.Android? I can't find it and System.Drawing.Image doesn't compile.

    If there's no other solution I would have to transform all the blobs as jpg files, transform them into pngs and re-insert them into the database I use in my app. I hope not having to do all this process.

    Thursday, May 8, 2014 2:04 PM
  • User44709 posted

    Well that's embarrassing. That's what I get for coding off the top of my head. Sorry, didn't have a compiler handy. I have another possible solution. Give me a bit of time to get it put together.

    Thursday, May 8, 2014 2:20 PM
  • User44709 posted

    Sorry, I've been struggling to come up with a workable solution, but I'm not having a lot of luck. I'll need to think about this some more. Here's the issue: The solution is to create the FlushedInputStream as described here.

    You would then create the FlushedInputStream from your byte array and then use it. This gets around the bug in the decoder.

    The problem is you can't implement FlushedInputStream in C# because Xamarin wants a Stream passed to DecodeStream. It then wraps that in a regular InputStream which will then cause the same failure.

    The solution, I believe, is going to be to create java library that has the FlushedInputStream and loads your image via the FlushedInputStream and then returns the Android Bitamp to your app. I haven't done this before, so I'd be hesitant to give much more in the way of specifics, but short of using some other image library to handle your images, I don't see a better solution.

    Thursday, May 8, 2014 2:56 PM
  • User8355 posted

    Thanks a lot @PeterDavis?,

    Neither have I created a java library. I will try, if nobody gives other solutions, to do that searching how to do it.

    Thanks a lot for your time. And if someone else knows a solution, please share it ;)

    Thursday, May 8, 2014 3:11 PM
  • User44709 posted

    @RogierKoning? This may interest you. Good timing.

    https://github.com/nostra13/Android-Universal-Image-Loader

    and

    http://forums.xamarin.com/discussion/comment/52918/#Comment_52918

    Thursday, May 8, 2014 7:04 PM
  • User30820 posted
    using System;
    using System.Net;
    using Android.Graphics;
    using System.Reflection;
    
    namespace Spotlight
    {
        class ImageFetchr
        {
            public static Bitmap GetImage(string url)
            {
                Bitmap _image = null;
    
                try
                {
                    byte[] _byteArray;
                    using(var _wClient = new WebClient())
                    {
                        _byteArray = _wClient.DownloadData(url);
                    }
                    _image = BitmapFactory.DecodeByteArray(_byteArray, 0, _byteArray.Length);
                }
                catch(Exception _exception)
                {
                    MethodBase _currentMethod = MethodInfo.GetCurrentMethod ();
                    Console.WriteLine(String.Format("CLASS : {0}; METHOD : {1}; EXCEPTION : {2}"
                        , _currentMethod.DeclaringType.FullName
                        , _currentMethod.Name
                        , _exception.Message));
                }
                return _image;
            }
        }
    }
    
    Thursday, May 8, 2014 8:13 PM
  • User1669 posted

    Could you not decode the bitmap from a memory stream? Something like (off the top of my head):

    using(var ms = new MemoryStream(imageBytes))
    {
       var myBitmap = BitmapFactory.DecodeStream(ms);
    }
    

    You may need to modify it as I'm doing this without a compiler handy at the moment.

    Thursday, May 8, 2014 9:05 PM
  • User44709 posted

    @rmacias? and @naynishchaughule.5750? - You cannot use DecodeByteArray at all and you can't DecodeStream with a regular stream because of the bug that's causing the problem. The bug has to do with poor error handling in the decoder. The JPEGs he's reading are truncated and causing the decoding to fail. You can use DecodeStream, but you need to implement something like this https://github.com/nostra13/Android-Universal-Image-Loader/blob/master/library/src/com/nostra13/universalimageloader/core/assist/FlushedInputStream.java And unfortunately you can't do that in C#. It has to be done in Java because of the way Mono wraps DecodeStream.

    Thursday, May 8, 2014 9:20 PM
  • User8355 posted

    Thanks a lot @rmacias?, @naynishchaughule.5750? and @PeterDavis?. DecodeByteArray doesn't work as well because the bug is in the BitmapFactory.Decode function.

    I'm trying to use the universalImageLoader project. However, after doing the parse of the java project I've realized that it only allows to load images from an uri. So I'm trying to modify the java project adding a

    public class FlushedInputStream extends ByteArrayInputStream

    which overrides the skip method to avoid the bug.

    I will continue on Monday and I'll tell you how was it.

    Friday, May 9, 2014 4:10 PM
  • User8355 posted

    I've been working on this for several days without success. I haven't been able to bind the project UniversalImageLoader modificated by me to a C# dll.

    What I've modified on the UniversalImageLoader project is what follows:

    interface ImageDownloader: Added method

    InputStream getStream(String imageUri, byte[] byteArray) throws IOException;

    BaseImageDownloader: Implemented method

    @Override
    public InputStream getStream(String imageUri, byte[] byteArray) throws IOException {
          return new FlushedInputStream(imagebytes);
    }
    

    FlushedInputStream: Created class

    public class FlushedInputStream extends ByteArrayInputStream {
    
    
        private ByteArrayInputStream in;
    
        public FlushedInputStream(final byte[] imagebytes) {
    
            super(imagebytes);
            in= new ByteArrayInputStream(imagebytes);
        }
    
    
        @Override
        public long skip(final long n) {
            long totalBytesSkipped = 0L;
            //If totalBytesSkipped is equal to the required number 
            //of bytes to be skipped i.e. "n"
            //then come out of the loop.
            while (totalBytesSkipped < n) {
                //Skipping the left out bytes.
                long bytesSkipped = in.skip(n - totalBytesSkipped);
                //If number of bytes skipped is zero then 
                //we need to check if we have reached the EOF
                if (bytesSkipped == 0L) {
                    //Reading the next byte to find out whether we have reached EOF.
                    int bytesRead = read();
                    //If bytes read count is less than zero (-1) we have reached EOF.
                    //Cant skip any more bytes.
                    if (bytesRead < 0) {
                        break;  // we reached EOF
                    } else {
                        //Since we read one byte we have actually 
                        //skipped that byte hence bytesSkipped = 1
                        bytesSkipped = 1; // we read one byte
                    }
                }
                //Adding the bytesSkipped to totalBytesSkipped
                totalBytesSkipped += bytesSkipped;
            }        
            return totalBytesSkipped;
        }
    }
    

    Now I have my personalized UniversalImageLoader.jar but when I try to bind it to C# with a Xamarin Studio binding project I get the following error:

    Error CS0234: The type or namespace nameDiskLruCache' does not exist in the namespace Com.Nostra13.Universalimageloader.Cache.Disc.Impl.Ext'. Are you missing an assembly reference? (CS0234) (UniversalImageLoader)

    DiskLruCache is a file I haven't modified at all.

    I'm exhausted of 'fighting' against this issue. Could anyone help me get rid of this error to be able to generate the binding successfully? Thanks.

    Wednesday, May 14, 2014 2:26 PM
  • User8355 posted

    I was able to generate the dll library from a jar of the UniversalImageLoader project.

    Editing a file inside the xamarin binding project called api.xml did the trick. On the line 180 it was the line:

    <class abstract="false" deprecated="not deprecated" extends="java.lang.Object" extends-generic-aware="java.lang.Object" final="true" name="DiskLruCache" static="false" visibility="">
    

    And adding the visibility like this the binding was created:

    <class abstract="false" deprecated="not deprecated" extends="java.lang.Object" extends-generic-aware="java.lang.Object" final="true" name="DiskLruCache" static="false" visibility="public">
    

    However. The UniversalImageLoader has the same problem with the images. The images are not shown from an byteArray except the ones that where being shown properly in C# code as well.

    So unfortunately UniversalImageLoader hasn't done the trick.

    Friday, May 23, 2014 3:47 PM
  • User8355 posted

    Finally got it working!!!

    I had to make a workaround like this:

    /// Loads a Bitmap from a byte array
    public static Bitmap bytesToUIImage (byte[] bytes)
    {
    
        if (bytes == null)
            return null;
    
        Bitmap bitmap;
    
    
        var documentsFolder = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
    
        //Create a folder for the images if not exists
        System.IO.Directory.CreateDirectory(System.IO.Path.Combine (documentsFolder, "images"));
    
        string imatge = System.IO.Path.Combine (documents, "images", "image.jpg");
    
    
        System.IO.File.WriteAllBytes(imatge, bytes.Concat(new Byte[]{(byte)0xD9}).ToArray());
    
        bitmap = BitmapFactory.DecodeFile(imatge);
    
        return bitmap;
    
    }
    

    Note that the file created was missing the ending byte of a .jpeg file "D9" so I had to add it manually. I know for fact that my images had this byte included, and I also tried to generate the bitmap through the byteArray adding "D9" with BitmapFactory.DecodeByteArray but it didn't work.

    So, the only workaround that works for me is creating a file from the byteArray and decoding that file. Hope it could help someone in the future.

    Thanks a lot @rmacias?, @naynishchaughule.5750? and of course @PeterDavis? for your time.

    Friday, May 23, 2014 4:12 PM
  • User60709 posted

    @RogierKoning? , Could You Please, post your biding project of the Universal Image Loader (all the changes you had to do). Cause i'm trying to get it right, but I only got the version 1.8.4 working.

    Thanks

    Tuesday, July 8, 2014 10:20 PM
  • User35499 posted

    Yeah if you have this working, posting that code would be super helpful.

    Friday, August 22, 2014 7:05 PM
  • User205483 posted

    Hi guys! I may be late on this, but I went ahead and just used this library and resolved my issue: https://components.xamarin.com/gettingstarted/universalimageloader

    Monday, June 27, 2016 2:23 PM
  • User240105 posted

    @RogierKoning? , thank you very much for your code. Your answer helped me to find my the problem with jpegs. However, the end-of-image is two bytes 0xFF, 0xD9. Also there is no need to save an new image bytes array.

    using (var stream = Assets.Open("480054_s800.jpg"))
    {
        var bytesWithEnd = ReadFully(stream).Concat(new byte[] { (byte)0xFF, (byte)0xD9 }).ToArray();
        var bitmap = BitmapFactory.DecodeByteArray(bytesWithEnd, 0, bytesWithEnd.Length);
        var image = this.FindViewById<ImageView>(Resource.Id.fail_image);
        image.SetImageBitmap(bitmap);
    }
    
    public static byte[] ReadFully(Stream input)
    {
        using (System.IO.MemoryStream ms = new MemoryStream())
        {
            input.CopyTo(ms);
            return ms.ToArray();
        }
    }
    
    Tuesday, October 31, 2017 7:53 AM