I have some WriteableBitmap object.
Lets say that the image that is actully on the WriteableBitmap object is 600x400.
I want to copy part of the image - some Rectangle ( for example Rectangle of 100x100 in the middle of the WriteableBitmap ) and paste the copy to some other image control.
How can i do it ?
I suspect you've got this fixed now, but I had the same problem and I found the answer so I thought I'd post it. The code at http://writeablebitmapex.codeplex.com/ has a "Crop" routine that does pretty much what you're after. Once you have a SizeOfArgb constant set to 4, you can use this:
public static WriteableBitmap Crop(this WriteableBitmap bmp, int x, int y, int width, int height)
{
var srcWidth = bmp.PixelWidth;
var srcHeight = bmp.PixelHeight;
// If the rectangle is completly out of the bitmap
if (x > srcWidth || y > srcHeight)
{
return new WriteableBitmap(0, 0);
}
// Clamp to boundaries
if (x < 0) x = 0;
if (x + width > srcWidth) width = srcWidth - x;
if (y < 0) y = 0;
if (y + height > srcHeight) height = srcHeight - y;
// Copy the pixels line by line using fast BlockCopy
var result = new WriteableBitmap(width, height);
for (var line = 0; line < height; line++)
{
var srcOff = ((y + line) * srcWidth + x) * SizeOfArgb;
var dstOff = line * width * SizeOfArgb;
Buffer.BlockCopy(bmp.Pixels, srcOff, result.Pixels, dstOff, width * SizeOfArgb);
}
return result;
}
public static BitmapSource Crop(this BitmapSource bmp, Int32Rect rect)
{
return new CroppedBitmap(bmp, rect);
}
Related
I need to make a background looks like this:
I found the way to do it with <DrawingBrush TileMode="Tile"> but it's not allow to make a gradient, because this generats many repeating squares. Maybe exists any ways to do it dynamicly, not in xaml?
I had to do something very similar and chose to create a grid dynamically in C#, the method was called upon on the Canvas-loaded event.
The "max_value" is the max size in pixels you want the grid to be, I set mine to 3500.
The "ScaleFactor" is the spacing in between the lines/ the size of the squares, I set mine to 15.
The "Canvas" is simply the canvas you want to apply the grid too.
I did have to extract the code and tweak it from a larger program but it should still work, but I don't have an IDE to test it currently.
class GridLine //Declares a grid-line object
{ //Declares a variety of different line lists, used for different parts of the grid.
private List<Line> XGridline { get; set; } = new List<Line>();
private List<Line> YGridline { get; set; } = new List<Line>();
private Line[] Axis { get; set; } = new Line[2];
//Used to draw the grid.
public GridLine DrawGrid(int max_value, int scale_factor, Canvas canvas) //Draws the grid
{
int x = 0;
int y = 0;
while (x < max_value) // Used to draw lines from the center out to the far right.
{
YGridline.Add(new Line() {
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 2,
X1 = x,
Y1 = -max_value,
X2 = x,
Y2 = max_value
});
canvas.Children.Add(YGridline[YGridline.Count - 1]);
x += scale_factor;
}
x = 0;
y = 0;
while (x > -max_value) //used to draw lines from the center to the far left.
{
YGridline.Add(new Line() {StrokeThickness = 2,
Stroke = new SolidColorBrush(Colors.Black),
X1 = x ,
Y1 = -max_value,
X2 = x,
Y2 = max_value
});
canvas.Children.Add(YGridline[YGridline.Count - 1]);
x -= scale_factor;
}
y = 0;
x = 0;
while (y < max_value) //used to draw lines from the center to the bottom
{
XGridline.Add(new Line() {
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 2,
X1 = -max_value,
Y1 = y,
X2 = max_value,
Y2 = y
});
canvas.Children.Add(XGridline[XGridline.Count - 1]);
y += scale_factor;
}
y = 0;
x = 0;
while (y > -max_value) //Used to draw lines from the center to the top.
{
XGridline.Add(new Line() {
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 2,
X1 = -max_value,
Y1 = y,
X2 = max_value,
Y2 = y
});
canvas.Children.Add(XGridline[XGridline.Count - 1]);
y -= scale_factor;
}
//VERTICLE LINE- Y Axis
Axis[0] = (new Line() { Stroke = new SolidColorBrush(Colors.Black), StrokeThickness = 3, X1 = 0, Y1 = -max_value, X2 = 0, Y2 = max_value });
canvas.Children.Add(Axis[0]);
//Horizontal line - X Axis
Axis[1] = (new Line() { Stroke = new SolidColorBrush(Colors.Black), StrokeThickness = 3, X1 = -max_value, Y1 = 0, X2 = max_value, Y2 = 0 });
canvas.Children.Add(Axis[1]);
return this;
}
}
EDIT:
If you'd just like to have a gradient background, why don't you set the rectangles to have no/ a transparent fill then set a gradient background behind them? I would still suggest looking at my code, because depending on what you intend to do with the grid later you may find it easier to generate a grid pragmatically.
I am creating a document based application and i want to draw a horizontal line underlying the text. But, line should not be straight. i want to draw a line like this.
Currently i am using System.Graphics object to draw any object.
private void DrawLine(Graphics g, Point Location, int iWidth)
{
iWidth = Convert.ToInt16(iWidth / 2);
iWidth = iWidth * 2;
Point[] pArray = new Point[Convert.ToInt16(iWidth / 2)];
int iNag = 2;
for (int i = 0; i < iWidth; i+=2)
{
pArray[(i / 2)] = new Point(Location.X + i , Location.Y + iNag);
if (iNag == 0)
iNag = 2;
else
iNag = 0;
}
g.DrawLines(Pens.Black, pArray);
}
UPDATED:
Above code is working fine and line draws perfectly but, this code effects on application performance. Is there another way to do this thing.
If you want fast drawing just make a png image of the line you want, with width larger than you need and then draw the image:
private void DrawLine(Graphics g, Point Location, int iWidth)
{
Rectangle srcRect = new Rectangle(0, 0, iWidth, zigzagLine.Height);
Rectangle dstRect = new Rectangle(Location.X, Location.Y, iWidth, zigzagLine.Height);
g.DrawImage(zigzagLine, dstRect, srcRect, GraphicsUnit.Pixel);
}
zigzagLine is the bitmap.
valter
Need to divide a UserControl with a background picture into multiple small clickable areas. Clicking them should simply raise an event, allowing to determine which particular area of the picture was clicked.
The obvious solution is using transparent labels. However, they are heavily flickering. So it looks like labels are not designed for this purpose, they take too much time to load.
So I'm thinking if any lighter option exists? To logically "slice up" the surface.
I also need a border around the areas, though.
on the user control do:
MouseClick += new System.Windows.Forms.MouseEventHandler(this.UserControl1_MouseClick);
and now in the UserControl1_MouseClick event do:
private void UserControl1_MouseClick(object sender, MouseEventArgs e)
{
int x = e.X;
int y = e.Y;
}
now let's divide the user control to a 10x10 area:
int xIdx = x / (Width / 10);
int yIdx = y / (Height / 10);
ClickOnArea(xIdx, yIdx);
in ClickOnArea method you just need to decide what to do in each area. maybe using a 2d array of Action
as for the border do this:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
Pen p = new Pen(Color.Black);
float xIdx = (float)(Width / 10.0);
float yIdx = (float)(Height / 10.0);
for (int i = 0; i < 10; i++)
{
float currVal = yIdx*i;
g.DrawLine(p, 0, currVal, Width, currVal);
}
g.DrawLine(p, 0, Height - 1, Width, Height - 1);
for (int j = 0; j < 10; j++)
{
float currVal = xIdx * j;
g.DrawLine(p, currVal, 0, currVal, Height);
}
g.DrawLine(p, Width - 1, 0, Width - 1, Height);
}
First of all Ill explain what I try to do, just in the case that someone comes up with a better approach
I need to blend too images using the "color" stile from photoshop (you know, the blending methods: Screen, hard light, color ... )
So, I have my base image (a png) and WriteableBitmap generated on execution time (lets call it color mask). Then I need to blend this two images using the "color" method and show the result in a UI component.
So far what Im trying is just to draw things on the WriteableBitmap but I'm facing unexpected behavior with the alpha channel.
My code so far:
// variables declaration
WriteableBitmap img = new WriteableBitmap(width, height, 96,96,PixelFormats.Bgra32,null);
pixels = new uint[width * height];
//function for setting the color of one pixel
private void SetPixel(int x, int y, Color c)
{
int pixel = width * y + x;
int red = c.R;
int green = c.G;
int blue = c.B;
int alpha = c.A;
pixels[pixel] = (uint)((blue << 24) + (green << 16) + (red << 8) + alpha);
}
//function for paint all the pixels of the image
private void Render()
{
Color c = new Color();
c.R = 255; c.G = 255; c.B = 255; c.A = 50;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
SetPixel(x, y, c);
img.WritePixels(new Int32Rect(0, 0, width, height), pixels, width * 4, 0);
image1.Source = img; // image1 is a WPF Image in my XAML
}
Whenever I run the code with the color c.A = 255 I get the expected results. The whole Image is set to the desired color. But if I set c.A to a different value I get weird things.
If I set the color to BRGA = 0,0,255,50 I get a dark blue almost black. If I set it to BRGA = 255,255,255,50 I get a yellow ...
Any clue !?!?!
Thanks in advance !
Change the order of your colour components to
pixels[pixel] = (uint)((alpha << 24) + (red << 16) + (green << 8) + blue);
I have a BitmapImage instance in Silverlight, and I am trying to find a way to read the color information of each pixel in the image. How can I do this? I see that there is a CopyPixels() method on this class that writes pixel information into the array that you pass it, but I don't know how to read color information out of that array.
How can I do this?
Look for the WriteableBitmap class in Silverlight 3. This class has a member Pixels which returns the bitmap's pixel data in an array of int's.
An example with a transform. bi is an BitmapImage object.
Image img = new Image();
img.source = bi;
img.Measure(new Size(100, 100));
img.Arrange(new Rect(0, 0, 100, 100));
ScaleTransform scaleTrans = new ScaleTransform();
double scale = (double)500 / (double)Math.Max(bi.PixelHeight, bi.PixelWidth);
scaleTrans.CenterX = 0;
scaleTrans.CenterY = 0;
scaleTrans.ScaleX = scale;
scaleTrans.ScaleY = scale;
WriteableBitmap writeableBitmap = new WriteableBitmap(500, 500);
writeableBitmap.Render(img, scaleTrans);
int[] pixelData = writeableBitmap.Pixels;
This is not possible with the current Bitmap API in the currently released Silverlight 3 beta.
The Silverlight BitmapImage file does not have a CopyPixels method. Please see the MSDN documentation here.
First, you should use WritableBitmap to get pixels collection: WriteableBitmap bmp = new WriteableBitmap(bitmapImageObj);. Each pixel is represented as 32-bit integer. I have made structure, which just helps to splits integer into a four bytes (ARGB).
struct BitmapPixel
{
public byte A;
public byte R;
public byte G;
public byte B;
public BitmapPixel(int pixel)
: this(BitConverter.GetBytes(pixel))
{
}
public BitmapPixel(byte[] pixel)
: this(pixel[3], pixel[2], pixel[1], pixel[0])
{
}
public BitmapPixel(byte a, byte r, byte g, byte b)
{
this.A = a;
this.R = r;
this.G = g;
this.B = b;
}
public int ToInt32()
{
byte[] pixel = new byte[4] { this.B, this.G, this.R, this.A };
return BitConverter.ToInt32(pixel, 0);
}
}
Here is an example of how it could be used to change red value:
BitmapPixel pixel = new BitmapPixel(bmp.Pixels[0]);
pixel.R = 255;
bmp.Pixels[0] = pixel.ToInt32();
Also I would like to mention that WriteableBitmap.Pixels are in Premultiplied RGB format. This article will explain what it means. And here is how compensation done by using BitmapPixel structure:
public static void CompensateRGB(int[] pixels)
{
for (int i = 0; i < pixels.Length; i++)
{
BitmapPixel pixel = new BitmapPixel(pixels[i]);
if (pixel.A == 255 || pixel.A == 0)
continue;
if (pixel.R == 0 && pixel.G == 0 && pixel.B == 0)
{
// color is unrecoverable, get rid of this
// pixel by making it transparent
pixel.A = 0;
}
else
{
double factor = 255.0 / pixel.A;
pixel.A = 255;
pixel.R = (byte)(pixel.R * factor);
pixel.G = (byte)(pixel.G * factor);
pixel.B = (byte)(pixel.B * factor);
}
pixels[i] = pixel.ToInt32();
}
}