Writablebitmap Indexed8 writepixels - c#

I try to change writablebitmap using writepixels() method, but it doesn't change any pixels.
it has following consructor
public void createWbm(int viewportW, int viewportH)
{
writeableBitmap = new WriteableBitmap(
viewportW,
viewportH,
96,
96,
PixelFormats.Indexed8,
new BitmapPalette(Form1.form1.getColors()));
i.Source = writeableBitmap;
}
and I use this method calling leftbuttondown event, but there is not any change. Is it necessary to use two loops(outer for row of pixels and inner for columns ) to paint every pixle or it is possible use just writepixels() method? thanks
void BrushPixel(MouseEventArgs e)
{
byte[] ColorData = { 0, 0, 0, 0 }; // B G R
Int32Rect rect = new Int32Rect(
(int)(e.GetPosition(i).X),
(int)(e.GetPosition(i).Y),
1,
1);
writeableBitmap.WritePixels( rect, ColorData, 4, 0);
}

You are incorrectly using it,
at this format (8-bit) your array ColorData either represents an 1x4, 4x1 or 2x2 pixels image.
therefore rect dimensions should match either of these sizes
Keep in mind that these are indices to colors in a palette, not BGR values as you've commented.
Here's a simple example :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var bitmap = new WriteableBitmap(100, 100, 96, 96, PixelFormats.Indexed8, BitmapPalettes.Halftone256);
int width = 50;
int height = 50;
var pixels = new byte[width*height];
var random = new Random();
random.NextBytes(pixels);
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width, 0);
Image1.Source = bitmap;
}
}
XAML :
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350"
SnapsToDevicePixels="True"
UseLayoutRounding="True">
<Grid>
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderBrush="Black"
BorderThickness="1">
<Image x:Name="Image1" Stretch="None" />
</Border>
</Grid>
</Window>
Note : at this format stride parameter always equals width because a pixel length in bytes is 1.
I strongly suggest you to use WriteableBitmapEx instead, it makes WriteableBitmap manipulation way easier. Beware that it supports only PixelFormats.Pbgra32 but unless you really have a specific reason to use 8-bit I can only recommend it, it can draw many primitives such as lines, rectangles, circles etc ...

Related

How to draw rectangle on JPG in C# for WPF

