I have made a program which monitors the clipboard and when a new image is copied, it gets saved to a file. Now when I copy an image from Paint.net which has transparent background, the background gets filled with white but when I copy the same image from Firefox it saves fine. Here is the test image i'm using: https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png and here is the code which I use to get the image from clipboard:
private Image GetImageFromClipboard()
{
if (Clipboard.GetDataObject() == null) return null;
if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Dib))
{
var dib = ((System.IO.MemoryStream)Clipboard.GetData(DataFormats.Dib)).ToArray();
var width = BitConverter.ToInt32(dib, 4);
var height = BitConverter.ToInt32(dib, 8);
var bpp = BitConverter.ToInt16(dib, 14);
if (bpp == 32)
{
var gch = GCHandle.Alloc(dib, GCHandleType.Pinned);
Bitmap bmp = null;
try
{
var ptr = new IntPtr((long)gch.AddrOfPinnedObject() + 40);
bmp = new Bitmap(width, height, width * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, ptr);
return new Bitmap(bmp);
}
finally
{
gch.Free();
if (bmp != null) bmp.Dispose();
}
}
}
return Clipboard.ContainsImage() ? Clipboard.GetImage() : null;
}
Using the above code, I save the image:
Image img = GetImageFromClipboard();
img.RotateFlip(RotateFlipType.Rotate180FlipX);
img.Save("test.png");
I can also see the transparency in a picturebox when the image is copied from Firefox but not when copied from Paint.net.
Related
Capturing a screen shot is quite easy but cropping is a different story, or at least appears so. We are attempting to emulate the solution here in Xamarin using SkiaSharp (System.Drawing not supported in Xamarin).
Currently we are able to capture the screenshot and return the image BUT if we crop our image the image returned is all black.
How do we crop a captured screenshot correctly?
NOTE*: image = image.Subset(rec); under the "crop image" section is how we are trying to crop.
iOS screenshot
public byte[] Capture()
{
var capture = UIScreen.MainScreen.Capture();
using (NSData data = capture.AsPNG())
{
var bytes = new byte[data.Length];
Marshal.Copy(data.Bytes, bytes, 0, Convert.ToInt32(data.Length));
return bytes;
}
}
Droid screenshot
public byte[] Capture()
{
var rootView = context.Window.DecorView.RootView;
using (var screenshot = Bitmap.CreateBitmap(
rootView.Width,
rootView.Height,
Bitmap.Config.Argb8888))
{
var canvas = new Canvas(screenshot);
rootView.Draw(canvas);
using (var stream = new MemoryStream())
{
screenshot.Compress(Bitmap.CompressFormat.Png, 90, stream);
return stream.ToArray();
}
}
}
Attempting to crop captured screenshot
// Use this function to crop a screen shot to a specific element.
public byte[] test(byte[] screenshotData, View element)
{
// locate IntPtr to byte[] of uncropped screenshot
GCHandle gch = GCHandle.Alloc(screenshotData, GCHandleType.Pinned);
IntPtr addr = gch.AddrOfPinnedObject();
// assign initial bounds
SKImageInfo info = new SKImageInfo((int)App.Current.MainPage.Width,
(int)App.Current.MainPage.Height);
// create initial pixel map
using SKPixmap pixmap = new SKPixmap(info, addr);
// Release
gch.Free();
// create bitmap
using SKBitmap bitmap = new SKBitmap();
// assign pixel data
bitmap.InstallPixels(pixmap);
// create surface
using SKSurface surface = SKSurface.Create(info);
// create a canvas for drawing
using SKCanvas canvas = surface.Canvas;
// draw
canvas.DrawBitmap(bitmap, info.Rect);
// get an image subset to save
SKImage image = surface.Snapshot();
SKRectI rec = new SKRectI((int)element.Bounds.Left, (int)element.Bounds.Top,
(int)element.Bounds.Right, (int)element.Bounds.Bottom);
// crop image
image = image.Subset(rec);
byte[] bytes = SKBitmap.FromImage(image).Bytes;
image.Dispose();
return bytes;
}
EDIT: Alternative solution attempt (not working)
// Use this function to crop a screen shot to a specific element.
public byte[] test(byte[] screenshotData, View element)
{
// locate IntPtr to byte[] of uncropped screenshot
GCHandle gch = GCHandle.Alloc(screenshotData, GCHandleType.Pinned);
IntPtr addr = gch.AddrOfPinnedObject();
// assign initial bounds
SKImageInfo info = new SKImageInfo((int)App.Current.MainPage.Width,
(int)App.Current.MainPage.Height);
// create bitmap
SKBitmap bitmap = new SKBitmap();
bitmap.InstallPixels(info, addr);
// boundaries
SKRect cropRect = new SKRect((int)element.Bounds.Left, (int)element.Bounds.Top,
(int)element.Bounds.Right, (int)element.Bounds.Bottom);
SKBitmap croppedBitmap = new SKBitmap((int)cropRect.Width,
(int)cropRect.Height);
SKRect dest = new SKRect(0, 0, cropRect.Width, cropRect.Height);
SKRect source = new SKRect(cropRect.Left, cropRect.Top,
cropRect.Right, cropRect.Bottom);
// draw with destination and source rectangles
// to extract a subset of the original bitmap
using SKCanvas canvas = new SKCanvas(croppedBitmap);
canvas.DrawBitmap(bitmap, source, dest);
return croppedBitmap.Bytes;
//return bitmap.Bytes;
}
iOS solution - As seen here.
// crop the image, without resizing
private UIImage CropImage(UIImage sourceImage, int crop_x, int crop_y, int width, int height)
{
var imgSize = sourceImage.Size;
UIGraphics.BeginImageContextWithOptions(new System.Drawing.SizeF(width, height), false, 0.0f);
var context = UIGraphics.GetCurrentContext();
var clippedRect = new RectangleF(0, 0, width, height);
context.ClipToRect(clippedRect);
var drawRect = new RectangleF(-crop_x, -crop_y, imgSize.Width, imgSize.Height);
sourceImage.Draw(drawRect);
var modifiedImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return modifiedImage;
}
Android solution - getNavigationBarSize(context) as seen here
// crop the image, without resizing
private byte[] CropImage(byte[] screenshotBytes, int top)
{
Android.Graphics.Bitmap bitmap = Android.Graphics.BitmapFactory.DecodeByteArray(
screenshotBytes, 0, screenshotBytes.Length);
int viewStartY = (int)(top * 2.8f);
int viewHeight = (int)(bitmap.Height - (top * 2.8f));
var navBarXY = getNavigationBarSize(context);
int viewHeightMinusNavBar = viewHeight - navBarXY.Y;
Android.Graphics.Bitmap crop = Android.Graphics.Bitmap.CreateBitmap(bitmap,
0, viewStartY,
bitmap.Width, viewHeightMinusNavBar
);
bitmap.Dispose();
using MemoryStream stream = new MemoryStream();
crop.Compress(Android.Graphics.Bitmap.CompressFormat.Jpeg, 100, stream);
return stream.ToArray();
}
*NOTE: Unsure why a multiplication of 2.8 is required, though this works correctly. It should be stated testing was only done in the Android emulator. Perhaps it's emulator specific.
*NOTE2: x = 0 and width is equal to the entire width of the screen as that's per our requirement. Likewise top is Element.Bounds.Top.
I am doing a small project where I have to display all images from database to Listview.
I am passing image id,width,and height as querystring parameter.
<asp:Image ID="Image1" runat="server" ImageUrl='<%#"~/Handler/ImageHandler.ashx?ImgHeight=150&ImgWidth=200&ImgID="+Eval("Image_ID")%>' Height="150px" Width="200px"/>
public void ProcessRequest (HttpContext context)
{
string imgwidth = context.Request.QueryString["ImgWidth"];
string imgheight = context.Request.QueryString["ImgHeight"];
string imageid = context.Request.QueryString["ImgID"];
if (imgwidth != string.Empty && imgheight != string.Empty && (imgwidth != null && imgheight != null))
{
if (!System.Web.UI.WebControls.Unit.Parse(imgwidth).IsEmpty && !System.Web.UI.WebControls.Unit.Parse(imgheight).IsEmpty)
{
//create unit object for height and width. This is to convert parameter passed in differen unit like pixel, inch into generic unit.
System.Web.UI.WebControls.Unit widthUnit=System.Web.UI.WebControls.Unit.Parse(imgwidth);
System.Web.UI.WebControls.Unit heightUnit = System.Web.UI.WebControls.Unit.Parse(imgheight);
//AFTER THIS ???
}
}
}
when I display image directly from database some images get stretch and doesn't look good, this is because the image size is large. So I need to display the images just for thumbsnail in image gallery.
You could use GetThumbnailImage Method
refer the code
public Void GenerateImage(int iWidth,int iHeight,byte[] ImageBytes)
{
System.Drawing.Image image = byteArrayToImage(ImageBytes)
// create the actual thumbnail image
System.Drawing.Image thumbnailImage = image.GetThumbnailImage(iWidth, iHeight, new System.Drawing.Image.GetThumbnailImageAbort(ThumbnailCallback), IntPtr.Zero);
// make a memory stream to work with the image bytes
MemoryStream imageStream = new MemoryStream();
// put the image into the memory stream
thumbnailImage.Save(imageStream, System.Drawing.Imaging.Imageformat.Jpeg);
// make byte array the same size as the image
byte[] imageContent = new Byte[imageStream.Length];
// rewind the memory stream
imageStream.Position = 0;
// load the byte array with the image
imageStream.Read(imageContent, 0, (int)imageStream.Length);
// return byte array to caller with image type
Response.ContentType = "image/jpeg";
Response.BinaryWrite(imageContent);
}
public bool ThumbnailCallback()
{
return true;
}
public Image byteArrayToImage(byte[] byteArrayIn)
{
MemoryStream ms = new MemoryStream(byteArrayIn);
Image returnImage = Image.FromStream(ms);
return returnImage;
}
You could use this code to give the image a new size, while preserving the aspect ratio:
public static Image ResizeCanvas(Image original, Size newSize, Color background)
{
int xStart = (newSize.Width / 2) - (original.Width / 2);
int yStart = (newSize.Height / 2) - (original.Height / 2);
// Create blank canvas
Bitmap resizedImg = new Bitmap(newSize.Width, newSize.Height);
Graphics gfx = Graphics.FromImage(resizedImg);
// Fill canvas
gfx.FillRectangle(new SolidBrush(background), new Rectangle(new Point(0, 0), newSize));
gfx.DrawImage(original, xStart, yStart, original.Width, original.Height);
return resizedImg;
}
I am trying to create a bitmap and all I have is the savedUri and the imagePath. I believe I am retrieving the imagePath correctly because when I go and look at the actual properties of the photos of the photo and the logged path they are the same so I believe it is a problem in the way I'm creating the bitmap. Here is the code I use:
if (_bitmap == null)
{
string imagePath = null;
try
{
imagePath = GetRealPathFromURI(_saveUri);
LogManager.Info("********the image path is", imagePath);
}
catch
{
imagePath = _saveUri.Path;
}
// First decode with inJustDecodeBounds=true to check dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.InJustDecodeBounds = true;
BitmapFactory.DecodeFile(imagePath, options);
var width = (int)Math.Round(_outputX * 0.5);
var heigth = (int)Math.Round(_outputY * 0.5);
options.InSampleSize = BitmapTools.CalculateInSampleSize(options, width, width * heigth);
options.InJustDecodeBounds = false;
options.InDensity = 160;
try
{
_bitmap = BitmapFactory.DecodeFile(imagePath, options);
}
catch
{
GC.Collect();
_bitmap = BitmapFactory.DecodeFile(imagePath, options);
}
}
if (_bitmap == null)
LogManager.Info("********the bitmap width is: ", "well bloody hell the bitmap is null");
else
LogManager.Info("********the bitmap width is: ", _bitmap.Width.ToString());
On the last two lines the bitmap is never null but it always has a width of 1 and when I display it there is no image. This is for creating, displaying and saving an image on Monodroid but that isn't too relevant to this question as it is.
Hello stackoverflow community.
I do not want to draw each pictureBoxes image to Panel1 but instead I want to add each pictureBox image(pictureBox1,pictureBox2,pictureBox3) to a Bitmap, then set that Bitmap to Panel1's background Image.
Is this possible?
thanks
Here's the method I've found to combine multiple images into one bitmap
public static System.Drawing.Bitmap CombineBitmap(Image[] files)
{
//read all images into memory
List<System.Drawing.Bitmap> images = new List<System.Drawing.Bitmap>();
System.Drawing.Bitmap finalImage = null;
try
{
int width = 0;
int height = 0;
foreach (Image image in files)
{
//create a Bitmap from the file and add it to the list
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(image);
//update the size of the final bitmap
width += bitmap.Width;
height = bitmap.Height > height ? bitmap.Height : height;
images.Add(bitmap);
}
//create a bitmap to hold the combined image
finalImage = new System.Drawing.Bitmap(width, height);
//get a graphics object from the image so we can draw on it
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(finalImage))
{
//set background color
g.Clear(System.Drawing.Color.Black); // Change this to whatever you want the background color to be, you may set this to Color.Transparent as well
//go through each image and draw it on the final image
int offset = 0;
foreach (System.Drawing.Bitmap image in images)
{
g.DrawImage(image,
new System.Drawing.Rectangle(offset, 0, image.Width, image.Height));
offset += image.Width;
}
}
return finalImage;
}
catch (Exception ex)
{
if (finalImage != null)
finalImage.Dispose();
throw ex;
}
finally
{
//clean up memory
foreach (System.Drawing.Bitmap image in images)
{
image.Dispose();
}
}
}
You'll need to create an array of images for this to work, then you could try this
Image[] Files = new Image[3]; // Create a new array of maximum 3 indices
Files[0] = pictureBox1.Image;
Files[1] = pictureBox2.Image;
Files[2] = pictureBox3.Image;
panel1.BackgroundImage = CombineBitmap(Files); //Add the combined bitmap to the BackgroundImage
Thanks,
Have a great day and sorry for misunderstanding :)
I have a Grayscale image which I am pulling from the DB (it is in Bytes). I want to draw some boxes using graphics object on it and then display this image. This is what was coded before -
public byte[] DrawOverlayOnGreyscaleImage(byte[] buffer, List<ImagingTransaction.ImagingTransactionField> TransactionFieldList, BLLImageType imageType)
{
//Load image into a bitmap object via first going into a MemoryStream
MemoryStream msBitmap = new MemoryStream(buffer);
Bitmap BitmapObj = null;
BitmapObj = new Bitmap(msBitmap);
int bmwidth = BitmapObj.Width;
int bmheight = BitmapObj.Height;
// draw some text on top
Graphics g = Graphics.FromImage(BitmapObj);
Because of the changes in the way the image is now generated (Format8bppIndexed) the Graphics object threw an exception - "A graphics object cannot be created from an image that has an indexed pixel format". So I changed the Bitmap to be Format24bppRGB. Now, there is no exception. But after I draw boxes on the image and try to save it, the image is all black. This is because in case of "Grayscale" images R=G=B. This is lost after making it non indexed. I change the Bitmap to be again Indexed (Format8bbIndexed). Change the ColorPalette, but nothing helps. I still get the image to be totally black. Please help. My new code is as follows -
public byte[] DrawOverlayOnGreyscaleImage(byte[] buffer, List<ImagingTransaction.ImagingTransactionField> TransactionFieldList, BLLImageType imageType)
{
//Load image into a bitmap object via first going into a MemoryStream
MemoryStream msBitmap = new MemoryStream(buffer);
Bitmap BitmapObj = null;
BitmapObj = new Bitmap(msBitmap);
int bmwidth = BitmapObj.Width;
int bmheight = BitmapObj.Height;
Bitmap tmp = new Bitmap(BitmapObj.Width, BitmapObj.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(tmp);
Rectangle srcRect;
int RectWidth;
int RectHeight;
Pen myPen = new Pen(System.Drawing.Color.Red, 3);
foreach (ImagingTransaction.ImagingTransactionField Field in TransactionFieldList)
{
// first, do they want to see the rectangles
if (imageType == BLLImageType.GreyScale_With_FieldRectangles || imageType == BLLImageType.GreyScale_With_FieldRectangles_And_Field_Data)
{
RectWidth = Field.LowerRightX - Field.UpperLeftX;
RectHeight = Field.LowerRightY - Field.UpperLeftY;
// sanity check for negative values
if (RectWidth <= 0)
RectWidth = 10;
if (RectHeight <= 0)
RectHeight = 10;
srcRect = new Rectangle(Field.UpperLeftX, Field.UpperLeftY, RectWidth, RectHeight);
g.DrawRectangle(myPen, srcRect);
}
// now, do they want to see the text to the lower right of the field
if (imageType == BLLImageType.GreyScale_With_Field_Data || imageType == BLLImageType.GreyScale_With_FieldRectangles_And_Field_Data)
{
g.DrawString(Field.FieldValue, new Font("Tahoma", 12), Brushes.Red, new PointF(Field.LowerRightX, Field.LowerRightY)); ;
}
}
MemoryStream msBitmapWithRectangle = new MemoryStream();
// Save to memory using the Jpeg format
Bitmap tmp2 = new Bitmap(tmp.Width, tmp.Height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
ColorPalette pal = tmp2.Palette;
for (int i = 0; i < pal.Entries.Length; i++)
{
// create greyscale color table
pal.Entries[i] = Color.FromArgb(i, i, i);
}
tmp2.Palette = pal;
tmp2.Save(msBitmapWithRectangle, System.Drawing.Imaging.ImageFormat.Jpeg);
// read to end
byte[] ByteArrayWithRectangle = msBitmapWithRectangle.GetBuffer();
// cleanup
tmp.Dispose();
tmp2.Dispose();
BitmapObj.Dispose();
msBitmap.Close();
msBitmapWithRectangle.Close();
return ByteArrayWithRectangle;
}
Seems that tmp2 is created but never filled with original bitmap, so you create a perfectly black rectangle.
Try creating a new bitmap in the BPP and size you need, and draw the image, and then draw the rectange.