How to convert byte[] to Bitmap on Xamarin forms

ifoxbrifoxbr Member ✭✭
edited July 4 in Xamarin.Forms

Need help to have Bitmap class on Xamarin Forms. Maybe DependencyService for Android and IOS.
I found that Xamarin forms does not have System.Drawing.Bitmap, then I installed Nuget "System.Drawing.Common" from Microsoft. Then program compiles without errors. But when running I receive this error:
**Could not resolve type with token 01000116 from typeref (expected class 'System.Drawing.Bitmap' in assembly 'System.Drawing.Common, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51')
**
Tried a Google Search but no solution.
What I am trying to do, is receive a Photo in byte[] string and convert it to Bitmap to scale and print.
This is the code:

Function is called as: byte[] _buffer = PrintImage(PHOTO);

       public byte[] PrintImage(byte[] PHOTO)
        {
            Bitmap bmp;
            using (var ms = new MemoryStream(PHOTO))
            {
                bmp = new Bitmap(ms);
            }

            BitmapData data = GetBitmapData(bmp);        

            BitArray dots = data.Dots;
            byte[] width = BitConverter.GetBytes(data.Width);

            int offset = 0;
            MemoryStream stream = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(stream);

            // center command
            bw.Write(27);
            bw.Write('a');
            bw.Write(1);

            // print image
            bw.Write((char)0x1B);
            bw.Write('@');

            bw.Write((char)0x1B);
            bw.Write('3');
            bw.Write((byte)24);

            while (offset < data.Height)
            {
                bw.Write((char)0x1B);
                bw.Write('*');         // bit-image mode
                bw.Write((byte)33);    // 24-dot double-density
                bw.Write(width[0]);  // width low byte
                bw.Write(width[1]);  // width high byte

                for (int x = 0; x < data.Width; ++x)
                {
                    for (int k = 0; k < 3; ++k)
                    {
                        byte slice = 0;
                        for (int b = 0; b < 8; ++b)
                        {
                            int y = (((offset / 8) + k) * 8) + b;
                            // Calculate the location of the pixel we want in the bit array.
                            // It'll be at (y * width) + x.
                            int i = (y * data.Width) + x;

                            // If the image is shorter than 24 dots, pad with zero.
                            bool v = false;
                            if (i < dots.Length)
                            {
                                v = dots[i];
                            }
                            slice |= (byte)((v ? 1 : 0) << (7 - b));
                        }

                        bw.Write(slice);
                    }
                }
                offset += 24;
                bw.Write((char)0x0A);
            }
            // Restore the line spacing to the default of 30 dots.
            bw.Write((char)0x1B);
            bw.Write('3');
            bw.Write((byte)30);

            bw.Flush();
            byte[] bytes = stream.ToArray();
            return bytes;       // logo + Encoding.Default.GetString(bytes);
        }

        public BitmapData GetBitmapData(Bitmap bmp) 
        {
            //using (var bitmap = (Bitmap)Bitmap.FromFile(bmpFileName))
            using (var bitmap = bmp)
            {
                var threshold = 127;
                var index = 0;
                double multiplier = 300; // scale
                double scale = (double)(multiplier / (double)bitmap.Width);
                int xheight = (int)(bitmap.Height * scale);
                int xwidth = (int)(bitmap.Width * scale);
                var dimensions = xwidth * xheight;
                var dots = new BitArray(dimensions);

                for (var y = 0; y < xheight; y++)
                {
                    for (var x = 0; x < xwidth; x++)
                    {
                        var _x = (int)(x / scale);
                        var _y = (int)(y / scale);
                        var color = bitmap.GetPixel(_x, _y);
                        var luminance = (int)(color.R * 0.3 + color.G * 0.59 + color.B * 0.11);
                        dots[index] = (luminance < threshold);
                        index++;
                    }
                }

                return new BitmapData()
                {
                    Dots = dots,
                    Height = (int)(bitmap.Height * scale),
                    Width = (int)(bitmap.Width * scale)
                };
            }
        }

        public class BitmapData
        {
            public BitArray Dots
            {
                get;
                set;
            }

            public int Height
            {
                get;
                set;
            }

            public int Width
            {
                get;
                set;
            }
        }

