I need to add text to an image file. I need to read one image file (jpg,png,gif) and I need add one line text to it.
Well in GDI+ you would read in the file using a Image class and then use the Graphics class to add text to it. Something like:
Image image = Image.FromFile(#"c:\somepic.gif"); //or .jpg, etc...
Graphics graphics = Graphics.FromImage(image);
graphics.DrawString("Hello", this.Font, Brushes.Black, 0, 0);
If you want to save the file over the old one, the code has to change a bit as the Image.FromFile() method locks the file until it's disposed. The following is what I came up with:
FileStream fs = new FileStream(#"c:\somepic.gif", FileMode.Open, FileAccess.Read);
Image image = Image.FromStream(fs);
fs.Close();
Bitmap b = new Bitmap(image);
Graphics graphics = Graphics.FromImage(b);
graphics.DrawString("Hello", this.Font, Brushes.Black, 0, 0);
b.Save(#"c:\somepic.gif", image.RawFormat);
image.Dispose();
b.Dispose();
I would test this quite thoroughly though :)
Specifically for gifs to have a gif result, you should write on each frame like the following:
string originalImgPath = #"C:\test.gif";
Image IMG = Image.FromFile(originalImgPath);
FrameDimension dimension = new FrameDimension(IMG.FrameDimensionsList[0]);
int frameCount = IMG.GetFrameCount(dimension);
int Length = frameCount;
GifBitmapEncoder gEnc = new GifBitmapEncoder();
for (int i = 0; i < Length; i++)
{
// Get each frame
IMG.SelectActiveFrame(dimension, i);
var aFrame = new Bitmap(IMG);
// write one the selected frame
Graphics graphics = Graphics.FromImage(aFrame);
graphics.DrawString("Hello", new Font("Arial", 24, System.Drawing.FontStyle.Bold), System.Drawing.Brushes.Black, 50, 50);
var bmp = aFrame.GetHbitmap();
var src = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
bmp,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
// merge frames
gEnc.Frames.Add(BitmapFrame.Create(src));
}
string saveImgFile = #"C:\modified_test.gif"
using (FileStream fs2 = new FileStream(saveImgFile, FileMode.Create))
{
gEnc.Save(fs2);
}
I should have mentioned that getting gif frames from this post.
You can do this by using the Graphics object in C#. You can get a Graphics object from the picture ( image.CreateGraphics() - or something like this as I remember ) and the use some of the built in methods for adding text to it like : Graphycs.DrawString() or other related methods.
Related
I am trying to take multiple screenshots and save them to a file. However, screenshots are taken decently often, and in order to not lose any of them, my current program will simply create a new file for every screenshot. Ideally, the program would simply "append" the most recent screenshot onto a single file every time.
Here is the code:
static Rectangle bounds = Screen.GetBounds(Point.Empty);
static Size rectSize = new Size(bounds.Width, bounds.Height);
public static void takeScreenshot(string path, int iteration, string filetype)
{
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(Point.Empty, Point.Empty, new Size
(rectSize.Width * iteration, rectSize.Height));
}
bitmap.Save(path + filetype);
}
}
iteration was the amount of times the method has been called. I was trying to just shift the next screenshot over by one screenshot's width while keeping every other screenshot, but it seems to overwrite the file anyways. Is it possible to do this?
Try this..
string n = string.Format("{0:yyyy-MM-dd_hh-mm-ss-tt}", DateTime.Now);
bitmap.Save(path + n + filetype);
Like TheGeneral said, you will run out of memory quickly. And every time the file grows it has a chance of needing to be moved on disc wearing down your drives.
Also, opening and viewing huge image files can be very slow, your computer will not be able to handle it very quickly.
But here it is, maybe at least the AppendImage function could be used for something like generating particle sprite strips in games or something.
If you're appending a screenshot, I wouldn't append more than maybe 10 times, with 1080P monitor.
public enum AppendLocation
{
Before,
After
}
void AppendScreenToFile(string filename, AppendLocation appendLocation = AppendLocation.After)
{
Rectangle bounds = Screen.PrimaryScreen.Bounds;
using (Bitmap screenShot = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(screenShot))
{
g.CopyFromScreen(UpperLeftSource, Point.Empty, bounds.Size);
}
if (!File.Exists(filename))
{
screenShot.Save(filename, ImageFormat.Png);
return;
}
//Not using Image.FromFile as it blocks saving.
Image onDisc;
using (Stream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
onDisc = Image.FromStream(fs);
using (Image appended = appendLocation == AppendLocation.Before ? AppendImage(screenShot, onDisc) : AppendImage(onDisc, screenShot))
appended.Save(filename, ImageFormat.Png);
}
}
public enum AppendAxis
{
Vertical,
Horizontal
}
Bitmap AppendImage(Image image, Image append, AppendAxis axis = AppendAxis.Vertical)
{
Bitmap bitmap;
Rectangle destinationRect;
RectangleF imageBounds = new Rectangle(0, 0, image.Width, image.Height);
RectangleF appendRect = new Rectangle(0, 0, append.Width, append.Height);
pictureBox_Item4.BackgroundImage = image;
pictureBox_Item3.BackgroundImage = append;
switch (axis)
{
case AppendAxis.Vertical:
destinationRect = new Rectangle(0, image.Height, append.Width, append.Height);
bitmap = new Bitmap(image.Width, image.Height + append.Height);
break;
case AppendAxis.Horizontal:
destinationRect = new Rectangle(image.Width, 0, append.Width, append.Width);
bitmap = new Bitmap(image.Width + append.Width, image.Height);
break;
default:
throw new ArgumentException("AppendAxis is invalid.");
}
using (Graphics g = Graphics.FromImage(bitmap))
{
g.DrawImage(image, imageBounds, imageBounds, GraphicsUnit.Pixel);
g.DrawImage(append, destinationRect, appendRect, GraphicsUnit.Pixel);
return bitmap;
}
}
recently I started working on my project and unfortunately I have a problem. I want to get sqaures 5x5 from one image, count average color of them and then draw a circle to another Bitmap so I can get a result like this http://imageshack.com/a/img924/9093/ldgQAd.jpg
I have it done, but I can't save to file the Graphics object. I've tried many solutions from Stack, but none of them worked for me.
My code:
//GET IMAGE OBJECT
Image img = Image.FromFile(path);
Image newbmp = new Bitmap(img.Width, img.Height);
Size size = img.Size;
//CREATE NEW BITMAP WITH THIS IMAGE
Bitmap bmp = new Bitmap(img);
//CREATE EMPTY BITMAP TO DRAW ON IT
Graphics g = Graphics.FromImage(newbmp);
//DRAWING...
//SAVING TO FILE
Bitmap save = new Bitmap(size.Width, size.Height, g);
g.Dispose();
save.Save("file.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
The file 'file.bmp' is just a blank image. What am I doing wrong?
First, your Graphics object should be created from the target bitmap.
Bitmap save = new Bitmap(size.Width, size.Height) ;
Graphics g = Graphics.FromImage(save );
Second, flush your graphics before Save()
g.Flush() ;
And last, Dispose() after Save() (or use a using block)
save.Save("file.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
g.Dispose();
It should give you something like this :
Image img = Image.FromFile(path);
Size size = img.Size;
//CREATE EMPTY BITMAP TO DRAW ON IT
using (Bitmap save = new Bitmap(size.Width, size.Height))
{
using (Graphics g = Graphics.FromImage(save))
{
//DRAWING...
//SAVING TO FILE
g.Flush();
save.Save("file.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
}
}
I am trying to automatically generate a thumbnail from an image uploaded by a user but I keep getting the exception "Out of memory".
From what I understand the out of memory exception is thrown when you specify a starting position or a width/height that's outside of the image but even if I do this
var rct = new Rectangle(5, 5, 10, 10);
var whatever = bitmap.Clone(rct, bitmap.PixelFormat);
on an image that is 800x900 pixels I still get the "Out of memory" exception, I can't figure out what's wrong with it and I can't really get any good answers from other threads since everything regarding the OOM exception is just the mistake of going outside the image boundaries.
Does anyone have an explanation or solution to this?
EDIT: A bit more context
The loop for the images.
foreach (var blob in fileInfoList)
{
var blockBlobName = CheckExistence(BaseBlobUrl, blob.FileName, blob.FileNameWithoutExtension);
var image = new Image()
{
BlobUrl = Path.Combine(BaseBlobUrl, blockBlobName),
FullName = blob.FileName,
FileName = blob.FileNameWithoutExtension,
BlockBlobName = blockBlobName,
OwningOrganizationId = CurrentUser.UserOrganization.OrganizationId,
ThumbnailUrl = CreateThumbnail(blob.File, blockBlobName),
Name = "Whatever"
};
blobList.Add(image);
RepositoryFactory.AzureStorageRepository.SaveImage(blob.File, blockBlobName, blob.ContentType, CurrentUser.UserOrganization.Organization.Id);
}
The method that is being called by each image in the list to generate the thumbnail.
public string CreateThumbnail(byte[] b, string parentImageName)
{
Bitmap bmp;
using (var ms = new MemoryStream(b))
{
bmp = new Bitmap(ms);
}
Bitmap thumbnail = bmp;
Rectangle rect = new Rectangle(5, 5, 10, 10);
if (bmp.Width > bmp.Height)
thumbnail = bmp.Clone(rect, bmp.PixelFormat);
else if (bmp.Height > bmp.Width)
thumbnail = bmp.Clone(new Rectangle((bmp.Height/2) - (bmp.Width/2), 0, bmp.Width, bmp.Width), bmp.PixelFormat);
byte[] bmpArray = new byte[0];
using (var ms = new MemoryStream())
{
finalCrop.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
ms.Close();
bmpArray = ms.ToArray();
}
var name = "Thumbnail_" + parentImageName;
RepositoryFactory.AzureStorageRepository.SaveThumbnail(bmpArray, name, "jpg/image", CurrentUser.UserOrganization.Organization.Id);
return BaseBlobUrl + "thumbnails/" + name;
}
I think the problem you're getting here is that Bitmaps need to be disposed. If one gets garbage collected without releasing its underlying unmanaged content (i.e. being disposed), then that memory can not be recovered...
Also note that you will need to dispose both bitmaps. Best thing to do is wrap them in a using, something like this:
using (var ms = new MemoryStream(b))
{
using (Bitmap bmp = new Bitmap(ms))
using (Bitmap thumbnail = bmp)
{
Rectangle rect = new Rectangle(5, 5, 10, 10);
if (bmp.Width > bmp.Height)
thumbnail = bmp.Clone(rect, bmp.PixelFormat);
else if (bmp.Height > bmp.Width)
thumbnail = bmp.Clone(new Rectangle((bmp.Height / 2) - (bmp.Width / 2), 0, bmp.Width, bmp.Width), bmp.PixelFormat);
byte[] bmpArray = new byte[0];
using (var ms = new MemoryStream())
{
finalCrop.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
ms.Close();
bmpArray = ms.ToArray();
}
var name = "Thumbnail_" + parentImageName;
RepositoryFactory.AzureStorageRepository.SaveThumbnail(bmpArray, name, "jpg/image", CurrentUser.UserOrganization.Organization.Id);
return BaseBlobUrl + "thumbnails/" + name;
}
}
It is worth noting that using will call Dispose() on it's target, even if an exception is thrown (thus having the same finally type functionality as #Scott Chamberlain answer
Okay so I found an answer to my problem by disposing the Bitmaps.
After this bit of code
Bitmap thumbnail = bmp;
I added
bmp.Dispose();
And during debugging I noticed that none of the properties from bmp were left in the Bitmap called thumbnail so I changed it into the following
Bitmap thumbnail = new Bitmap(bmp);
Thank you all for telling me to dispose the Bitmaps!
Here is the correct way to properly dispose of your objects.
Bitmap bmp = null;
Bitmap thumbnail = null;
try
{
using (var ms = new MemoryStream(b))
{
bmp = new Bitmap(ms);
}
Rectangle rect = new Rectangle(5, 5, 10, 10);
if (bmp.Width > bmp.Height)
thumbnail = bmp.Clone(rect, bmp.PixelFormat);
else if (bmp.Height > bmp.Width)
thumbnail = bmp.Clone(new Rectangle((bmp.Height/2) - (bmp.Width/2), 0, bmp.Width, bmp.Width), bmp.PixelFormat);
else
thumbnail = bmp;
byte[] bmpArray = new byte[0];
using (var ms = new MemoryStream())
{
finalCrop.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
ms.Close();
bmpArray = ms.ToArray();
}
var name = "Thumbnail_" + parentImageName;
RepositoryFactory.AzureStorageRepository.SaveThumbnail(bmpArray, name, "jpg/image", CurrentUser.UserOrganization.Organization.Id);
return BaseBlobUrl + "thumbnails/" + name;
}
finally
{
if(bmp != null)
bmp.Dispose();
if(thumbnail != null)
thumbnail.Dispose(); //If bmp and thumbnail are the same object this is still safe to do.
}
Use a try/finally block to ensure that even in the event of a error your objects get disposed. Doing the extra Bitmap thumbnail = new Bitmap(bmp); in your answer just makes a extra bitmap you are forgetting to dispose.
I received this Exception when the Rectangle used to crop the image was partly outside the bounds of the image.
I have some images that I need to do some crude re-size work on -- For the purpose of this example lets just say I need to increase the width and height of a given image by 4 pixels.
I am unsure why the call to Graphics.DrawImage() is throwing an OOM -- Any advice here would be greatly appreciated.
class Program
{
static void Main(string[] args)
{
string filename = #"c:\testImage.png";
// Load png from stream
FileStream fs = new FileStream(filename, FileMode.Open);
Image pngImage = Image.FromStream(fs);
fs.Close();
// super-hacky resize
Graphics g = Graphics.FromImage(pngImage);
g.DrawImage(pngImage, 0, 0, pngImage.Width + 4, pngImage.Height + 4); // <--- out of memory exception?!
// save it out
pngImage.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
}
}
I just had the same problem. However fixing the size of the output Graphics did not solve my problem. I realized that I tried to use a very high quality for the drawing the image which was consuming too much memory when I use the code on a lot of images.
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;
after commenting these lines out the code worked perfectly.
Your Graphics surface is only big enough for the original-sized image. You need to create a new image of the correct size and use it as the source of your Graphics object.
Image newImage = new Bitmap(pngImage.Width + 4, pngImage.Height+4);
Graphics g = Graphics.FromImage(newImage);
That likely will not accomplish what you'd like to do seeing as how the image is the same size as the one specified by FromImage, instead you can use the Bitmap class:
using (var bmp = new Bitmap(fileName))
{
using (var output = new Bitmap(
bmp.Width + 4, bmp.Height + 4, bmp.PixelFormat))
using (var g = Graphics.FromImage(output))
{
g.DrawImage(bmp, 0, 0, output.Width, output.Height);
output.Save(outFileName, ImageFormat.Png);
}
}
Can you try this fix?
class Program
{
static void Main(string[] args)
{
string filename = #"c:\testImage.png";
// Load png from stream
FileStream fs = new FileStream(filename, FileMode.Open);
Image pngImage = Image.FromStream(fs);
fs.Close();
// super-hacky resize
Graphics g = Graphics.FromImage(pngImage);
pngImage = pngImage.GetThumbnailImage(image.Width, image.Height, null, IntPtr.Zero);
g.DrawImage(pngImage, 0, 0, pngImage.Width + 4, pngImage.Height + 4); // <--- out of memory exception?!
// save it out
pngImage.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
}
}
Inspired by this question: Help to resolve 'Out of memory' exception when calling DrawImage
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.