[![enter image description here][1]][1]I have folder with a series of jpgs, which are frames of video that have been converted into jpgs. I made some code that iterates through them and displays them.
I am trying to draw a green box on a JPG image from C#. The Hight, Width, XC and YC are in a XML I just access the data there for each frame. I got it to work using a bitmap but then to display it in WPF I have to convert it into a bitmapImage first. The problem is it takes way to long. I want the video to be played at 25 fps. so all the processing needs to happen under 40 ms. Right now it takes anywhere between .01 and .3 seconds to displays the new image.
Here is the code I have so far-
public void UpdateImage(string imageName, int[] boxData)
{
// imageName is the file path the image
Bitmap oldImage = new Bitmap(imageName);
// if objects are detected
if (boxData.Length != 0)
{
// transforms x and y cords to align box better to light
int newXC = boxData[0] - (boxData[2] / 2);
int newYC = boxData[1] - (boxData[3] / 2);
// ensures cords are not negative
if (newXC < 0)
newXC = 0;
if (newYC < 0)
newYC = 0;
// uses the DrawRectangleBorder to draw rectangles
Bitmap newImage = DrawRectangleBorder(oldImage, new Rectangle(new System.Drawing.Point(newXC, newYC), new System.Drawing.Size(boxData[2], boxData[3])), Color.Green);
// converts Bitmap to BitmapImage
using (MemoryStream memory = new MemoryStream())
{
newImage.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
ImportImage.Source = bitmapImage;
}
}
else
{
Bitmap newImage = oldImage;
// converts Bitmap to BitmapImage
using (MemoryStream memory = new MemoryStream())
{
newImage.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
ImportImage.Source = bitmapImage;
}
}
}
The DrawRectangleBorder Method-
private static Bitmap DrawRectangleBorder(Bitmap image, Rectangle rectangle, Color colour)
{
// makes new blank Bitmap from the old ones width and height
Bitmap newBitmap = new Bitmap(image.Width, image.Height);
// opens up the blank bit
using (Graphics graphics = Graphics.FromImage(newBitmap))
graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height),
new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
// what actually draws the rectangles
for (Int32 x = rectangle.Location.X; x <= rectangle.Right && x < image.Width; x++)
for (Int32 y = rectangle.Location.Y; y <= rectangle.Bottom && y < image.Height; y++)
if (y == rectangle.Location.Y || y == rectangle.Bottom || x == rectangle.Location.X || x == rectangle.Right)
newBitmap.SetPixel(x, y, colour);
return newBitmap;
}
Here is what one of the pictures look like, they are 640 by 480 resolution-
[1]: https://i.stack.imgur.com/ZiocC.jpg
Any help would be great!
You could simplify your code by using this XAML
<Canvas>
<Image x:Name="ImportImage"/>
<Path x:Name="ObjectBox"
Width="{Binding ActualWidth, ElementName=ImportImage}"
Height="{Binding ActualHeight, ElementName=ImportImage}"
Stretch="None" Stroke="Green" StrokeThickness="1"/>
</Canvas>
with an UpdateImage method like this:
public void UpdateImage(string imagePath, int[] boxData)
{
ImportImage.Source = new BitmapImage(new Uri(imagePath));
var boxes = new GeometryGroup();
for (int i = 0; i <= boxData.Length - 4; i += 4)
{
int width = boxData[i + 2];
int height = boxData[i + 3];
int x = boxData[i] - width / 2;
int y = boxData[i + 1] - height / 2;
boxes.Children.Add(new RectangleGeometry(new Rect(x, y, width, height)));
}
ObjectBox.Data = boxes;
}
here is a rough demo of using a rectangle overlaid in front of an image and controlling its position and size. Sorry, never shared wpf on SO before, but think this code is all you need. I tested the time and it can go well below 40 ms (but I am not updating the image at all, just the rectangle overlay. In case it is needed, I also found this Fast Video Display WPF but ya, did not implement it or test it out.
".cs"
namespace ImageStreamer
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
Stopwatch stopWatch = new Stopwatch();
long lastTime = 0;
public MainWindow()
{
InitializeComponent();
dispatcherTimer.Tick += dispatcherTimer_Tick;
dispatcherTimer.Interval = new TimeSpan(0, 0, 0,0,25);
stopWatch.Start();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
targetRect.Margin = new Thickness(targetRect.Margin.Left+1, targetRect.Margin.Top+1,0,0);
targetRect.Width += 1;
targetRect.Height += 1;
Trace.WriteLine(lastTime - stopWatch.ElapsedMilliseconds);
lastTime = stopWatch.ElapsedMilliseconds;
}
private void Grid_Initialized(object sender, EventArgs e)
{
}
private void Button_Click(object sender, RoutedEventArgs e)
{
dispatcherTimer.Start();
}
}
}
"xaml"
<Window x:Class="ImageStreamer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ImageStreamer"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Initialized="Grid_Initialized">
<Image x:Name="imgBox" HorizontalAlignment="Left" Height="265" Margin="166,87,0,0" VerticalAlignment="Top" Width="512" Source="/ZiocC.jpg"/>
<Rectangle x:Name="targetRect" HorizontalAlignment="Left" Height="49" Margin="323,228,0,0" Stroke="Red" VerticalAlignment="Top" Width="113" StrokeThickness="5"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="24,43,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>
</Window>

Why window smaller than actualwidth?

I want capture window but The actual window size seems to be smaller than the figure.
this is code
<Window x:Class="FileRead.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="620" Height="340" >
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal">
<Button x:Name="ReadImageButton" Width="100" Height="30" Margin="10" Click="ReadImage_Click">
LoadImage
</Button>
<Button x:Name="ReadTextButton" Width="100" Height="30" Margin="10" Click="ReadText_Click">
LoadText
</Button>
<Button x:Name="CaptueScreenButton" Width="80" Height="30" Margin="10" Click="CaptueScreenButton_Click">
ScreenCapture
</Button>
<Button x:Name="CaptuerWindowButton" Width="80" Height="30" Margin="10" Click="CaptuerWindowButton_Click">
WindowCapture
</Button>
I couldn't find a problem.
private void CaptuerWindowButton_Click(object sender, RoutedEventArgs e)
{
int width = (int)this.ActualWidth;
int height = (int)this.ActualHeight;
Point point = this.PointToScreen(new Point(0, 0));
CheckLable.Content = string.Format("{0} / {1}", this.Width, this.ActualWidth);
using (Bitmap bmp = new Bitmap(width, height))
{
using (Graphics gr = Graphics.FromImage(bmp))
{
gr.CopyFromScreen( (int)point.X, (int)this.Top, 0, 0, bmp.Size);
}
bmp.Save(ImagePath + "/WindowCapture.png", ImageFormat.Png);
}
}
result image
There is always a difference of about 15 points.:
help me please.
enter image description here
The cause of your problem is that size of a window include the area that draw by OS, which was called "non-client area", usually include frame, border, drop show effect. And your calculation didn't consider that. The right code will like
var clientTopLeft = this.PointToScreen(new System.Windows.Point(0, 0));
// calculate the drop show effect offset.
var shadowOffset = SystemParameters.DropShadow ? clientTopLeft.X - Left -
((WindowStyle == WindowStyle.None && ResizeMode < ResizeMode.CanResize) ? 0 : SystemParameters.BorderWidth) : 0;
// exclude left and right drop shadow area
int width = (int)(Width - 2 * shadowOffset);
// exclude bottom drop shadow area
int height = (int)(Height - shadowOffset);
using (Bitmap bmp = new Bitmap(width, height))
{
using (Graphics gr = Graphics.FromImage(bmp))
{
gr.CopyFromScreen((int)(Left + shadowOffset),
(int)Top, 0, 0, bmp.Size);
}
bmp.Save("WindowCapture.png");
}