Best Answers

Answers

  • Amar_BaitAmar_Bait DZMember ✭✭✭✭✭
    edited July 4

    I think System.Drawing.Common targets .NET Core, not .NET Standard.

    I've been using ImageSharp for all my image processing, it works very well, .NET Standard based and a lot faster than System.Drawing. Give it a try. Documentation.

    using (var image = Image.LoadPixelData<Rgba32>(imageBytes))
    {
        // get pixel at 100,100
        var pixel = image[100,100];
    
        // resize image and make it grayscale
        image.Mutate(x => x
             .Resize(image.Width / 2, image.Height / 2)
             .Grayscale());
    
        var newImageBytes = MemoryMarshal.AsBytes(image.GetPixelSpan()).ToArray();
    }
    
  • LandLuLandLu Member, Xamarin Team Xamurai

    @ifoxbr Why do you want to convert the byte array to bitmap? If you want to consume it directly on Forms you can use stream:

    MemoryStream stream = new MemoryStream(bytes);
    MyImage.Source = ImageSource.FromStream(() => stream);
    

    If you want to consume it on each platform, you can pass the bytes to the specific platform and convert it there.
    On Android:

    public void ConvertImage(byte[] imageArray)
    {
        Bitmap bitmap = BitmapFactory.DecodeByteArray(imageArray, 0, imageArray.Length);
        var imgView = new ImageView(this.Context);
        imgView.SetImageBitmap(bitmap);
    }
    

    Generally, we use NSData on iOS, so there's no need to convert it to bitmap:

    public void ConvertImage(byte[] imageArray)
    {
        NSData data = NSData.FromArray(imageArray);
        UIImageView imgView = new UIImageView();
        imgView.Image = new UIImage(data);
    }
    
  • ifoxbrifoxbr Member ✭✭
    edited July 5

    Thank you** LandLu**, but need on Xamarin forms, get file from byte[], resize it according to my variable double scale factor and get return byte[] to be printed.

    Amar-Bait, installed SixLabors.ImageSharp, but did not worked for me, see picture. My "Image" does not have LoadPixelData method.

  • ifoxbrifoxbr Member ✭✭

    Hello ..
    In short, only what I need is: make code posted works on Xamarin Forms. It works fine on Windows.
    Convert byte[] Photo to "Bitmap" on Xamarin Forms and get its members Dots, Height, Width like code posted.

    Thank for any help

  • ifoxbrifoxbr Member ✭✭

    Hello, Amar-Bait I greatly appreciate your help, found SkiaSharp which has all methods I need.
    Regards

  • JassimRahmaJassimRahma USMember ✭✭✭✭

    I am having problem converting Image Source from FFImageLoading to Stream

    I am trying this:

    viewModel.Image = ImageSource.FromStream(() =>
    {
        var stream = GetStreamFromImageSourceAsync((StreamImageSource)ImageStudent.Source).Result;
        return stream;
    });
    

    My Convert:

    private static async Task<Stream> GetStreamFromImageSourceAsync(StreamImageSource imageSource, CancellationToken cancellationToken = default(CancellationToken))
    {
        if (imageSource.Stream != null)
        {
            return await imageSource.Stream(cancellationToken);
        }
    
        return null;
    }
    

    **and this is my Model:
    **

    public class ImageViewModel : INotifyPropertyChanged
    {
        private ImageSource image;
        public ImageSource Image
        {
            get { return image; }
            set
            {
                image = value;
    
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Image"));
                }
            }
        }
    
        private string imageStream;
    
        public string ImageStream
        {
            get { return imageStream; }
            set
            {
                imageStream = value;
    
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("ImageStream"));
                }
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    }
    

    Kindly help..

Sign In or Register to comment.