I have this simple code in C# that basically implements a blur filter to an image based on the value of a Trackbar in Windows Forms.
private void BarraBlur_Scroll_1(object sender, EventArgs e)
{
// I take the image inside picture box and use my own Effects class
Bitmap image = new Bitmap(pictureBox1.Image);
Effects foto = new Effects(image);
switch (BarraBlur.Value)
{
case 0:
pictureBox1.Image = imagens; // This reestablishes the image to its original state
break;
case 1:
// Each case reestablishes the image to its original state
// and then applies the blur filter with a given depth
pictureBox1.Image = imagens;
pictureBox1.Image = foto.BlurEffect(1);
break;
case 2:
pictureBox1.Image = imagens;
pictureBox1.Image = foto.BlurEffect(2);
break;
case 3:
pictureBox1.Image = imagens;
pictureBox1.Image = foto.BlurEffect(3);
break;
case 4:
pictureBox1.Image = imagens;
pictureBox1.Image = foto.BlurEffect(4);
break;
case 5:
pictureBox1.Image = imagens;
pictureBox1.Image = foto.BlurEffect(5);
break;
default:
break;
}
}
What I want is that, depending on the value of the trackbar whenever it is set, to apply the blur filter to the image. The BlurEffect method takes an int argument so that the filter is applied in such value of depth.
The problem is that, if for example, the user sets the trackbar in the second position, it works fine, but when it sets it back to the first position, instead of returning the image to its original state and then applying the blur filter with depth 1, it applies a blur of depth 1 to the already blurred image with depth 2.
In other words, I want the trackbar to augment the depth of the filter each time the tick goes to the right and diminish the depth of the blur each time the tick of the trackbar goes left.
I have tried this with switch cases and if statements, but neither have worked.
Thanks a lot in advance.
This is the Effects class with the BlurEffect method.
class Effects
{
private Bitmap imagen;
// Constructor
public Effects(Bitmap item)
{
imagen = item;
}
public Bitmap BlurEffect(int depth)
{
for (int k = 0; k < depth; k++)
{
for (int i = 2; i < imagen.Width; i++)
{
for (int j = 2; j < imagen.Height; j++)
{
try
{
Color antX1 = imagen.GetPixel(i - 1, j);
Color antX2 = imagen.GetPixel(i - 2, j);
Color desX1 = imagen.GetPixel(i + 1, j);
Color desX2 = imagen.GetPixel(i + 2, j);
Color antY1 = imagen.GetPixel(i, j - 1);
Color antY2 = imagen.GetPixel(i, j - 2);
Color desY1 = imagen.GetPixel(i, j + 1);
Color desY2 = imagen.GetPixel(i, j + 2);
int promR = (int)((antX1.R + antX2.R + desX1.R + desX2.R + antY1.R + antY2.R + desY1.R + desY2.R + imagen.GetPixel(i, j).R) / 9);
int promG = (int)((antX1.G + antX2.G + desX1.G + desX2.G + antY1.G + antY2.G + desY1.G + desY2.G + imagen.GetPixel(i, j).G) / 9);
int promB = (int)((antX1.B + antX2.B + desX1.B + desX2.B + antY1.B + antY2.B + desY1.B + desY2.B + imagen.GetPixel(i, j).B) / 9);
imagen.SetPixel(i, j, Color.FromArgb(promR, promG, promB));
}
catch (Exception) { }
}
}
}
return imagen;
}
}
Firstly, you don't need the big switch with the repeated code. You can reduce your code to this:
if (BarraBlur.Value = 0)
{
pictureBox1.Image = imagens; // This reestablishes the image to its original state
}
else {
pictureBox1.Image = foto.BlurEffect(BarraBlur.Value);
}
However, this won't fix your problem. I don't know what class foto is, but it sounds like it is a wrapper for a Bitmap, and the BlurEffect() function applies a blur effect to that bitmap, before returning it. Your code appears to be written with the assumption that BlurEffect returns a blurred copy of the bitmap.
You can probably fix your code by changing it to construct a new foto every time you change the blur:
if (BarraBlur.Value = 0)
{
pictureBox1.Image = imagens; // This reestablishes the image to its original state
}
else {
var blur = new Foto(imagens); // or however the foto is constructed
pictureBox1.Image = blur.BlurEffect(BarraBlur.Value);
}
Since you've indicated that you wrote/control the foto class (named Effects), you could also change how that class works (instead of doing the above).
Sounds like currently it looks something like:
class Effects
{
private Bitmap _image;
public Effects(Bitmap image)
{
_image = image;
}
public Bitmap Blur(int amount)
{
// .. code to perform blur on _image ..
return _image;
}
}
And you should probably change it to be more like:
class Effects
{
private Bitmap _image;
public Effects(Bitmap image)
{
_image = image;
}
public Bitmap Blur(int amount)
{
var copy = _image.Clone();
// .. code to perform blur on copy ..
return copy;
}
}
pictureBox1.Image = imagens;
if (BarraBlur.Value == 0) return;
pictureBox1.Image = foto.BlurEffect(BarraBlur.Value);
Above replaces your whole code. Your problem is withing BlurEffect method. For that reason whenever you use 0 it will work because you will return image to the original. You need to reload image to the original as well at the start of BlurEffect method instead of what I'm guessing reusing picture.
Related
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 writing an emulator program, and the virtual display is supposed to be able to take in 3 bytes of color data and display the correct color pixel, similar to how a real screen works. But when I set up some scroll bars to test the generation of pixels nothing happens. Here is my code and a screenshot of the form:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TSC_Multi_System_Emulator
{
public partial class Form1 : Form
{
private PictureBox Display = new PictureBox();
string #emulationfolderpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Bitmap screen = new Bitmap(#Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + #"\Resource_Folder\" + #"FirstFrame.bmp");
int x = 0;
int y = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e) {
// Dock the PictureBox to the form and set its background to black.
Display.BackColor = Color.Black;
// Connect the Paint event of the PictureBox to the event handler method.
// Add the PictureBox control to the Form.
this.Controls.Add(Display);
}
public void DigitalGraphicsDisplay(int red, int green, int blue) {
Graphics g = Display.CreateGraphics();
screen.SetPixel(x, y, Color.FromArgb(red, green, blue));
g.DrawImage(screen, 0, 0, screen.Width, screen.Height);
g.Save();
if (x < screen.Width)
{
x = x + 1;
}
else if (x == screen.Width)
{
x = 0;
if (y < screen.Height)
{
y = y + 1;
}
else if (y == screen.Height)
{
y = 0;
}
}
}
private void button1_Click(object sender, EventArgs e){
int rchannel = redControl.Value;
int gchannel = greenControl.Value;
int bchannel = blueControl.Value;
DigitalGraphicsDisplay(rchannel, gchannel, bchannel);
}
}
}
UPDATE:
The code is now working somewhat, but I can't test the code using just a test button. I had to use the exact code given to me in the first answer, which only displayed a gradient, I wonder what I am doing wrong... :(
public partial class Form1 : Form
{
string #emulationfolderpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Bitmap screen = new Bitmap(#Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + #"\Resource_Folder\" + #"FirstFrame.bmp");
int x = 0;
int y = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e) {
// Dock the PictureBox to the form and set its background to black.
Display.BackColor = Color.Black;
// Connect the Paint event of the PictureBox to the event handler method.
// Add the PictureBox control to the Form.
this.Controls.Add(Display);
}
public void DigitalGraphicsDisplay(int red, int green, int blue)
{
if (Display.Image == null)
{
Bitmap NewBMP = new Bitmap(Display.ClientRectangle.Width, Display.ClientRectangle.Height);
using (Graphics g = Graphics.FromImage(NewBMP))
{
g.Clear(Color.White);
}
Display.Image = NewBMP;
}
(Display.Image as Bitmap).SetPixel(x, y, Color.FromArgb(red, green, blue));
Display.Invalidate();
x++;
if (x >= Display.Image.Width)
{
x = 0;
y++;
if (y >= Display.Image.Height)
{
y = 0;
}
}
}
private void button1_Click(object sender, EventArgs e){
Boolean a = false;
int b = 0;
do
{
DigitalGraphicsDisplay(51, 153, 102);
if (b == 10000)
{
a = true;
}
b = b + 1;
} while (a);
}
}
}
All I am getting is a white picturebox with nothing else in it...
(The gradient code did work though)
It looks like you are trying to draw directly on the PictureBox control itself.
Instead you should have an Image assigned to the PictureBox and then draw on the image.
Try changing your code as shown below. (Including the click event for testing.)
Note, the PictureBox keeps the reference to the image directly so you don't need a separate screen image in your class, unless you have a different purpose for it.
Also, this uses Bitmap.SetPixel() which is an extremely slow way to set pixels. There is a much faster but slightly more complex way, in these other links:
SetPixel is too slow. Is there a faster way to draw to bitmap?
Work with bitmaps faster in C#
Remember your button click will only draw one pixel at a time.
So be sure to look carefully:
Running my test code within the click event will yield this:
int x = 0;
int y = 0;
public void DigitalGraphicsDisplay(int red, int green, int blue)
{
if (Display.Image == null)
{
Bitmap NewBMP = new Bitmap(Display.ClientRectangle.Width, Display.ClientRectangle.Height);
using (Graphics g = Graphics.FromImage(NewBMP))
{
g.Clear(Color.White);
}
Display.Image = NewBMP;
}
(Display.Image as Bitmap).SetPixel(x, y, Color.FromArgb(red, green, blue));
Display.Invalidate();
x++;
if (x >= Display.Image.Width)
{
x = 0;
y++;
if (y >= Display.Image.Height)
{
y = 0;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
// Temporary code to show that it works (Due to Bitmap.SetPixel() it will be slow)
for (int I = 1; I < Display.ClientRectangle.Width * Display.ClientRectangle.Height; I++)
DigitalGraphicsDisplay((I/255)%255, (I % Display.ClientRectangle.Width) % 255, 127);
}
UPDATE: Per your comment, try this sample code:
private void button1_Click(object sender, EventArgs e)
{
Boolean a = true;
int b = 0;
do
{
DigitalGraphicsDisplay(51, 153, 102);
if (b == 10000)
{
a = false;
}
b = b + 1;
} while (a);
}
public void DigitalGraphicsDisplay(int red, int green, int blue) {
Graphics g = Display.CreateGraphics();
screen.SetPixel(x, y, Color.FromArgb(red, green, blue));
g.DrawImage(screen, 0, 0, screen.Width, screen.Height);
g.Save();
All possible mistakes in one go..
Never use CreateGraphics to draw persistent Graphics! Always either go for the Paint event or draw into the Image.
Graphics.Save does not save any drawn pixels. It saves the state of the Graphics object, which does not contain graphics but is a tool to write into a related bitmap. The state includes scale, rotation, smoothing-mode and then some..
You already write into the Bitmap so you can simply make it your new PictureBox.Image..
Or the PictureBox.BackgroundImage.
And, as I said, you can instead write on top of both that is onto the PBox's surface. For this use the Paint event, Invalidate to trigger it and class level variables to hold the necessary data..
The latter is for graphics that will change a lot, the two former ones are for changes that accumulate.
Control.CreateGraphics is for transient graphics only, like a rubber-band line or a cursor cross..
Is there any way to determine if two images are identical?
I want to change an image every time my timer ticks (animation).
But, I need to see which image is displaying, so is there any way to compare 2 images
to do what I want?
if (myImage.Flags == (Image.FromFile(#"Images/Enemy.png").Flags))
{
myImage = Image.FromFile(#"Images/Enemy2.png");
}
else
{
myImage = Image.FromFile(#"Images/Enemy.png");
}
Don't compare the images, just maintain the index of the current image in a variable.
Here's an example that works for any number of images:
private int _currentImageIndex;
private string[] _imagePaths =
{
"Images/Enemy.png",
"Images/Enemy2.png",
"Images/Enemy3.png",
};
...
void NextImage()
{
// Dispose the current image
Image img = pictureBox1.Image;
pictureBox1.Image = null;
if (img != null)
img.Dispose();
// Show the next image
_currentImageIndex = (_currentImageIndex + 1) % _imagePaths.Length;
string path = _imagePaths[_currentImageIndex];
pictureBox1.Image = Image.FromFile(path);
}
I'd try to compare ImageLocation. Although it doesn't work if you have your pictures as resources.
if (PictureBox1.ImageLocation == PictureBox2.ImageLocation)
{
}
See my question:
Dynamically changing image in a picturebox
Here goes simple answer.
In case of just 2 images, use flag
// field, true if enemy2.png is loaded
bool _image2;
// somewhere
if(_image2)
{
myImage = Image.FromFile(#"Images/Enemy.png");
_image2 = false;
}
else
{
myImage = Image.FromFile(#"Images/Enemy2.png");
_image2 = true;
}
I have an animation that is ran on a timer along with some other animations. I would like to be able to change the images source file when an event is triggered but i am currently unable to access element.Source (element equaling the current canvas object which is a image).
public static void Clouds(Canvas canvas, int boundry)
{
var random = new Random();
foreach (FrameworkElement element in canvas.Children)
{
var elementName = Regex.Split(element.Name, "_");
if (elementName[0] == "cloud")
{
if (Canvas.GetLeft(element) < canvas.ActualWidth + element.Width)
{
Canvas.SetLeft(element, Canvas.GetLeft(element) + 1);
} else
{
// Change image source file here.
Canvas.SetTop(element, random.Next(0 - ((int)element.Height / 2), Core.GetPercentage((int)canvas.ActualHeight, boundry)));
Canvas.SetLeft(element, 0 - element.Width);
}
}
}
}
Any help would be great, thanks.
Its because FrameworkElement has no property called Source, you have to cast or just pick the image elements you want.
Somthing like this may work
public static void Clouds(Canvas canvas, int boundry)
{
var random = new Random();
foreach (var image in canvas.Children.OfType<Image>())
{
if (image.Name.Contains("cloud_"))
{
if (Canvas.GetLeft(image) < canvas.ActualWidth + image.Width)
{
Canvas.SetLeft(image, Canvas.GetLeft(image) + 1);
}
else
{
Canvas.SetTop(image, random.Next(0 - ((int)image.Height / 2), Core.GetPercentage((int)canvas.ActualHeight, boundry)));
Canvas.SetLeft(image, 0 - image.Width);
}
}
}
}
now you will be able to access the source property as the var "image" is an Image
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...