I'm using Graphics.DrawImage to write a index bitmap into another image. The color black in the indexed image should be replaced with a transparent color when written to the "targetBitmap"
How do I do this in a nice performant way ?
var graphics = Graphics.FromImage(targetBitmap);
//I want the black color in "indexBitmap" to be transparent when it's written to "targetBitmap"
graphics.DrawImage(indexedBitmap,...)
Creating a color map and passing it as an "ImageAttributes" argument to DrawImage worked for me
var colorMaps = new[]{
new ColorMap {OldColor = Color.FromArgb(255, 0, 0, 0), NewColor = Color.Transparent}
};
var attr = new ImageAttributes();
attr.SetRemapTable(colorMaps);
How about using SetColorKey?
SetColorKey allows you to choose a transparent background when drawing the image on a graphics object.
But the fact is that this doesn't work when the SetRemapTable() function has been called.
You can make that work also by adding an extra ColorMap in the "colorMaps" array.
This extra ColorMap should have
- OldColor = 'Choose the transparent color'
- NewColor = Color.Transparent
and then call the SetRemapTable with the extended sub.
Below you see a code sample in C# to easily draw an image to a graphics object.
I use this to make a game with graphics.
This void (sub in basic) allows you to draw an image to graphics (eg. FormX.CreateGraphics()).
You can replace certain colors by other colors, and also choose a transparent color.
You can also draw the image with a specified angle (degrees).
public static void DrawImageToGraphics(Graphics gr, Bitmap img, Rectangle DestRect, Color[] OldColors, Color[] NewColors, Color TransparantColor, uint Angle)
{
System.Drawing.Drawing2D.Matrix lmx = new System.Drawing.Drawing2D.Matrix();
lmx.RotateAt(Angle, new PointF((DestRect.Left + DestRect.Right) / 2, (DestRect.Top + DestRect.Bottom) / 2));
gr.Transform = lmx;
System.Drawing.Imaging.ColorMap[] maps = new System.Drawing.Imaging.ColorMap[OldColors.Count() + 1];
for (int i = 0; i < OldColors.Count(); i++)
{
maps[i].OldColor = OldColors[i];
maps[i].NewColor = NewColors[i];
}
maps[OldColors.Count()].OldColor = TransparantColor;
maps[OldColors.Count()].NewColor = Color.Transparent;
System.Drawing.Imaging.ImageAttributes attr = new System.Drawing.Imaging.ImageAttributes();
attr.SetRemapTable(maps);
gr.DrawImage(img, DestRect, 0, 0, img.Width, img.Height, GraphicsUnit.Point, attr);
}
Related
I have a bitmap and I'm trying to show a user control object on a specific position(based on X and Y) of that bitmap image, Do you have any solution?
actually I have used map file with the specific extension(*.ECW).
also I could add ellipse or draw an image in the specific position but I would add a specific component (for a example a button that created as a user Control).
sorry for this unexecutable code . assume we have a graphic object that a picture or map placed on as a background .
now we have to add a component to specific point .
I have drawn an ellipse like this
ImageLayout BackgroundImageLayout;
protected Bitmap Background;
int ppiRadius =100;
Background = new Bitmap(Width, Height);
BackgroundImageLayout = ImageLayout.Center;
// Set up the map bitmap
// Get a graphics obejct for the bitmap
var graphics = Graphics.FromImage(Background);
graphics.FillRectangle(new SolidBrush(System.Drawing.Color.Black), 0, 0,
Width, Height);
// Draw the layers to the bitmap
var hdc = graphics.GetHdc();
if (_allLayersHandle != null)
{
foreach (var v in _allLayersHandle)
{
LastError = GlobalMapperDLL.GM_DrawLayer(hdc, v.Value,
ref _currentViewRect, 0, 0,
Background.Width, Background.Height);
}
}
graphics.ReleaseHdc(hdc);
if (LastError != GlobalMapperDLL.GM_Error_t32.GM_Error_None) return;
_centerPoint = GISConvert.GetCenterPixel();
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
var point = GISConvert.LatLonToPixel(sitePosition);
graphics.DrawEllipse(new System.Drawing.Pen(System.Drawing.Brushes.Red, 2), new RectangleF((point.X - ppiRadius / 2), (point.Y - ppiRadius / 2), ppiRadius, ppiRadius));
//or graphics.DrawImage
I'm triying to cover an image with another in order to provide a watermark but it has to cover the entire source image. The problem is that the watermark provided is of 600x600 and the source image can have any size and aspect ratio. So far it does not cover the source image entirely.
I solved it like this (in a very straightforward way).
private void button1_Click(object sender, EventArgs e)
{
var image = new Bitmap( this.pictureBox1.Image.Width, this.pictureBox1.Image.Height);
var rect = new Rectangle(0, 0, this.pictureBox1.Image.Width, this.pictureBox1.Image.Height);
Graphics graphics = Graphics.FromImage(image);
graphics.DrawImage(this.pictureBox1.Image, 0, 0);
var waterMarkImage = new Bitmap(this.pictureBox2.Image.Width, this.pictureBox2.Image.Height);
for (int y = 0; y < waterMarkImage.Height; y++)
{
for (int x = 0; x < waterMarkImage.Width; x++)
{
var color = (this.pictureBox2.Image as Bitmap).GetPixel(x, y);
color = Color.FromArgb(50, color.R, color.G, color.B);
waterMarkImage.SetPixel(x, y, color);
}
}
graphics.DrawImage(waterMarkImage, rect);
this.pictureBox3.Image = image;
}
In pictureBox1 I loaded main image. In pictureBox2 I loaded "water mark". In the event handler I created resulting image (first main image then the second) and loaded it into pictureBox3. To get water mark affect I reduced alpha component of color (I set it to 50).
I'm working in creating a composite image on the fly using Xamarin for iOS. I have two images, one is the base image (black and white), and the other is a alpha mask 'map' for a color filter. Then, at runtime, supply an RGB value so I can make the composite image be whatever color it should be.
I've found plenty of solutions for changing the entire image, but I'm having trouble finding a good solution for creating that color filter that only colors the area (with the alpha values) contained in the mask map:
Base Images:
Resulting image (if I tell it I want red):
In MonoDroid, I was able to create a custom ImageView and override the OnDraw method then used a ColorMatrixColorFilter which worked like a charm, but don't know how to accomplish the same thing in MonoTouch.
This is what I did for MonoDroid:
protected override void OnDraw(Canvas canvas)
{
if (_alphaMask == null)
_alphaMask = BitmapFactory.DecodeResource(Resources, GetDrawable("star_mask"));
if(_image == null)
_image = BitmapFactory.DecodeResource(Resources, GetDrawable("star"));
AdjustColor(_color, canvas, _alphaMask, _image);
}
private static void AdjustColor(Color color, Canvas c, Bitmap alphaMask, Bitmap image)
{
float R = color.R;
float G = color.G;
float B = color.B;
var maskPaint = new Paint();
var mat = new[]
{
0, 0, 0, 0, R, //red
0, 0, 0, 0, G, //green
0, 0, 0, 0, B, //blue
0, 0, 0, 1, 0 //alpha
};
ColorMatrix cm = new ColorMatrix(mat);
maskPaint.SetColorFilter(new ColorMatrixColorFilter(cm));
c.DrawBitmap(image, 0, 0, null);
c.DrawBitmap(alphaMask, 0, 0, maskPaint);
}
From Apple developer website: https://developer.apple.com/library/mac/documentation/GraphicsImaging/Conceptual/CoreImaging/ci_filer_recipes/ci_filter_recipes.html
Seems like what you are looking for (CIFilter).
Ok... Not the most elegant solution, but I created two ImageView on top of each other.
On top of the base image was the MaskView then used the following method to create the colored UIImage:
private UIImage GetColoredImage(string imageName, Color color)
{
UIImage image = UIImage.FromBundle(imageName);
UIGraphics.BeginImageContext(image.Size);
CGContext context = UIGraphics.GetCurrentContext();
context.TranslateCTM(0, image.Size.Height);
context.ScaleCTM(1.0f, -1.0f);
var rect = new RectangleF(0, 0, image.Size.Width, image.Size.Height);
// draw image, (to get transparancy mask)
context.SetBlendMode(CGBlendMode.Normal);
context.DrawImage(rect, image.CGImage);
// draw the color using the sourcein blend mode so its only draw on the non-transparent pixels
context.SetBlendMode(CGBlendMode.SourceIn);
context.SetFillColor(color.R / 255f, color.G / 255f, color.B / 255f, 1);
context.FillRect(rect);
UIImage coloredImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return coloredImage;
}
if someone has a better solution (maybe using CIFilter) I'd love to see it.
How to convert Image to Graphic?
You can't convert a Graphics object into an image, as the Graphics object doesn't contain any image data.
The Graphics object is just a tool used to draw on a canvas. That canvas is typically a Bitmap object or the screen.
If the Graphics object is used for drawing on a Bitmap, then you already have the image. If the Graphics object is used for drawing on the screen, you would have to make a screen shot to get an image of the canvas.
If the Graphics object was created from a window control, you could use the control's DrawToBitmap method to render the control on an image instead of on the screen.
You need an Image in order to draw your Graphics on, so you probably already have the image:
Graphics g = Graphics.FromImage(image);
As Darin states, you probably already have the image. If you don't, you can create a new one and draw to that one
Image bmp = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(bmp)) {
// draw in bmp using g
}
bmp.Save(filename);
Save saves the image to a file on your hard drive.
If you're drawing directly on a Control's graphics, you can create a new Bitmap with the same dimensions as the control and then call Control.DrawToBitmap(). However, the better way to go is usually to start with a Bitmap, draw to its graphics (as suggested by Darin), and then paint the bitmap onto the Control.
The best method to turn graphics into a bitmap is to get rid of the 'using' stuff:
Bitmap b1 = new Bitmap(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);
Graphics g = Graphics.FromImage(b1);
g.CopyFromScreen(0, 0, Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height));
b1.Save("screen.bmp");
I discovered this while figuring out how to turn graphics into a bitmap, and it works like a charm.
I have some examples on how to use this:
//1. Take a screenshot
Bitmap b1 = new Bitmap(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);
Graphics g = Graphics.FromImage(b1);
g.CopyFromScreen(0, 0, Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height));
b1.Save("screen.bmp");
//2. Create pixels (stars) at a custom resolution, changing constantly like stars
private void timer1_Tick(object sender, EventArgs e)
{
/*
* Steps to use this code:
* 1. Create new form
* 2. Set form properties to match the settings below:
* AutoSize = true
* AutoSizeMode = GrowAndShrink
* MaximizeBox = false
* MinimizeBox = false
* ShowIcon = false;
*
* 3. Create picture box with these properties:
* Dock = Fill
*
*/
//<Definitions>
Size imageSize = new Size(400, 400);
int minimumStars = 600;
int maximumStars = 800;
//</Definitions>
Random r = new Random();
Bitmap b1 = new Bitmap(imageSize.Width, imageSize.Height);
Graphics g = Graphics.FromImage(b1);
g.Clear(Color.Black);
for (int i = 0; i <r.Next(minimumStars, maximumStars); i++)
{
int x = r.Next(1, imageSize.Width);
int y = r.Next(1, imageSize.Height);
b1.SetPixel(x, y, Color.WhiteSmoke);
}
pictureBox1.Image = b1;
}
With this code, you can use all the commands for the Graphics Class, and copy them to a bitmap, therefore allowing you to save anything designed with the graphics class.
You may use this to your advantage.
My scenario:
I have one color background image JPG.
I have one black text on white background JPG.
Both images are the same size (height and width)
I want to overlay the image with black text and white background over the color background image, i.e. the white background becomes transparent to see the color background beneath it.
How can I do this with GDI in C#?
Thanks!
Thanks to GalacticCowboy I was able to come up with this solution:
using (Bitmap background = (Bitmap)Bitmap.FromFile(backgroundPath))
{
using (Bitmap foreground = (Bitmap)Bitmap.FromFile(foregroundPath))
{
// check if heights and widths are the same
if (background.Height == foreground.Height & background.Width == foreground.Width)
{
using (Bitmap mergedImage = new Bitmap(background.Width, background.Height))
{
for (int x = 0; x < mergedImage.Width; x++)
{
for (int y = 0; y < mergedImage.Height; y++)
{
Color backgroundPixel = background.GetPixel(x, y);
Color foregroundPixel = foreground.GetPixel(x, y);
Color mergedPixel = Color.FromArgb(backgroundPixel.ToArgb() & foregroundPixel.ToArgb());
mergedImage.SetPixel(x, y, mergedPixel);
}
}
mergedImage.Save("filepath");
}
}
}
}
Works like a charm. Thanks!
If the images are the same size, iterate over them and "AND" the colors for each pixel. For the white pixels, you should get the color of the other image, and for the black ones you should get black.
If they're not the same size, scale first.
I'm making this up off the top of my head, but something like:
Color destColor = Color.FromArgb(pixel1.ToArgb() & pixel2.ToArgb());
There exist easier and faster way. You should use ImageAttributes when you draw image that must be partially visible.
Image BackImage = Image.FromFile(backgroundPath);
using (Graphics g = Graphics.FromImage(BackImage))
{
using (ForeImage = Image.FromFile(foregroundPath))
{
ImageAttributes imageAttr = new ImageAttributes();
imageAttr.SetColorKey(Color.FromArgb(245, 245, 245), Color.FromArgb(255, 255, 255),
ColorAdjustType.Default);
g.DrawImage(ForeImage, new Rectangle(0, 0, BackImage.Width, BackImage.Height),
0, 0, BackImage.Width, BackImage.Height, GraphicsUnit.Pixel, imageAttr);
}
}
SetColorKey method will make color from specified range transparent, so you can make your white bitmap pixels transparent, including all pixels that are affected to jpeg compression artefacts.