How to paint a custom Progress Bar by code (WPF)

I created a WinForms custom progress bar, but it flickers a little. It's double buffered, but it still flickers a little bit, so I'm trying WPF to see if the little flickering can get away.
I'm completely new to WPF. As far as I've read, WPF's OnPaint(PaintEventArgs e) method is called OnRender(DrawingContext drawingContext).
System.Drawing.Image BMP = System.Drawing.Image.FromFile(MyImagePath);
BMP = new Bitmap(BMP, (int)Width, (int)Height);
// Working method I founded in this site for converting a System.Drawing.Bitmap to a BitmapSource
ImageSource Rainbow = CreateBitmapSourceFromGdiBitmap((Bitmap)BMP);
// Working method I founded in this site for converting a System.Drawing.Bitmap to System.Windows.Media.Brush
System.Windows.Media.Brush RainbowBrush = CreateBrushFromBitmap((Bitmap)BMP);
protected override void OnRender(DrawingContext DrawingContext)
{
if (Value > 0)
{
Rect myRect = new Rect(0, 0, ((Width * Value) / (Maximum - Minimum)) + 5, Height);
DrawingContext.DrawRectangle(RainbowBrush, new System.Windows.Media.Pen(), myRect);
}
}
The problem:
My image is not "overriding" the green bar.
Now, if I change Rect myRect = new Rect(0, 0, ((Width * Value) / (Maximum - Minimum)) + 5, Height); to... let's say, Rect myRect = new Rect(0, 50, ((Width * Value) / (Maximum - Minimum)) + 5, Height);, the result is this:
So, the rainbow bar is drawn, but not over the progress bar. If I write Rect myRect = new Rect(0, 0, ((Width * Value) / (Maximum - Minimum)) + 5, Height);, it's drawn but UNDER the progress bar. What do I do to have a rainbow progress bar (and other custom progress bars for that matter)?
Thank you for your help.
Edit: The original progress bar (that one that flickers a little bit) has way more than the rainbow. I just started with the rainbow to make a quick test in WPF and then try and add the other stuff. Just in case if you're wondering why such a simple progress bar was flickering in WinForms. It was because the WinForms one had more than the rainbow. Thank you.
If you need to create a ProgressBar at the start of the Window of WPF application, it is possible to do by using this code:
Your xaml:
<Window x:Class="PasswordBoxMVVM.MainWindow"
<!--The code omitted for the brevity-->
Title="MainWindow" Height="350" Width="525">
<StackPanel x:Name="stackPanel">
<TextBox x:Name="textBox"
<DataGrid />
</StackPanel>
</Window>
Code-Behind:
public MainWindow()
{
InitializeComponent();
PaintProgressBar();
}
private void PaintProgressBar()
{
ProgressBar progressBar = new ProgressBar();
progressBar.IsIndeterminate = true;
progressBar.Margin = new Thickness(10, 0, 10, 10);
progressBar.Visibility = Visibility.Visible;
progressBar.Height = 25;
//progressBar.FlowDirection = FlowDirection.LeftToRight;
progressBar.Foreground = System.Windows.Media.Brushes.Green;
progressBar.Background = System.Windows.Media.Brushes.Red;
progressBar.Value = 50;
stackPanel.Children.Add(progressBar);
}
Where property progressBar.Foreground sets color of your ProgressBar.

WPF Viewbox and Image size

I want to create the window that would show list of pictures one below the other. I've created control that contains ViewBox and Image in it:
<UserControl x:Class="..."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Viewbox Name="viewbox">
<Image Height="10" Name="image" Width="10" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Viewbox>
</Grid>
</UserControl>
public BitmapImage Image
{
get { return image.Source as BitmapImage; }
set { changeImage(value); }
}
public SingleIllustrationViewer()
{
InitializeComponent();
}
private void changeImage(BitmapImage img)
{
System.Drawing.Graphics graphics = System.Drawing.Graphics.FromHwnd(IntPtr.Zero);
float dpiX = graphics.DpiX / 96;
this.image.BeginInit();
this.image.Source = img;
this.image.EndInit();
this.image.Width = img.PixelWidth / img.DpiX * dpiX;
}
and I'm placing Images on window like this:
double margin = 0;
for (int i = 0; i < illustrations.Count; i++)
{
String path = illustrations[i].printVersions.Last<String>();
BitmapImage bmp = new BitmapImage(new Uri(path));
Controls.SingleIllustrationViewer iv = new Controls.SingleIllustrationViewer();
iv.VerticalAlignment = System.Windows.VerticalAlignment.Top;
iv.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
iv.Margin = new Thickness(50, margin, 0, 0);
iv.Image = bmp;
grid.Children.Add(iv);
margin += iv.Image.Height + 20;
}
So, for example, I've placed 3 pictures (all 3 of same width) like this, and received such an interesting behavior: first one is good, second smaller, third smaller than a second. Here is the screen shot:
Maybe someone can tell me why is that so, and how can fix this, to see all those picture in the same width?
Thanks!
Regards, Tomas
Root cause:
You did not specify the Height or Width of the UserControl, so when the first SingleIllustrationViewer is added to the Grid, it will be stretched to occupied all available space until it reaches the edge of the Grid. The same happens to the second one, but it is constrained to a smaller region due to the incremented margin.
The size specified as
d:DesignHeight="300" d:DesignWidth="300"
is only used by designer, set the size like
Height="300" Width="300"
And then, put you viewers in a StackPanel instead of a Grid, then you don't have to calculate the Margin of a viewer base on the last viewer's position. StackPanel is a container that stacks its children in one direction, vertically or horizontally.
for (int i = 0; i < illustrations.Count; i++)
{
String path = illustrations[i].printVersions.Last<String>();
BitmapImage bmp = new BitmapImage(new Uri(path));
Controls.SingleIllustrationViewer iv = new Controls.SingleIllustrationViewer();
iv.VerticalAlignment = System.Windows.VerticalAlignment.Top;
iv.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
iv.Margin = new Thickness(50, 0, 0, 20); //50 px to the left, 20 px to the next child
iv.Image = bmp;
stackPanel1.Children.Add(iv);
}

Efficient way to draw a single point on canvas

I am looking for a way to draw a single point (with a color) on C# canvas.
In android I would do something like
paint.Color = Color.Rgb (10, 10, 10);
canvas.DrawPoint (x, y, paint);
So I thought that I would be able to find it in the Shape class, but it was not there. Am I missing something or there is no way to draw a single point?
In the second case, what is a recommended way of drawing a point? In HTML5 canvas there is a similar problem and people are drawing points using rectangles/circles.
P.S. a question with similar title Add Point to Canvas is not answering it and moving into "how to draw a shape".
I just ran in the same question for UWP, I finally decided to use an Ellipse:
int dotSize = 10;
Ellipse currentDot = new Ellipse();
currentDot.Stroke = new SolidColorBrush(Colors.Green);
currentDot.StrokeThickness = 3;
Canvas.SetZIndex(currentDot, 3);
currentDot.Height = dotSize;
currentDot.Width = dotSize;
currentDot.Fill = new SolidColorBrush(Colors.Green);
currentDot.Margin = new Thickness(100, 200, 0, 0); // Sets the position.
myGrid.Children.Add(currentDot);
What about a Polyline?
xaml:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Canvas x:Name="canvas" Background="#00FFFFFF" MouseMove="Canvas_MouseMove">
<Polyline x:Name="polyline" Stroke="DarkGreen" StrokeThickness="3"/>
</Canvas>
</Grid>
c#:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
polyline.Points.Add(new Point(0,0));
polyline.Points.Add(new Point(0, 1));
polyline.Points.Add(new Point(1, 0));
polyline.Points.Add(new Point(1, 1));
}

Categories

Resources