I try to draw an image and after the first drawing i want invert the color of the drawing.
If the background of my new square is black i need white and if it white i need black.
In my example i draw 3 squares and make a offset of 10 pixel in the x.
Unfortunately, it does not produce the wanted result.
using var skBitmap = new SKBitmap(100, 40);
using var skCanvas = new SKCanvas(skBitmap);
skCanvas.Clear(SKColors.White);
var color = SKColors.Black;
float[] invertMatrix = {
-1.0f, 0.0f, 0.0f, 0.0f, 255.0f,
0.0f, -1.0f, 0.0f, 0.0f, 255.0f,
0.0f, 0.0f, -1.0f, 0.0f, 255.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};
using var skPaint = new SKPaint();
skPaint.Color = color;
skPaint.Style = SKPaintStyle.Fill;
skCanvas.DrawRect(10, 10, 20, 20, skPaint);
skPaint.ColorFilter = SKColorFilter.CreateColorMatrix(invertMatrix);
//move +10 in x
skCanvas.DrawRect(20, 10, 20, 20, skPaint);
//move +10 in x
skCanvas.DrawRect(30, 10, 20, 20, skPaint);
It looks like after making your second call to skCanvas.DrawRect you are not updating the colour.
Further, in your drawing 20 pixel wide rectangles, and overlapping them, as your only shifting your new rectangle 10 pixels to the right.
Following your wanted example, your need to make the 4th call to changing the colour back to black, then make a call to skCanvas.DrawRect(40, 10, 20, 20, skPaint).
Given that you are shifting your rectangles by 10 pixels and overlapping them, I suggest you update your calls to produce a thinner initial rectangle at 10 pixels wide.
I have found a solution for my problem unfortunately without a matrix.
using var skBitmap = new SKBitmap(100, 40);
using var skCanvas = new SKCanvas(skBitmap);
skCanvas.Clear(SKColors.White);
var color = SKColors.Black;
using var skPaint = new SKPaint();
skPaint.Color = color;
skPaint.Style = SKPaintStyle.Fill;
skCanvas.DrawRect(10, 10, 20, 20, skPaint);
DrawInvertRectangle(20, 10, 20, 20, skBitmap);
DrawInvertRectangle(30, 10, 20, 20, skBitmap);
private static void DrawInvertRectangle(int x, int y, int width, int height, SKBitmap skBitmap)
{
using var skPaint = new SKPaint();
skPaint.Color = SKColors.Black;
skPaint.Style = SKPaintStyle.Fill;
using var skBitmapInvert = new SKBitmap(skBitmap.Width, skBitmap.Height);
using var skCanvas = new SKCanvas(skBitmapInvert);
skCanvas.DrawRect(x, y, width, height, skPaint);
for (var row = 0; row < skBitmapInvert.Height; row++)
{
for (var column = 0; column < skBitmapInvert.Width; column++)
{
var pixel = skBitmap.GetPixel(column, row);
var pixelInvert = skBitmapInvert.GetPixel(column, row);
if (pixelInvert.Alpha == 255 && pixelInvert.Blue == 0 && pixel.Blue == 255)
{
skBitmap.SetPixel(column, row, SKColors.Black);
}
if (pixelInvert.Alpha == 255 && pixelInvert.Blue == 0 && pixel.Blue == 0)
{
skBitmap.SetPixel(column, row, SKColors.White);
}
}
}
}
Related
I read the documentation from skiasharp.. I am interested in how I can divide the surface of a shape (rectangle or polygon) into equal parts. For example, divide the surface into 6 equal parts and paint those parts with two colors according to the even-odd principle (something like football grass field texture). I did not find any similar example in the documentation.
Maku, thanks for your answer. I resolved this.
I needed something like this in the picture:
And my code for this result looks like this:
using System;
using SkiaSharp;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
void Draw(SKCanvas canvas, int width, int height)
{
float scale = 22.0f;
SKPath path = new SKPath();
List<SKPoint> AreaCenters = new List<SKPoint>();
List<SKPath> OutlinePaths = new List<SKPath>();
OutlinePaths.Clear();
AreaCenters.Clear();
AreaCenters.Add(new SKPoint(0.0f, 0.0f));
AreaCenters.Add(new SKPoint(200.0f, 0.0f));
AreaCenters.Add(new SKPoint(100f, 200.0f));
float scaleFactor = 1.1f;
var scaleMatrix = SKMatrix.MakeSkew(scale * 2.0f, scale * scaleFactor);
SKPaint fillColor1 = new SKPaint
{
IsAntialias = true,
Color = SKColors.Transparent,
Style = SKPaintStyle.Stroke,
StrokeWidth = 3,
StrokeCap = SKStrokeCap.Square
};
SKPaint fillColor2 = new SKPaint
{
IsAntialias = true,
Color = SKColors.DarkGreen,
Style = SKPaintStyle.StrokeAndFill,
StrokeWidth = 3,
StrokeCap = SKStrokeCap.Square
};
SKPaint lineColor = new SKPaint
{
IsAntialias = true,
Color = SKColors.Orange,
Style = SKPaintStyle.Stroke,
StrokeWidth = 4.0f
};
fillColor2.PathEffect = SKPathEffect.Create2DLine(scale, scaleMatrix);
if (AreaCenters.Count > 0)
{
path.MoveTo((AreaCenters[0]));
foreach (SKPoint p in AreaCenters.ToArray())
{
path.LineTo((p));
}
path.Close();
//path.Transform(TransformationMatrix);
//OutlinePath = path;
//this.OutlinePaths.Add(this.OutlinePath);
OutlinePaths.Add(path);
canvas.Save();
canvas.DrawPath(path, lineColor);
if (AreaCenters.Count > 2)
canvas.ClipPath(OutlinePaths[0]);
SKPath sKPath = new SKPath();
sKPath.AddPath(path, -scale, -scale * 2f);
sKPath.AddPath(path, -scale, scale * 2f);
sKPath.AddPath(path, scale, -scale * 2f);
sKPath.AddPath(path, scale, scale * 2f);
canvas.DrawPath(sKPath, fillColor1);
canvas.DrawPath(path, fillColor2);
canvas.DrawPath(path, lineColor);
fillColor1.Dispose();
fillColor2.Dispose();
canvas.Restore();
}
}
For this purposes I'm used SKMatrix.CreateSkew() (or SKMatrix.MakeSkew()) method in skiasharp.
If I render two rectangles on a canvas there is a small white line between them. Why? And how can I remove it?
Code:
var r = new Rectangle {Width = 100, Height = 100, Fill = Brushes.Black};
MyCanvas.Children.Add(r);
Canvas.SetLeft(r, 0);
Canvas.SetTop(r, 0);
var r2 = new Rectangle {Width = 100, Height = 100, Fill = Brushes.Black};
MyCanvas.Children.Add(r2);
Canvas.SetLeft(r2, 100);
Canvas.SetTop(r2, 0);
I already tried to set the stroke to "transparent".
I was trying to make a ColorBlend editor with draggable position marker when I noticed that sometime, it does not seems to render correctly.
Using a LinearGradientBrush with an InterpolationColors fading from white to blue to white again and the middle position for blue is 0.02f. The middle gradient does not seems to be aligned with the position I provided.
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var colorBlend = new ColorBlend {
Colors = new[] { Color.White, Color.Blue, Color.White },
Positions = new[] { 0f, 0.02f, 1f }
};
var rect = new RectangleF(10, 10, 500, 50);
//colors in constructor are not supposed to matter, and InterpolationColors should override them
using(var brush = new LinearGradientBrush(rect, Color.Aqua, Color.Black, LinearGradientMode.Horizontal)
{InterpolationColors = colorBlend})
e.Graphics.FillRectangle(brush, rect);
for (var i = 0; i < colorBlend.Positions.Length; i++)
{
var x = rect.X + colorBlend.Positions[i] * rect.Width;
e.Graphics.DrawLine(Pens.Black, x, rect.Bottom, x, rect.Bottom + 20);
}
}
The pure blue should be aligned with the middle marker, but the gradient continues a little after the marker before going back to white again.
When I use a position of 0.2f for the blue, the gradient seems fine.
I am attempting to create a minimap for an overlay. Currently the radar portion is working, targets rotate around the center point at the correct distance. I am having trouble printing the graphic that holds the minimap to screen. Everything other than the map prints to screen using the SlimDx.Direct3D9 library but based on my research the best way to create sub images based on player location was with graphics and this method avoids the OutOfMemory exception with cloning bitmaps and is more efficient.
if (ShowRadar)
{
//Draw background
//DrawFilledBox(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 50, 201f, 201f, Color.DarkOliveGreen, RadarTransparency);
RadarCenter = new Vector2(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 125 + 25);
if (DXTextrureMap != null)
{
//Transpose player posistion to map position and create the current mini map image
x = (int)player_X / 8;// +/- 4000 is max player range /8 = 500 = max map range
z = (int)player_Z / 8;
mini = new Rectangle(x, z, 100, 100);
Graphics g = Graphics.FromImage(originalBitmap);
g.DrawImage(originalBitmap, mini, new Rectangle((int)this.GameWindowRect.Right - 125, (int)this.GameWindowRect.Top + 125, 100, 100), System.Drawing.GraphicsUnit.Pixel);
}//the rest works as it should and displays on screen
if (RadarCenter.Length() > 0f)
{
//Display each entity in correct relational position
foreach (ENTITY entity in Entity)
{
Vector2 pointToRotate = new Vector2(entity.Pos.X, entity.Pos.Z);
Vector2 vector3 = new Vector2(player_X, player_Z);
pointToRotate = vector3 - pointToRotate;
float num30 = pointToRotate.Length() * 0.5f;
num30 = Math.Min(num30, 90f);
pointToRotate.Normalize();
pointToRotate = (Vector2)(pointToRotate * num30);
pointToRotate += RadarCenter;
pointToRotate = RotatePoint(pointToRotate, RadarCenter, player_D, true);
if (entity.Type == 0x04 && RadarPlayers)
{
DrawFilledBox(pointToRotate.X, pointToRotate.Y, 3f, 3f, Color.SkyBlue);
}
if ((entity.Type == 0x0C || entity.Type == 0x14 || entity.Type == 0x50 || entity.Type == 0x5b) && RadarAggressive)
{
DrawFilledBox(pointToRotate.X, pointToRotate.Y, 3f, 3f, Color.Red);
}
if ((entity.Type == 0x13 || entity.Type == 0x55) && RadarAnimals)
{
DrawFilledBox(pointToRotate.X, pointToRotate.Y, 3f, 3f, Color.LightGreen);
}
if ((entity.Type == 0x11 || entity.Type == 0x72 || entity.Type == 0x76) && RadarVehicles)
{
DrawFilledBox(pointToRotate.X, pointToRotate.Y, 3f, 3f, Color.HotPink);
}
}
}
//Draw radar border
DrawBoxAbs(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 50, 201f, 201f, 1f, Color.Black);
//Draw radar axis
DrawLine(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 50, this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 251, 1f, Color.Black);
DrawLine(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 150, this.GameWindowRect.Right - 24, this.GameWindowRect.Top + 150, 1f, Color.Black);
//Center point
DrawFilledBox(RadarCenter.X - 1f, RadarCenter.Y - 1f, 3f, 3f, Color.White);
}
What is the correct way to print my graphic?
Makes more sense when you realize a graphic draws on the image it grabs, also had to dispose of some elements. Time to make it rotate then perhaps add headings.
if (DXTextrureMap != null)
{
DXSprite.Begin(SpriteFlags.None);
//Transpose player posistion to map position and create the current mini map image
x = (int)player_X / 4;// +/- 4000 is max player range and /4 = 1000 = max map range
z = (int)player_Z / 4;
mini = new Rectangle(x-78, z-78, 157, 157);
croppedBitmap = new Bitmap(200, 200);
using (Graphics grD = Graphics.FromImage(croppedBitmap))
{
grD.DrawImage(originalBitmap, new Rectangle(0, 0, 157, 157), mini, GraphicsUnit.Pixel);
}
//croppedBitmap = Copy(originalBitmap, mini);
using (MemoryStream s = new MemoryStream())//s is a MemoryStream
{
croppedBitmap.Save(s, System.Drawing.Imaging.ImageFormat.Png);
s.Seek(0, SeekOrigin.Begin); //must do this, or error is thrown in next line
currentMinimap = Texture.FromStream(DXDevice, s);
s.Dispose();
}
//croppedImage.Dispose();
DXSprite.Draw(currentMinimap, new Vector3(100f, 100f, 0f), new Vector3(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 135, 0f), Color.White);
DXSprite.End();
currentMinimap.Dispose();
}
I build a minimap for an overylay, it correctly updates images based on player location. I am attempting now to make it rotate, although the graphics function rotateTransform, rotates about the upper left corner, how can I rotate about the center?
if (ShowRadar)
{
//Center point
RadarCenter = new Vector2(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 125 + 25 - 15);
//If map exists
if (originalBitmap != null)
{
DXSprite.Begin(SpriteFlags.None);
//Transpose player posistion to map position and create the current mini map image
//player location range -4000 - 4000, bitmap range 0 - 8000
x = (int)(Math.Abs(-4000 - player_Z));
z = (int)(Math.Abs(4000 - player_X));
//minimap rec loc on bitmap with a scale of 1000x1000
mini = new Rectangle(x-500, z-500, 1000, 1000);
croppedBitmap = new Bitmap(200, 200);
croppedBitmapFinal = new Bitmap(200, 200);
//draw the crop
using (Graphics grD = Graphics.FromImage(croppedBitmap))
{
grD.DrawImage(originalBitmap, new Rectangle(0, 0, 158, 158), mini, GraphicsUnit.Pixel);
grD.Dispose();
}
//redraw crop as circle
using (Graphics grD = Graphics.FromImage(croppedBitmapFinal))
{
GraphicsPath path = new GraphicsPath();
path.AddEllipse(0, 0, croppedBitmapFinal.Width-45, croppedBitmapFinal.Height-45);
grD.SetClip(path);
//apply rotate - direction 0 to 3.15 = 0 to 180, -3.15 to 0 = 180 to 360; so multiply by (180/3.15)
grD.RotateTransform(player_D * 57);
grD.DrawImage(croppedBitmap, 0, 0);
grD.Dispose();
}
//make texture from final bitmap
using (MemoryStream s = new MemoryStream())//s is a MemoryStream
{
croppedBitmapFinal.Save(s, System.Drawing.Imaging.ImageFormat.Png);
s.Seek(0, SeekOrigin.Begin); //must do this, or error is thrown in next line
currentMinimap = Texture.FromStream(DXDevice, s);
s.Dispose();
}
//clean up and print to screen
croppedBitmapFinal.Dispose();
croppedBitmap.Dispose();
DXSprite.Draw(currentMinimap, new Vector3(0f, 0f, 0f), new Vector3(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 35, 0f), Color.White);
DXSprite.End();
currentMinimap.Dispose();
}
}
simply had to move the origin like so
if (ShowRadar)
{
//Center point
RadarCenter = new Vector2(this.GameWindowRect.Right - 125, this.GameWindowRect.Top + 125 + 25 - 15);
//If map exists
if (originalBitmap != null)
{
DXSprite.Begin(SpriteFlags.None);
//Transpose player posistion to map position and create the current mini map image
//player location range -4000 - 4000, bitmap range 0 - 8000
x = (int)(Math.Abs(-4000 - player_Z));
z = (int)(Math.Abs(4000 - player_X));
//minimap rec loc on bitmap with a scale of 1000x1000
mini = new Rectangle(x-500, z-500, 1000, 1000);
croppedBitmap = new Bitmap(200, 200);
croppedBitmapFinal = new Bitmap(200, 200);
//draw the crop
using (Graphics grD = Graphics.FromImage(croppedBitmap))
{
grD.DrawImage(originalBitmap, new Rectangle(0, 0, 158, 158), mini, GraphicsUnit.Pixel);
grD.Dispose();
}
//redraw crop as circle
using (Graphics grD = Graphics.FromImage(croppedBitmapFinal))
{
//add circle clip to graphics
GraphicsPath path = new GraphicsPath();
path.AddEllipse(0, 0, croppedBitmapFinal.Width-45, croppedBitmapFinal.Height-45);
grD.SetClip(path);
//move origin to center for rotate
float hw = (croppedBitmapFinal.Width / 2)-22.5f;
float hh = (croppedBitmapFinal.Height / 2)-22.5f;
grD.TranslateTransform(hw, hh);
//apply rotate - direction 0 to 3.15 = 0 to 180, -3.15 to 0 = 180 to 360; so multiply by (180/3.15)
grD.RotateTransform((player_D * 57)-90);
//Move origin back to original pos
grD.TranslateTransform(-hw, -hh);
//Draw to bitmap
grD.DrawImage(croppedBitmap, 0, 0);
grD.Dispose();
}
//make texture from final bitmap
using (MemoryStream s = new MemoryStream())//s is a MemoryStream
{
croppedBitmapFinal.Save(s, System.Drawing.Imaging.ImageFormat.Png);
s.Seek(0, SeekOrigin.Begin); //must do this, or error is thrown in next line
currentMinimap = Texture.FromStream(DXDevice, s);
s.Dispose();
}
//clean up and print to screen
croppedBitmapFinal.Dispose();
croppedBitmap.Dispose();
DXSprite.Draw(currentMinimap, new Vector3(0f, 0f, 0f), new Vector3(this.GameWindowRect.Right - 225, this.GameWindowRect.Top + 35, 0f), Color.White);
DXSprite.End();
currentMinimap.Dispose();
}