I'm writing a CSS sprite engine in C#, however I'm having a few issues. I create the master image, set all the properties to that then iterate the sprites and draw those to the master image. However when I come to save the master image it only appears to be just the empty master image with transparent background and none of the sprites. I'm very confused at where I'm going wrong.
The code I'm using is:
// Work out the width/height required
int max_width = 0;
int max_height = 0;
foreach(SpriteInformation sprite in sprites) {
if (max_width < (sprite.Left + greatest_width)) max_width = sprite.Left + greatest_width;
if (max_height < (sprite.Top + greatest_height)) max_height = sprite.Top + greatest_height;
}
// Create new master bitmap
Bitmap bitmap = new Bitmap(max_width,max_height,PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bitmap);
// Set background color
SolidBrush brush;
if (cbxBackground.Checked) {
if (txtColor.Text == "") {
brush = new SolidBrush(Color.Black);
} else {
brush = new SolidBrush(pnlColor.BackColor);
}
} else {
if (txtColor.Text == "") {
brush = new SolidBrush(Color.White);
} else {
brush = new SolidBrush(pnlColor.BackColor);
}
}
//graphics.FillRectangle(brush,0,0,bitmap.Width,bitmap.Height);
bitmap.MakeTransparent(brush.Color);
graphics.Clear(brush.Color);
// Copy images into place
ImageAttributes attr = new ImageAttributes();
//attr.SetColorKey(brush.Color,brush.Color);
foreach(SpriteInformation sprite in sprites) {
Rectangle source = new Rectangle(0,0,sprite.Width,sprite.Height);
Rectangle dest = new Rectangle(sprite.Left,sprite.Top,sprite.Width,sprite.Height);
graphics.DrawImage(sprite.Sprite,dest,0,0,sprite.Width,sprite.Height,GraphicsUnit.Pixel,attr);
}
// Save image
string format = ddlFormat.Items[ddlFormat.SelectedIndex].ToString();
if (format == "PNG") {
dlgSave.Filter = "PNG Images|*.png|All Files|*.*";
dlgSave.DefaultExt = ",png";
if (dlgSave.ShowDialog() == DialogResult.OK) {
bitmap.Save(dlgSave.FileName,ImageFormat.Png);
}
} else if (format == "JPEG") {
} else {
}
What are sprite.Left and Top? If they aren't 0 that may be a problem. I think you have dest and source the wrong way round?
http://msdn.microsoft.com/en-us/library/ms142045.aspx
Try a simpler DrawImage variant if you haven't already.
e.g. DrawImage(sprite.Sprite,0,0)
You draw to "graphics", but then then you save "bitmap"? I'm not sure if that graphics internally works with your original "bitmap" object...
Related
i created a windows form c# application. It has feature to crop image. After cropping image. it show preview in other picturebox. My problem is the cropping place little bit blur in picture box. .Here is picture to understand what i meant...I want to fix it
enter image description here
here is the code i used.
{
string root = #"C:\FuelPass";
// If directory does not exist, create it.
if (!System.IO.Directory.Exists(root))
{
System.IO.Directory.CreateDirectory(root);
}
Bitmap bmp = new Bitmap(previewimg.Width, previewimg.Height);
previewimg.DrawToBitmap(bmp, new Rectangle(0, 0, previewimg.Width, previewimg.Height));
pictureBox2.DrawToBitmap(bmp, new Rectangle(pictureBox2.Location.X - previewimg.Location.X, pictureBox2.Location.Y - previewimg.Location.Y, pictureBox2.Width, pictureBox2.Height));
String savename="";
// DateTime dt = new DateTime();
// bmp.Save(#"C:\Fuelpass\" +dt.ToString()+ " .jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
bmp.Save(#"C:\Fuelpass\output-" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
Form2 f2 = new Form2();
f2.Show();
// MessageBox.Show("Image has been saved");
// SaveFileDialog dialog = new SaveFileDialog();
// dialog.Filter = "JPG(*.JPG)|*.jpg";
// if (dialog.ShowDialog() == DialogResult.OK)
// {
// previewimg.Image.Save(dialog.FileName);
// pictureBox2.Image.Save(dialog.FileName);
// }
// }
}
private void PictureBoxLoad_MouseUp_1(object sender, MouseEventArgs e)
{
//pictureBox1.Image.Clone();
try
{
if (IsMouseDown == true)
{
LocationX1Y1 = e.Location;
IsMouseDown = false;
if (Rect != null)
{
Bitmap bit = new Bitmap(PictureBoxLoad.Image, PictureBoxLoad.Width, PictureBoxLoad.Height);
Bitmap cropImg = new Bitmap(Rect.Width, Rect.Height);
Graphics g = Graphics.FromImage(cropImg);
g.DrawImage(bit, 0, 0, Rect, GraphicsUnit.Pixel);
pictureBox2.Image = cropImg;
}
}
}
catch { }
}
private Rectangle GetRect()
{
Rect = new Rectangle();
Rect.X = Math.Min(LocationXY.X, LocationX1Y1.X);
Rect.Y = Math.Min(LocationXY.Y, LocationX1Y1.Y);
Rect.Width = Math.Abs(LocationXY.X - LocationX1Y1.X);
Rect.Height = Math.Abs(LocationXY.Y - LocationX1Y1.Y);
return Rect;
}
I am having an odd issue whereby when I then try to iterate over the pixels of the PictureBox or save the bmp it is just all black.
The intention of the tool is that you select a font, size, and style, and then it loops over the ASCII chars of that font and shows each character in the picture box, and converts the pixel data into a HEX array so I can use them on LCD displays.
The main part of the tool works whereby it is correctly looping through the ASCII chars and displaying them in the picture box but after each char is drawn to the picture box and I then try to iterate over the pixels of the PictureBox every pixel is returned as 0,0,0 RGB "black" and if I save the bmp which was drawn to the PictureBox that too is all black, but again I can see the bmp of that char correct drawn in the PictureBox yet the PictureBox data and bmp data does not match what I see in the PictureBox itself, I am truly lost as to why I am unable to correctly iterate or save the bmp or PictureBox.
I have tried not using async functions which is not ideal as I want the UI to be free, and I have tried various means to read the pixels and save the bmp but the result is the same. I hope to ask if anyone knows why I am getting this odd behavior and the solution to the issue.
Regards from Ed.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace generateFonts
{
public partial class Form1 : Form
{
string font = "";
float fontSize = 0;
FontStyle fontStyle = FontStyle.Regular;
public Form1()
{
InitializeComponent();
comboBox1.SelectedIndex = 0;
comboBox2.SelectedIndex = 0;
comboBox3.SelectedIndex = 0;
font = comboBox1.SelectedItem.ToString();
fontSize = Convert.ToInt32(comboBox2.SelectedItem);
fontStyle = FontStyle.Regular;
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
font = comboBox1.SelectedItem.ToString();
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
fontSize = Convert.ToInt32(comboBox2.SelectedItem);
}
private void comboBox3_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox3.SelectedIndex == 0)
fontStyle = FontStyle.Regular;
else if (comboBox3.SelectedIndex == 1)
fontStyle = FontStyle.Italic;
else if(comboBox3.SelectedIndex == 2)
fontStyle = FontStyle.Bold;
}
private async void button1_Click(object sender, EventArgs e)
{
await Task.Run(() => StartProcess(1));
}
private void StartProcess(int runs)
{
// Font
Font myFont = new Font(font, fontSize, fontStyle);
List<string> bytes = new List<string>();
string[] bits = { "0", "0", "0", "0", "0", "0", "0", "0" };
byte bitPos = 0;
for (byte i = 32; i < 126; i++)
{
//Create a Image-Object on which we can paint
Image bmp = new Bitmap(200, 200);
//Create the Graphics-Object to paint on the Bitmap
Graphics g = Graphics.FromImage(bmp);
string c = Convert.ToChar(i).ToString();
//Get the perfect Image-Size so that Image-Size = String-Size
SizeF size = g.MeasureString(c, myFont);
//Use this to become better Text-Quality on Bitmap.
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
//Here we draw the string on the Bitmap
g.DrawString(c, myFont, new SolidBrush(Color.Black), 0, 0);
if (!Directory.Exists("FontBmps"))
Directory.CreateDirectory("FontBmps");
this.Invoke((MethodInvoker)delegate ()
{
pictureBox2.Width = Convert.ToInt32(size.Width);
pictureBox2.Height = Convert.ToInt32(size.Height);
pictureBox2.Image = bmp; // <--- this is working and the picturebox shows the bmp correctly
bmp.Save("FontBmps/" + i + "_" + font + "_" + fontSize + "px_" + fontStyle + ".bmp", ImageFormat.Bmp); // <--- error here: this saves a black square instead of the bmp i see displayed in the picturebox GUI ??
// Even if i save the picturebox itself that too is just a black square instead of the correct image shown in the GUI ??
// now convert the bmp to a HEX array of pixels
for (int h = 0; h < pictureBox2.Height; h++)
{
for (int w = 0; w < pictureBox2.Width; w++)
{
Color colour = (pictureBox2.Image as Bitmap).GetPixel(w, h);
if (colour.R == 0 && colour.G == 0 && colour.B == 0)
{
bits[bitPos] = "1";
}
else
{
bits[bitPos] = "0"; // <-- never hits me, again for some reason the bmp or picturebox is all black pixels data but i see it correctly show in the picturebox GUI ??
}
if (bitPos < 7)
bitPos++;
else
{
//string bitStr = bits.ToString();
//string hex = Convert.ToByte(bitStr, 2).ToString("x2");
//bytes.Add(hex);
bitPos = 0;
for(byte n = 0; n < 8; n++)
{
bits[n] = "0";
}
}
}
}
if (bitPos != 0)
{
// TO DO...
}
});
Thread.Sleep(500); // <--- i have this just to see it displaying the chars but i have removed it with no effect to the issue
}
// now add List to a file in the correct format
foreach (string str in bytes)
{
Console.WriteLine(str);
// TO DO...
}
}
}
}
I believe the image is black, but some parts have transparency. That is, you would need to check Alpha (Color.A). What you see in the picture box, would be the background color of the picture box where it is transparent.
You won't see the transparency in the saved file, given that the ImageFormat.Bmp does not support transparency. Try Png instead, which supports transparency (and has lossless compression).
Alternatively, you can use Graphics.Clear to have the image be the color you want for background (white, I guess) before drawing to it.
Aside from that, I'll suggest to use bmp instead of (pictureBox2.Image as Bitmap), and use Bitmap.LockBits. That would improve performance.
This might be useful for reference: Converting a bitmap to monochrome. See also C# - Faster Alternatives to SetPixel and GetPixel for Bitmaps for Windows Forms App.
I am using a UltraExpandableGroupBox in my WinForms application. And I am using the Office2003 style with it. However, I would like to reverse the Expanded and Collapsed Indicator images used. I tried to export the images from the .isl file, but these images don't seems to be among the images exported. How do I access these images?
When the ViewStyle property for the UltraExpandableGroupBox control is set to the GroupBoxViewStyle.Office2003 the Expanded/Collapsed indicator uses an embedded bitmap.
Code below demonstrates how this bitmap can be obtained from the assembly on the runtime and can be used to reverse the current Expanded/Collapsed indicators:
private void ReverseImage_Click(object sender, EventArgs e)
{
var imageName = "GroupBox.ExpansionIndicator_Chevron.bmp";
System.IO.Stream stream = typeof(UltraExpandableGroupBox).Module.Assembly.GetManifestResourceStream(typeof(UltraExpandableGroupBox), imageName);
if (stream != null)
{
// The source bitmap has 7x10px size.
var image = Bitmap.FromStream(stream);
// Converting the image to 16x16 pixels
ultraExpandableGroupBox1.ExpansionIndicatorExpanded = ResizeImage(image, 16, 16);
// Rotation
using (var bmp = new Bitmap(image))
{
bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
image = bmp.Clone() as Image;
// Exporting bitmap to a file
bmp.Save(#".\" + imageName, ImageFormat.Bmp);
}
ultraExpandableGroupBox1.ExpansionIndicatorCollapsed = ResizeImage(image, 16, 16);
}
}
public static Image ResizeImage(Image image, int new_height, int new_width)
{
var dest = new Bitmap(new_width, new_height);
var g = Graphics.FromImage(dest);
g.InterpolationMode = InterpolationMode.High;
g.DrawImage(image, (dest.Width - image.Width)/2, (dest.Height-image.Height)/2);
return dest;
}
Exported to a file the Expanded/Collapsed indicator bitmap looks like on the picture below:
You can achieve this with simple DrawFilter. Set to your UltraExpandableGroupBox DraFilter property like this:
this.myUltraExpandableGroupBox.DrawFilter = new MyDrawFilter(expandedIndicator, collapsedInidcator);
Then create a new class named MyDrawFilter and let it inherit IUIElementDrawFilter. Your draw filter class may look like this:
public class MyDrawFilter : IUIElementDrawFilter
{
Image expandedIndicator;
Image collapsedIndicator;
public MyDrawFilter(Image expandedIndicator, Image collapsedInidcator)
{
this.expandedIndicator = expandedIndicator;
this.collapsedIndicator = collapsedInidcator;
}
public bool DrawElement(DrawPhase drawPhase, ref UIElementDrawParams drawParams)
{
if (drawParams.Element is GroupBoxExpansionIndicatorUIElement)
{
// if groupbox is expanded change the image with one provided in the constructor
// as expandedIndicator
if ((drawParams.Element.Control as UltraExpandableGroupBox).Expanded)
{
(drawParams.Element.ChildElements[0] as ImageUIElement).Image = this.expandedIndicator;
}
// else gropbox is collapsed change the image with one provided in the constructor
// as collapsedIndicator
else
{
(drawParams.Element.ChildElements[0] as ImageUIElement).Image = this.collapsedIndicator;
}
}
return false;
}
public DrawPhase GetPhasesToFilter(ref UIElementDrawParams drawParams)
{
// filter when GroupBoxExpansionIndicatorUIElement should be drawn. This element has
// one child UIElement of ImageUIElement type. This UIElement holds the expansion
// indicator image.
if (drawParams.Element is GroupBoxExpansionIndicatorUIElement)
{
// we return BeforeDrawChildeElements in order to be able to change the image
return DrawPhase.BeforeDrawChildElements;
}
return DrawPhase.None;
}
}
Here is my Code, I am trying to stitch two pictures together and as soon as I get to line 42 "Image img = Image.FromFile(file.FullName);" I get an out of memory error. What should I do?
namespace Practicing_Stiching
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void cmdCombine_Click(object sender, EventArgs e)
{
//Change the path to location where your images are stored.
DirectoryInfo directory = new DirectoryInfo(#"C:\Users\Elder Zollinger\Desktop\Images");
if (directory != null)
{
FileInfo[] files = directory.GetFiles();
CombineImages(files);
}
}
private void CombineImages(FileInfo[] files)
{
//change the location to store the final image.
string finalImage = #"C:\Users\Elder Zollinger\Desktop\Images";
List<int> imageHeights = new List<int>();
int nIndex = 0;
int width = 0;
foreach (FileInfo file in files)
{
Image img = Image.FromFile(file.FullName);
imageHeights.Add(img.Height);
width += img.Width;
img.Dispose();
}
imageHeights.Sort();
int height = imageHeights[imageHeights.Count - 1];
Bitmap img3 = new Bitmap(width, height);
Graphics g = Graphics.FromImage(img3);
g.Clear(SystemColors.AppWorkspace);
foreach (FileInfo file in files)
{
Image img = Image.FromFile(file.FullName);
if (nIndex == 0)
{
g.DrawImage(img, new Point(0, 0));
nIndex++;
width = img.Width;
}
else
{
g.DrawImage(img, new Point(width, 0));
width += img.Width;
}
img.Dispose();
}
g.Dispose();
img3.Save(finalImage, System.Drawing.Imaging.ImageFormat.Jpeg);
img3.Dispose();
imageLocation.Image = Image.FromFile(finalImage);
}
}
}
That's likely GDI playing tricks on you.
You see, when GDI encounters an unknown file, it will quite likely cause an OutOfMemoryException. Since you're not filtering the input images at all, I'd expect that you're simply grabbing a non-image file (or an image type that GDI doesn't understand).
Oh, and a bit sideways - make sure you set JPEG quality when saving JPEGs - the default is something like 75, which is rather bad for a lot of images. And please, do use using - it's very handy to ensure proper and timely clean-up :)
Get more memory!
But seriously, you don't appear to be filtering out files that are not image files. There are some hidden files in folders that you may be trying to open as an image accidentally, such as "Thumbs.db". Make sure that the file is an image with something like if (Path.GetExtension(file.FullPath) != ".png") continue;.
Also, on objects that you are calling .Dispose() on, consider wrapping them in a using() instead. For example:
using(var img = Image.FromFile(file.FullPath))
{
// ...
}
I would like to generate some images dynamicaly. For that, I intend to create a XAML View, populate it with Data (using DataBinding) and then generate an image from the rendering of that view (kind of a screenshot).
Is there a way to do this in Silverligth or WPF?
In WPF:
public static Image GetImage(Visual target)
{
if (target == null)
{
return null; // No visual - no image.
}
var bounds = VisualTreeHelper.GetDescendantBounds(target);
var bitmapHeight = 0;
var bitmapWidth = 0;
if (bounds != Rect.Empty)
{
bitmapHeight = (int)(Math.Floor(bounds.Height) + 1);
bitmapWidth = (int)(Math.Floor(bounds.Width) + 1);
}
const double dpi = 96.0;
var renderBitmap =
new RenderTargetBitmap(bitmapWidth, bitmapHeight, dpi, dpi, PixelFormats.Pbgra32);
var visual = new DrawingVisual();
using (var context = visual.RenderOpen())
{
var brush = new VisualBrush(target);
context.DrawRectangle(brush, null, new Rect(new Point(), bounds.Size));
}
renderBitmap.Render(visual);
return new Image
{
Source = renderBitmap,
Width = bitmapWidth,
Height = bitmapHeight
};
}
Use the WriteableBitmap and its Render function in Silverlight.
In WPF use this trick by using the RenderTargetBitmap and its Render function
You can add the controls (data after they are databound) that you want to capture to a ViewBox - http://www.wpftutorial.net/ViewBox.html
From there, you can create an image using WriteableBitmap - http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap%28VS.95%29.aspx