简体   繁体   中英

Reducing an image size for compact framework

I'm looking for a method that allows me to reduce the size (in byte) of an image that I've captured from the screen than send to another pc like a remote desktop (that is actually my project purpose, btw this part is already done, I've some trouble just to compressing the image).
The computer where the program has to work on has Compact Framework 2.0 (Windows CE 6.0) and I cannot change (@ work we use these computer on some machines, so it's a bit hard to change OS and Framework).
Anyway, this is the method I use to capture the screen followed by the actual method that I use to change the pixel color depth.

    int DefaultMaxScreenDiskSize = 212500;

    private bool StreamScreen()
    {
        Rectangle bounds = Screen.PrimaryScreen.Bounds;
        IntPtr hdc = GetDC(IntPtr.Zero);

        Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format16bppRgb555);

        using (MemoryStream ms = new MemoryStream())
        {
            using (Graphics graphics = Graphics.FromImage(bitmap))
            {
                IntPtr dstHdc = graphics.GetHdc();
                BitBlt(dstHdc, 0, 0, bounds.Width, bounds.Height, hdc, 0, 0, Enums.RasterOperation.SRC_COPY);
                graphics.ReleaseHdc(dstHdc);
            }

            bitmap.Save(ms, ImageFormat.Png);
            int length = ms.ToArray().Length;

            if (length > DefaultMaxScreenDiskSize)
            {
                Bitmap nBitmap = ApplyDecreaseColourDepth(128, bitmap);
                nBitmap.Save(ms, ImageFormat.Png);
            }

            using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
                currentImgHash = Convert.ToBase64String(sha1.ComputeHash(ms.ToArray()));

            if (oldImgHash != currentImgHash || ForceScreenRefresh)
            {
                ReleaseDC(IntPtr.Zero, hdc);

                if (ms.ToArray() != null)
                {
                    while (!SendScreen(ZlibStream.CompressBuffer(ms.ToArray()))) ;

                    oldImgHash = currentImgHash;

                    ForceScreenRefresh = false;

                    return true;
                }
            }
        }

        return false;
    }

This is the other method that I use, the one that change the pixel depth. But, it's a bit slow and heavy on the system, so I don't know how to change it.

    public Bitmap ApplyDecreaseColourDepth(int offset, Bitmap bitmapImage)
    {
        for (int y = 0; y < bitmapImage.Height; y++)
        {
            for (int x = 0; x < bitmapImage.Width; x++)
            {
                Color pixelColor = bitmapImage.GetPixel(x, y);

                int R = Math.Max(0, (pixelColor.R + offset / 2) / offset * offset - 1);
                int G = Math.Max(0, (pixelColor.G + offset / 2) / offset * offset - 1);
                int B = Math.Max(0, (pixelColor.B + offset / 2) / offset * offset - 1);

                bitmapImage.SetPixel(x, y, Color.FromArgb(R, G, B));
            }
        }

        return bitmapImage;
    }

Any idea how to change the depth pixel or compressing the bitmap? I've tried to compress the image but (that's a .PNG) it change just for few bytes (ex. normal size ms.length = 38689, with compressing it ms.length = 38489)

Unfortunately I haven't got a solution for the compression problem. But I remembered an article from CodeProject that I've came across a while back that described exactly the problem with GetPixel() and SetPixel() being incredibly slow. This limitation can be overcome with the following piece of code. In order to make it compile you need to allow unsafe code in the build options of the respective project.

public unsafe Bitmap ApplyDecreaseColourDepth(int offset, Bitmap bitmapImage)
{
  BitmapData data = bitmapImage.LockBits(
    new Rectangle(0, 0, bitmapImage.Width, bitmapImage.Height),
    ImageLockMode.ReadWrite,
    PixelFormat.Format24bppRgb);
  const int bytesPerPixel = 3;

  Byte* scan0 = (Byte*)data.Scan0.ToPointer();
  Int32 stride = data.Stride;

  for (int y = 0; y < bitmapImage.Height; y++)
  {
    Byte* row = scan0 + (y * stride);
    for (int x = 0; x < bitmapImage.Width; x++)
    {
      Int32 bIndex = x * bytesPerPixel;
      Int32 gIndex = bIndex + 1;
      Int32 rIndex = bIndex + 2;

      Byte pixelR = row[rIndex];
      Byte pixelG = row[gIndex];
      Byte pixelB = row[bIndex];

      row[rIndex] = (Byte)Math.Max(0, (pixelR + offset / 2) / offset * offset - 1);
      row[gIndex] = (Byte)Math.Max(0, (pixelG + offset / 2) / offset * offset - 1);
      row[bIndex] = (Byte)Math.Max(0, (pixelB + offset / 2) / offset * offset - 1);
    }
  }

  bitmapImage.UnlockBits(data);
  return bitmapImage;
}

This is untested so please test before releasing it into production! The article that I was talking about (and that I used to change your code) can be found under https://www.codeproject.com/Articles/617613/Fast-pixel-operations-in-NET-with-and-without-unsa

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM