Divide area into equal parts - c#

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.

Related

'IImageProcessingContext' does not contain a definition for 'ApplyScalingWaterMark' ImageSharp

I am running the latest version and I am getting the error as preceding.
Severity Code Description Project File Line Suppression State Error
CS1061 'IImageProcessingContext' does not contain a definition for
'ApplyScalingWaterMark' and no accessible extension method
'ApplyScalingWaterMark' accepting a first argument of type
'IImageProcessingContext' could be found (are you missing a using
directive or an assembly reference?) GitHubFuncs
C:\Sibeesh\Github\GitHubFuncs\GetLatestPosts.cs 39 Active
When I run the code below.
using(var img = Image.Load("canvas.jpg")) {
Font font = SystemFonts.CreateFont("Arial", 10);
using
var img2 = img.Clone(ctx => ctx.ApplyScalingWaterMark(font, feedItem.Summary.Text, Color.HotPink, 5, true));
img2.Save("images/wrapped.png");
}
Already added the using statements.
using SixLabors.Fonts;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
What am I missing here? Is this an issue with the latest version?
Finally, I got it working. I just had to add a few extension methods.
using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.Processing;
using System;
namespace GitHubFuncs.ExtensionMethods {
public static class ImageSharpExtensions {
public static IImageProcessingContext ApplyScalingWaterMark(this IImageProcessingContext processingContext,
Font font,
string text,
Color color,
float padding,
bool wordwrap) {
if (wordwrap) {
return processingContext.ApplyScalingWaterMarkWordWrap(font, text, color, padding);
} else {
return processingContext.ApplyScalingWaterMarkSimple(font, text, color, padding);
}
}
private static IImageProcessingContext ApplyScalingWaterMarkSimple(this IImageProcessingContext processingContext,
Font font,
string text,
Color color,
float padding) {
Size imgSize = processingContext.GetCurrentSize();
float targetWidth = imgSize.Width - (padding * 2);
float targetHeight = imgSize.Height - (padding * 2);
// measure the text size
FontRectangle size = TextMeasurer.Measure(text, new RendererOptions(font));
//find out how much we need to scale the text to fill the space (up or down)
float scalingFactor = Math.Min(imgSize.Width / size.Width, imgSize.Height / size.Height);
//create a new font
Font scaledFont = new Font(font, scalingFactor * font.Size);
var center = new PointF(imgSize.Width / 2, imgSize.Height / 2);
var textGraphicOptions = new TextGraphicsOptions() {
TextOptions = {
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
}
};
return processingContext.DrawText(textGraphicOptions, text, scaledFont, color, center);
}
private static IImageProcessingContext ApplyScalingWaterMarkWordWrap(this IImageProcessingContext processingContext,
Font font,
string text,
Color color,
float padding) {
Size imgSize = processingContext.GetCurrentSize();
float targetWidth = imgSize.Width - (padding * 2);
float targetHeight = imgSize.Height - (padding * 2);
float targetMinHeight = imgSize.Height - (padding * 3); // must be with in a margin width of the target height
// now we are working i 2 dimensions at once and can't just scale because it will cause the text to
// reflow we need to just try multiple times
var scaledFont = font;
FontRectangle s = new FontRectangle(0, 0, float.MaxValue, float.MaxValue);
float scaleFactor = (scaledFont.Size / 2); // every time we change direction we half this size
int trapCount = (int) scaledFont.Size * 2;
if (trapCount < 10) {
trapCount = 10;
}
bool isTooSmall = false;
while ((s.Height > targetHeight || s.Height < targetMinHeight) && trapCount > 0) {
if (s.Height > targetHeight) {
if (isTooSmall) {
scaleFactor = scaleFactor / 2;
}
scaledFont = new Font(scaledFont, scaledFont.Size - scaleFactor);
isTooSmall = false;
}
if (s.Height < targetMinHeight) {
if (!isTooSmall) {
scaleFactor = scaleFactor / 2;
}
scaledFont = new Font(scaledFont, scaledFont.Size + scaleFactor);
isTooSmall = true;
}
trapCount--;
s = TextMeasurer.Measure(text, new RendererOptions(scaledFont) {
WrappingWidth = targetWidth
});
}
var center = new PointF(padding, imgSize.Height / 2);
var textGraphicOptions = new TextGraphicsOptions() {
TextOptions = {
HorizontalAlignment = HorizontalAlignment.Left,
VerticalAlignment = VerticalAlignment.Center,
WrapTextWidth = targetWidth
}
};
return processingContext.DrawText(textGraphicOptions, text, scaledFont, color, center);
}
}
}
And in the end, I could use this method as preceding.
private static string WriteOnImage(SyndicationItem feedItem) {
using var img = Image.Load("images/canvas.jpg");
var font = SystemFonts.CreateFont("Arial", 20);
using var img2 = img.Clone(ctx => ctx.ApplyScalingWaterMark(font, feedItem.Summary.Text, Color.White, 5, true));
return img2.ToBase64String(PngFormat.Instance);
}

Keeping Track of multiple objects that used Matrix Transform C# Winforms

I posted a question about using Graphics Transform and got a great answer. What I needed was to have a spot on an oval where, if the mouse was in it, the oval would rotate. The code below includes that. However, now I ALSO need a region where, if the mouse is clicked inside it, I can resize the oval.
The problem I'm having isn't with the resize, it's with the region detection because the code that's transforming the resize rectangle isn't working. That same code works great when applied to the small, moving circle, which is leaving me more than a little confused.
When you start out, you have everything correctly placed and working. The oval, the small circle that will rotate it when you click in it, and the green box (Currently hidden under a red one) where if you click in it, you can resize the oval.
However, if you begin to rotate everything, the red rectangle is what you get. But what I need is that red rectangle to be the exact same size and shape as the green one and in the same place as the green one so that I can use the rectange.Contains(mousepointer) methods.
I know that I'm missing something basic here, but I have yet to be able to isolate it. Any helpful ideas would be appreciated. Here's the code I'm using:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace OvalTestV2
{
public partial class Form1 : Form
{
int theAngle = 0;
Pen pen2 = new Pen(Color.FromArgb(255, 68, 125, 255), 4);
Pen pen3 = new Pen(Color.Green, 4);
Pen smallPen = new Pen(Color.Black, 1);
bool testspot = false;
PointF center = new PointF(0, 0);
Rectangle rectangle = new Rectangle(20, 20, 100, 30);
Rectangle myRect2 = new Rectangle();
Rectangle rotatedRect2 = new Rectangle();
Rectangle resizeRect = new Rectangle();
Rectangle resizeRect2 = new Rectangle();
bool mouseBtnDown = false;
Graphics gw;
public Form1()
{
InitializeComponent();
this.MouseDown += mouseDown;
gw = this.CreateGraphics();
}
private void mouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (resizeRect.Contains(e.Location))
{
testspot = true;
}
if (resizeRect2.Contains(e.Location))
testspot = true;
if (rotatedRect2.Contains(e.Location))
{
gw.Clear(SystemColors.Control);
theAngle += 5;
rotate();
}
}
if (e.Button == MouseButtons.Right)
{
//oval = false;
//mouseBtnDown = false;
theAngle = 0;
rectangle.X = e.X - 50;
rectangle.Y = e.Y - 15;
rectangle.Width = 100;
rectangle.Height = 30;
center.X = rectangle.Left + (0.5f * rectangle.Width);
center.Y = rectangle.Top + (0.5f * rectangle.Height);
myRect2.Size = new Size(15, 15);
myRect2.Location = new Point(rectangle.Left - 15, (rectangle.Top - (rectangle.Height / 2)) + 15);
resizeRect.X = rectangle.X;
resizeRect.Y = rectangle.Y;
resizeRect.Size = rectangle.Size;
rectangle.Location = rectangle.Location;
rotate();
//drawstuff();
}
}
public void rotate()
{
mouseDnX_lbl.Text = myRect2.X.ToString();
mouseDnY_lbl.Text = myRect2.Y.ToString();
var matrix = new Matrix();
matrix.RotateAt(theAngle, center);
gw.Transform = matrix;
//For the Rotation area
// Get the 4 corner points of myRect2.
Point p1 = new Point(myRect2.X, myRect2.Y),
p2 = new Point(myRect2.X + myRect2.Width, myRect2.Y),
p3 = new Point(myRect2.X, myRect2.Y + myRect2.Height),
p4 = new Point(myRect2.X + myRect2.Width, myRect2.Y + myRect2.Height);
Point[] pts = new Point[] { p1, p2, p3, p4 };
// Rotate the 4 points.
gw.Transform.TransformPoints(pts);
// Update rotatedRect2 with those rotated points.
rotatedRect2.X = pts.Min(pt => pt.X);
rotatedRect2.Y = pts.Min(pt => pt.Y);
rotatedRect2.Width = pts.Max(pt => pt.X) - pts.Min(pt => pt.X);
rotatedRect2.Height = pts.Max(pt => pt.Y) - pts.Min(pt => pt.Y);
//End of Rotation Area
//For the Resize area
Point rp1 = new Point(resizeRect.X, resizeRect.Y),
rp2 = new Point(resizeRect.X + resizeRect.Width, resizeRect.Y),
rp3 = new Point(resizeRect.X, resizeRect.Y + resizeRect.Height),
rp4 = new Point(resizeRect.X + resizeRect.Width, resizeRect.Y + resizeRect.Height);
Point[] rpts = new Point[] { rp1, rp2, rp3,rp4 };
// Rotate the 4 points.
gw.Transform.TransformPoints(rpts);
// Update rotatedRect2 with those rotated points.
resizeRect2.X = rpts.Min(pt => pt.X);
resizeRect2.Y = rpts.Min(pt => pt.Y);
resizeRect2.Width = rpts.Max(pt => pt.X) - rpts.Min(pt => pt.X);
resizeRect2.Height = rpts.Max(pt => pt.Y) - rpts.Min(pt => pt.Y);
//End of Resize area
drawstuff();
}
Pen pen4 = new Pen(Color.Red, 4);
public void drawstuff()
{
gw.SmoothingMode = SmoothingMode.AntiAlias; // Creates smooth lines.
gw.DrawEllipse(pen2, rectangle);
gw.DrawEllipse(smallPen, myRect2);
gw.DrawRectangle(pen3, resizeRect);
gw.DrawRectangle(pen4, resizeRect2);
}

Rotate and scale ImageBrush

I can rotate an image by:
RotateTransform aRotateTransform = new RotateTransform();
aRotateTransform.CenterX = 0.5;
aRotateTransform.CenterY = 0.5;
tateTransform.Angle = rotationAngle;
ImageBrush bgbrush = new ImageBrush();
bgbrush.RelativeTransform = aRotateTransform;
ScaleTransform s = new ScaleTransform();
s.ScaleX = -1; // how to set without overriding the rotation?
...
How can I scale it in addition? I tried using matrices without success.
You could use a TransformGroup like so:
TransformGroup tg = new Transformgroup();
tg.Children.Add(rotateTransform);
tg.Children.Add(scaleTransform);
bgbrush.RelativeTransform = tg;
You could use a CompositeTransform, it combines translation, rotation and scaling in a single matrix.
Just for completeness. Using Matrix transformations, you would get the expected result by this:
var transform = Matrix.Identity;
transform.RotateAt(rotationAngle, 0.5, 0.5);
transform.Scale(-1, 1);
bgbrush.RelativeTransform = new MatrixTransform(transform);
However, I guess that actually you want to keep the image centered, so you might use ScaleAt instead of Scale:
var transform = Matrix.Identity;
transform.RotateAt(rotationAngle, 0.5, 0.5);
transform.ScaleAt(-1, 1, 0.5, 0.5);
bgBrush.RelativeTransform = new MatrixTransform(transform);
If you just need to rotate the Brush it self and put it into a rectangle later without having to adjust anything else, this worked for me:
// Get the image from somewhere...
ib = new ImageBrush(bmp);
// Here I get it from a larger texture picture.
ib.Viewbox = this.GetVBRect(1728, 634);
ib.Transform = new RotateTransform()
{
CenterX = 0.5,
CenterY = 0.5,
Angle = 180,
};
ib.TileMode = TileMode.Tile;

Printing Image from Silverlight

I'm trying to print a Image in Landscape mode in Silverlight.
I found a great example here. Where most of the code comes from. The code worked perfectly as expected. When I changed the Line to an Image it failed.
Code
Canvas OuterCanvas = new Canvas();
/* a container for everything that will print */
Border OuterBorder = new Border()
{
BorderThickness = new Thickness(3),
BorderBrush = new SolidColorBrush(Colors.Red),
Margin = new Thickness(10)
};
double Width = e.PrintableArea.Width - OuterBorder.Margin.Left - OuterBorder.Margin.Right;
double Height = e.PrintableArea.Height - OuterBorder.Margin.Top - OuterBorder.Margin.Bottom;
/* NOTE: We're trying to force landscape, so swop the width and height */
OuterBorder.Width = Height;
OuterBorder.Height = Width;
/* on portrait, this line goes down (leave the printer settings, we're trying to force landscape) */
Line Line = new Line()
{
X1 = OuterBorder.Width / 2,
Y1 = 0,
X2 = OuterBorder.Width / 2,
Y2 = OuterBorder.Height,
Stroke = new SolidColorBrush(Colors.Blue),
StrokeThickness = 3
};
//
// Here is where I changed the Line to an Image
//
OuterBorder.Child = imageElementInXaml; //Line;
OuterCanvas.Children.Add(OuterBorder);
/* rotate 90 degrees, and move into place */
var transformGroup = new TransformGroup();
transformGroup.Children.Add(new RotateTransform() { Angle = 90 });
transformGroup.Children.Add(new TranslateTransform() { X = e.PrintableArea.Width });
OuterBorder.RenderTransform = transformGroup;
e.PageVisual = OuterCanvas;
e.HasMorePages = false;
I know that a Border can only contain 1 element in which I have done so, and when I printed the image on its own without trying to make it landscape this worked too. So why wont it work when I simply replace the Line with the image Element
So since posting this I found some code (cant remember where now) that has helped me get the printing working. Its not as clean as I would have liked but it works.
void pd_PrintPage(object sender, PrintPageEventArgs e)
{
Image image = new Image();
image.Source = imgPlayer.Source;
//This is important
image.Stretch = Stretch.Uniform;
// Find the full size of the page
Size pageSize = new Size(e.PrintableArea.Width + e.PageMargins.Left + e.PageMargins.Right, e.PrintableArea.Height + e.PageMargins.Top + e.PageMargins.Bottom);
var MARGIN= 10;
// Get additional margins to bring the total to MARGIN (= 96)
Thickness additionalMargin = new Thickness
{
Left = Math.Max(0, MARGIN - e.PageMargins.Left),
Top = Math.Max(0, MARGIN - e.PageMargins.Top),
Right = Math.Max(0, MARGIN - e.PageMargins.Right),
Bottom = Math.Max(0, MARGIN - e.PageMargins.Bottom)
};
// Find the area for display purposes
Size displayArea = new Size(e.PrintableArea.Width - additionalMargin.Left - additionalMargin.Right, e.PrintableArea.Height - additionalMargin.Top - additionalMargin.Bottom);
bool pageIsLandscape = displayArea.Width > displayArea.Height;
bool imageIsLandscape = image.ActualWidth > image.ActualHeight;
double displayAspectRatio = displayArea.Width / displayArea.Height;
double imageAspectRatio = (double)image.ActualWidth / image.ActualHeight;
double scaleX = Math.Min(1, imageAspectRatio / displayAspectRatio);
double scaleY = Math.Min(1, displayAspectRatio / imageAspectRatio);
// Calculate the transform matrix
MatrixTransform transform = new MatrixTransform();
if (pageIsLandscape == imageIsLandscape)
{
// Pure scaling
transform.Matrix = new Matrix(scaleX, 0, 0, scaleY, 0, 0);
}
else
{
// Scaling with rotation
scaleX *= pageIsLandscape ? displayAspectRatio : 1 / displayAspectRatio;
scaleY *= pageIsLandscape ? displayAspectRatio : 1 / displayAspectRatio;
transform.Matrix = new Matrix(0, scaleX, -scaleY, 0, 0, 0);
}
Image image2 = new Image
{
Source = image.Source,
Stretch = Stretch.Fill,
Width = displayArea.Width,
Height = displayArea.Height,
RenderTransform = transform,
RenderTransformOrigin = new Point(0.5, 0.5),
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Margin = additionalMargin,
};
Border border = new Border
{
Child = image,
};
e.PageVisual = border;
}

How do I draw an annulus (doughnut) using GDI+?

I have been trying to draw an annulus (ring with thickness) with a transparent hole and a gradient rim in C# with very little success. Does anyone have any suggestions on how to do this?
here's a nice Blend Utility
Here's the Final result - thanks to BlueMonkMN
Rectangle GetSquareRec(double radius, int x, int y)
{
double r = radius;
double side = Math.Sqrt(Math.Pow(r, 2) / 2);
Rectangle rec = new Rectangle(x - ((int)side), y - ((int)side), (int)(side * 2) + x, (int)(side * 2) + y);
return rec;
}
void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics gTarget = e.Graphics;
gTarget.SmoothingMode = SmoothingMode.AntiAlias;
GraphicsPath pTemp = new GraphicsPath();
Rectangle r = GetSquareRec(200, 225, 225);
pTemp.AddEllipse(r);
pTemp.AddEllipse(GetSquareRec(50, 225, 225));
Color[] colors = new Color[5];
colors[0] = Color.FromArgb(192, 192, 192);
colors[1] = Color.FromArgb(105, 0, 0);
colors[2] = Color.FromArgb(169, 169, 169);
colors[3] = Color.FromArgb(0, 0, 0);
colors[4] = Color.FromArgb(0, 0, 0);
float[] positions = new float[5];
positions[0] = 0f;
positions[1] = 0.1f;
positions[2] = 0.35f;
positions[3] = 0.5f;
positions[4] = 1f;
ColorBlend Cb = new ColorBlend();
Cb.Colors = colors;
Cb.Positions = positions;
PathGradientBrush pgb = new PathGradientBrush(pTemp);
pgb.InterpolationColors = Cb;
pgb.CenterPoint = new PointF(r.X + (r.Width / 2), r.Y + (r.Height / 2));
gTarget.FillPath(pgb, pTemp);
}
http://www.freeimagehosting.net/uploads/th.515733e62e.jpg
This is how I did it in the Scrolling Game Development Kit:
pTemp = new GraphicsPath();
pTemp.AddEllipse(Start.X, Start.Y, End.X - Start.X, End.Y - Start.Y);
pTemp.AddEllipse((Start.X * 3 + End.X) / 4f,
(Start.Y * 3 + End.Y) / 4f,
(End.X - Start.X) / 2f,
(End.Y - Start.Y) / 2f);
PathGradientBrush pgb = new PathGradientBrush(pTemp);
Blend b = new Blend();
b.Factors = new float[] { 0, 1, 1 };
b.Positions = new float[] { 0, .5F, 1 };
pgb.Blend = b;
pgb.CenterColor = ((SolidBrush)CurrentBrush).Color;
pgb.SurroundColors = new Color[] {CurrentPen.Color};
gTarget.FillPath(pgb, pTemp);
pgb.Dispose();
pTemp.Dispose();
(source: enigmadream.com)
I edited the original SGDK code for this sample because originally I wasn't smart enough to scale the gradient to exclude the hole, but now I guess I am :).
If you would rather see the gradient like this:
(source: enigmadream.com)
Then change the blend code to look like this:
Blend blend = new Blend();
blend.Factors = new float[] { 0, 1, 0, 0 };
blend.Positions = new float[] { 0, 0.25F, .5F, 1 };
pgb.Blend = blend;
You may use two calls to Graphics.DrawArc combined, drawing the top and bottom or left and right portions of the annulus, one portion at a time.

Categories