Extracting points coordinates(x,y) from a curve c# - c#

i have a curve that i draw on a picturebox in c# using the method graphics.drawcurve(pen, points, tension)
is there anyway that i can extract all points (x,y coordinates) been covered by the curve ? and save them into an array or list or any thing would be great, so i can use them in a different things.
My code:
void Curved()
{
Graphics gg = pictureBox1.CreateGraphics();
Pen pp = new Pen(Color.Green, 1);
int i,j;
Point[] pointss = new Point[counter];
for (i = 0; i < counter; i++)
{
pointss[i].X = Convert.ToInt32(arrayx[i]);
pointss[i].Y = Convert.ToInt32(arrayy[i]);
}
gg.DrawCurve(pp, pointss, 1.0F);
}
Many thanks in advance.

If you really want a list of pixel co-ordinates, you can still let GDI+ do the heavy lifting:
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace so_pointsfromcurve
{
class Program
{
static void Main(string[] args)
{
/* some test data */
var pointss = new Point[]
{
new Point(5,20),
new Point(17,63),
new Point(2,9)
};
/* instead of to the picture box, draw to a path */
using (var path = new GraphicsPath())
{
path.AddCurve(pointss, 1.0F);
/* use a unit matrix to get points per pixel */
using (var mx = new Matrix(1, 0, 0, 1, 0, 0))
{
path.Flatten(mx, 0.1f);
}
/* store points in a list */
var list_of_points = new List<PointF>(path.PathPoints);
/* show them */
int i = 0;
foreach(var point in list_of_points)
{
Debug.WriteLine($"Point #{ ++i }: X={ point.X }, Y={point.Y}");
}
}
}
}
}
This approach draws the spline to a path, then uses the built-in capability of flattening that path to a sufficiently dense set of line segments (in a way most vector drawing programs do, too) and then extracts the path points from the line mesh into a list of PointFs.
The artefacts of GDI+ device rendering (smoothing, anti-aliasing) are lost in this process.

Related

create custom polygon wall using xbim library

I try to make ifc wall using some polygon points and save to ifc file.
I found some approach and try that, but it does not work.
https://github.com/xBimTeam/XbimGeometry/issues/117
IFC - Representation of triangle mesh
here is my code:
private static void CreateCustomPolygonWall(IfcStore model)
{
using (var txn = model.BeginTransaction("Create Custom Polygon"))
{
List<double[]> points = new List<double[]>();
points.Add(new double[] { 0, 0, 0 });
points.Add(new double[] { 100, 0, 0 });
points.Add(new double[] { 100, 100, 0 });
var list = new List<IfcCartesianPoint>();
foreach (var coordinates in points.Select(p => p.Select(x => new IfcLengthMeasure(x))))
{
var point = model.Instances.New<IfcCartesianPoint>();
point.Coordinates.AddRange(coordinates);
list.Add(point);
}
var faceSet = model.Instances.New<Xbim.Ifc4.TopologyResource.IfcConnectedFaceSet>();
List<int[]> indexes = new List<int[]>();
indexes.Add(new int[] { 0, 1, 2 });
foreach (var t in indexes)
{
var polyLoop = model.Instances.New<Xbim.Ifc4.TopologyResource.IfcPolyLoop>();
polyLoop.Polygon.AddRange(t.Select(k => list[k]));
var bound = model.Instances.New<Xbim.Ifc4.TopologyResource.IfcFaceBound>();
bound.Bound = polyLoop;
var face = model.Instances.New<Xbim.Ifc4.TopologyResource.IfcFace>();
face.Bounds.Add(bound);
faceSet.CfsFaces.Add(face);
}
var surface = model.Instances.New<IfcFaceBasedSurfaceModel>();
surface.FbsmFaces.Add(faceSet);
txn.Commit();
}
}
and if I save to ifc file following the code, the file has polygon points that I describe. but it is not showing any ifc viewer.
#23=IFCCARTESIANPOINT((0.,0.,0.));
#24=IFCCARTESIANPOINT((100.,0.,0.));
#25=IFCCARTESIANPOINT((100.,100.,0.));
so how can I create a polygon wall and save it to ifc file using xbim library?
any hint?
best regards.
You need to create more than just the geometry to create an IFC file which other viewers would process and display. Here is a working example of 3D wall creation. If you want to define the wall as any arbitrary profile, you would replace IfcRectangleProfileDef in the example with other profile definition, likely IfcArbitraryClosedProfileDef with OuterCurve being IfcPolyline.

Is there a fast way to manipulate and buffer a screen in Windows Forms?

I am working on a game for learning purposes, I want to make it only with the .NET-Framework and a Windows Forms project in C#.
I want to get the 'screen' (Something that can be displayed on the window) as an int[]. Modify the array and reapply the altered array to the 'screen' in a buffered manner (So that it doesn't flicker).
I am currently using a Panel, which I draw a Bitmap on with Graphics. The Bitmap is converted to an int[] which I then can modify and reapply to the Bitmap and redraw. It works, but is very slow, especially because I have to scale up the image every frame because my game is only 300x160 and the screen 900x500.
Build up:
// Renders 1 frame
private void Render()
{
// Buffer setup
_bufferedContext = BufferedGraphicsManager.Current;
_buffer = _bufferedContext.Allocate(panel_canvas.CreateGraphics(), new Rectangle(0, 0, _scaledWidth, _scaledHeight));
_screen.clear();
// Get position of player on map
_xScroll = _player._xMap - _screen._width / 2;
_yScroll = _player._yMap - _screen._height / 2;
// Indirectly modifies the int[] '_pixels'
_level.render(_xScroll, _yScroll, _screen);
_player.render(_screen);
// Converts the int[] into a Bitmap (unsafe method is faster)
unsafe
{
fixed (int* intPtr = &_screen._pixels[0])
{
_screenImage = new Bitmap(_trueWidth, _trueHeight, _trueWidth * 4, PixelFormat.Format32bppRgb, new IntPtr(intPtr));
}
}
// Draw generated image on buffer
Graphics g = _buffer.Graphics;
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
g.DrawImage(_screenImage, new Rectangle(0, 0, 900, 506));
// Update panel buffered
_buffer.Render();
}
Is there a faster way without external libraries to make this work?
I'm not to sure about the unsafe code , But I do know about the buffered graphics manager. I think you should create a class for it instead of creating a new one every time.As well as having all of your sprites widths and heights be determined at the load instead of scaling them. That sped up my small game engine a good bit.
class Spritebatch
{
private Graphics Gfx;
private BufferedGraphics bfgfx;
private BufferedGraphicsContext cntxt = BufferedGraphicsManager.Current;
public Spritebatch(Size clientsize, Graphics gfx)
{
cntxt.MaximumBuffer = new Size(clientsize.Width + 1, clientsize.Height + 1);
bfgfx = cntxt.Allocate(gfx, new Rectangle(Point.Empty, clientsize));
Gfx = gfx;
}
public void Begin()
{
bfgfx.Graphics.Clear(Color.Black);
}
public void Draw(Sprite s)
{
bfgfx.Graphics.DrawImageUnscaled(s.Texture, new Rectangle(s.toRec.X - s.rotationOffset.Width,s.toRec.Y - s.rotationOffset.Height,s.toRec.Width,s.toRec.Height));
}
public void drawImage(Bitmap b, Rectangle rec)
{
bfgfx.Graphics.DrawImageUnscaled(b, rec);
}
public void drawImageClipped(Bitmap b, Rectangle rec)
{
bfgfx.Graphics.DrawImageUnscaledAndClipped(b, rec);
}
public void drawRectangle(Pen p, Rectangle rec)
{
bfgfx.Graphics.DrawRectangle(p, rec);
}
public void End()
{
bfgfx.Render(Gfx);
}
}
This is a example of what I used. It's set up to mimic the Spritebatch in Xna. Drawing the images Unscaled will really increase the speed of it.Also creating one instance of the buffered graphics and Context will be faster then creating a new one every time you have to render. So I would advise you to change the line g.DrawImage(_screenImage, new Rectangle(0, 0, 900, 506)); to DrawImageUnscaled(_screenImage, new Rectangle(0, 0, 900, 506));
Edited : Example of how to scale code on sprite load
public Sprite(Bitmap texture, float x, float y, int width, int height)
{
//texture is the image you originally start with.
Bitmap b = new Bitmap(width, height);
// Create a bitmap with the desired width and height
using (Graphics g = Graphics.FromImage(b))
{
g.DrawImage(texture, 0, 0, width, height);
}
// get the graphics from the new image and draw the old image to it
//scaling it to the proper width and height
Texture = b;
//set Texture which is the final picture to the sprite.
//Uppercase Texture is different from lowercase
Scaling of the image is expensive enough, even when is done without any interpolation. To speed up the things, you should minimize memory allocations: when you create brand new Bitmap every frame, it leads to object creation and pixmap buffer allocation. This fact negates all the benefits you get from BufferedGraphics. I advise you to do the following:
Create the Bitmap instance of required size (equal to screen size) only once, outside of Render method.
Use direct access to bitmap data through LockBits method, and try to implement the scaling be hand using nearest pixel.
Of course, using some sort of hardware acceleration for scaling operation is the most preferred option (for example, in opengl all images are usually drawn using textured rectangles, and rendering such rectangles implicitly involves the process of "scaling" when texture sampling is performed).
I'm wondering why do you call this "very slow", because I did some tests and the performance doesn't seem bad. Also have you measured the performance of your rendering code into int[] '_pixels' (unfortunately you haven't provided that code) separately from the bitmap operations, because it might be the slow part.
About your concrete question. As others mentioned, using preallocated buffered graphics and bitmap objects would speed up it a bit.
But do you really need that int[] buffer? BufferedGraphics is already backed internally with a bitmap, so what really happens is:
(1) You fill the int[] buffer
(2) int[] buffer is copied to the new/preallocated Bitmap
(3) Bitmap from step 2 is copied (applying scale) to the BufferedGraphics internal bitmap (via DrawImage)
(4) BufferedGraphics internal bitmap is copied to the screen (via Render)
As you can see, there are a lot of copy operations. The intended usage of BufferedGraphics is:
(1) You fill the BufferedGraphics internal bitmap via drawing methods of the BufferedGraphics.Graphics property. If setup, the Graphics will do the scaling (as well other transformations) for you.
(2) BufferedGraphics internal bitmap is copied to the screen (via Render)
I don't know what your drawing code is doing, but if you can afford it, this definitely should provide the best performance.
Here is my quick and dirty test in case you are interested in:
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Threading;
using System.Windows.Forms;
namespace Test
{
enum RenderMode { NewBitmap, PreallocatedBitmap, Graphics }
class Screen
{
Control canvas;
public Rectangle area;
int[,] pixels;
BitmapData info;
Bitmap bitmap;
BufferedGraphics buffer;
float scaleX, scaleY;
public RenderMode mode = RenderMode.NewBitmap;
public Screen(Control canvas, Size size)
{
this.canvas = canvas;
var bounds = canvas.DisplayRectangle;
scaleX = (float)bounds.Width / size.Width;
scaleY = (float)bounds.Height / size.Height;
area.Size = size;
info = new BitmapData { Width = size.Width, Height = size.Height, PixelFormat = PixelFormat.Format32bppRgb, Stride = size.Width * 4 };
pixels = new int[size.Height, size.Width];
bitmap = new Bitmap(size.Width, size.Height, info.PixelFormat);
buffer = BufferedGraphicsManager.Current.Allocate(canvas.CreateGraphics(), bounds);
buffer.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
ApplyMode();
}
public void ApplyMode()
{
buffer.Graphics.ResetTransform();
if (mode == RenderMode.Graphics)
buffer.Graphics.ScaleTransform(scaleX, scaleY);
}
public void FillRectangle(Color color, Rectangle rect)
{
if (mode == RenderMode.Graphics)
{
using (var brush = new SolidBrush(color))
buffer.Graphics.FillRectangle(brush, rect);
}
else
{
rect.Intersect(area);
if (rect.IsEmpty) return;
int colorData = color.ToArgb();
var pixels = this.pixels;
for (int y = rect.Y; y < rect.Bottom; y++)
for (int x = rect.X; x < rect.Right; x++)
pixels[y, x] = colorData;
}
}
public unsafe void Render()
{
if (mode == RenderMode.NewBitmap)
{
var bounds = canvas.DisplayRectangle;
using (var buffer = BufferedGraphicsManager.Current.Allocate(canvas.CreateGraphics(), bounds))
{
Bitmap bitmap;
fixed (int* pixels = &this.pixels[0, 0])
bitmap = new Bitmap(info.Width, info.Height, info.Stride, info.PixelFormat, new IntPtr(pixels));
buffer.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
buffer.Graphics.DrawImage(bitmap, bounds);
buffer.Render();
}
}
else
{
if (mode == RenderMode.PreallocatedBitmap)
{
fixed (int* pixels = &this.pixels[0, 0])
{
info.Scan0 = new IntPtr(pixels); info.Reserved = 0;
bitmap.LockBits(area, ImageLockMode.WriteOnly | ImageLockMode.UserInputBuffer, info.PixelFormat, info);
bitmap.UnlockBits(info);
}
buffer.Graphics.DrawImage(bitmap, canvas.DisplayRectangle);
}
buffer.Render();
}
}
}
class Game
{
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var game = new Game();
game.Run();
}
Form form;
Control canvas;
Screen screen;
Level level;
Player player;
private Game()
{
form = new Form();
canvas = new Control { Parent = form, Bounds = new Rectangle(0, 0, 900, 506) };
form.ClientSize = canvas.Size;
screen = new Screen(canvas, new Size(300, 160));
level = new Level { game = this };
player = new Player { game = this };
}
private void Run()
{
bool toggleModeRequest = false;
canvas.MouseClick += (sender, e) => toggleModeRequest = true;
var worker = new Thread(() =>
{
int frameCount = 0;
Stopwatch drawT = new Stopwatch(), applyT = new Stopwatch(), advanceT = Stopwatch.StartNew(), renderT = Stopwatch.StartNew(), infoT = Stopwatch.StartNew();
while (true)
{
if (advanceT.ElapsedMilliseconds >= 3)
{
level.Advance(); player.Advance();
advanceT.Restart();
}
if (renderT.ElapsedMilliseconds >= 8)
{
frameCount++;
drawT.Start(); level.Render(); player.Render(); drawT.Stop();
applyT.Start(); screen.Render(); applyT.Stop();
renderT.Restart();
}
if (infoT.ElapsedMilliseconds >= 1000)
{
double drawS = drawT.ElapsedMilliseconds / 1000.0, applyS = applyT.ElapsedMilliseconds / 1000.0, totalS = drawS + applyS;
var info = string.Format("Render using {0} - Frames:{1:n0} FPS:{2:n0} Draw:{3:p2} Apply:{4:p2}",
screen.mode, frameCount, frameCount / totalS, drawS / totalS, applyS / totalS);
form.BeginInvoke(new Action(() => form.Text = info));
infoT.Restart();
}
if (toggleModeRequest)
{
toggleModeRequest = false;
screen.mode = (RenderMode)(((int)screen.mode + 1) % 3);
screen.ApplyMode();
frameCount = 0; drawT.Reset(); applyT.Reset();
}
}
});
worker.IsBackground = true;
worker.Start();
Application.Run(form);
}
class Level
{
public Game game;
public int pos = 0; bool right = true;
public void Advance() { Game.Advance(ref pos, ref right, 0, game.screen.area.Right - 1); }
public void Render()
{
game.screen.FillRectangle(Color.SaddleBrown, new Rectangle(0, 0, pos, game.screen.area.Height));
game.screen.FillRectangle(Color.DarkGreen, new Rectangle(pos, 0, game.screen.area.Right, game.screen.area.Height));
}
}
class Player
{
public Game game;
public int x = 0, y = 0;
public bool right = true, down = true;
public void Advance()
{
Game.Advance(ref x, ref right, game.level.pos, game.screen.area.Right - 5, 2);
Game.Advance(ref y, ref down, 0, game.screen.area.Bottom - 1, 2);
}
public void Render() { game.screen.FillRectangle(Color.Yellow, new Rectangle(x, y, 4, 4)); }
}
static void Advance(ref int pos, ref bool forward, int minPos, int maxPos, int delta = 1)
{
if (forward) { pos += delta; if (pos < minPos) pos = minPos; else if (pos > maxPos) { pos = maxPos; forward = false; } }
else { pos -= delta; if (pos > maxPos) pos = maxPos; else if (pos < minPos) { pos = minPos; forward = true; } }
}
}
}

Eye detection using OpenCVSharp in Unity (fps issues)

I'm currently working on a project involving integrating OpenCVSharp into Unity, to allow eye tracking within a game environment. I've managed to get OpenCVSharp integrated into the Unity editor and currently have eye-detection (not tracking) working within a game. It can find your eyes within a webcam image, then display where its currently detected them on a texture, which I display within the scene.
However its causing a HUGE fps drop, mainly because every frame its converting a webcam texture into an IPLimage so that OpenCV can handle it. It then has to convert it back to a 2Dtexture to be displayed within the scene, after its done all the eye detection. So understandably its too much for the CPU to handle. (As far as I can tell its only using 1 core on my CPU).
Is there a way to do all the eye detection without converting the texture to an IPLimage? Or any other way to fix the fps drop. Some things that I've tried include:
Limiting the frames that it updates on. However this just causes it
to run smoothly, then stutter horribly on the frame that it has to
update.
Looking at threading, but as far as I'm aware Unity doesn't allow it.
As far as I can tell its only using 1 core on my CPU which seems a bit silly. If there was a way to change this it could fix the issue?
Tried different resolutions on the camera, however the resolution that the game can actually run smoothly at, is too small for the eye's to actually be detected, let alone tracked.
I've included the code below, of if you would prefer to look at it in a code editor here is a link to the C# File. Any suggestions or help would be greatly appreciated!
For reference I used code from here (eye detection using opencvsharp).
using UnityEngine;
using System.Collections;
using System;
using System.IO;
using OpenCvSharp;
//using System.Xml;
//using OpenCvSharp.Extensions;
//using System.Windows.Media;
//using System.Windows.Media.Imaging;
public class CaptureScript : MonoBehaviour
{
public GameObject planeObj;
public WebCamTexture webcamTexture; //Texture retrieved from the webcam
public Texture2D texImage; //Texture to apply to plane
public string deviceName;
private int devId = 1;
private int imWidth = 640; //camera width
private int imHeight = 360; //camera height
private string errorMsg = "No errors found!";
static IplImage matrix; //Ipl image of the converted webcam texture
CvColor[] colors = new CvColor[]
{
new CvColor(0,0,255),
new CvColor(0,128,255),
new CvColor(0,255,255),
new CvColor(0,255,0),
new CvColor(255,128,0),
new CvColor(255,255,0),
new CvColor(255,0,0),
new CvColor(255,0,255),
};
const double Scale = 1.25;
const double ScaleFactor = 2.5;
const int MinNeighbors = 2;
// Use this for initialization
void Start ()
{
//Webcam initialisation
WebCamDevice[] devices = WebCamTexture.devices;
Debug.Log ("num:" + devices.Length);
for (int i=0; i<devices.Length; i++) {
print (devices [i].name);
if (devices [i].name.CompareTo (deviceName) == 1) {
devId = i;
}
}
if (devId >= 0) {
planeObj = GameObject.Find ("Plane");
texImage = new Texture2D (imWidth, imHeight, TextureFormat.RGB24, false);
webcamTexture = new WebCamTexture (devices [devId].name, imWidth, imHeight, 30);
webcamTexture.Play ();
matrix = new IplImage (imWidth, imHeight, BitDepth.U8, 3);
}
}
void Update ()
{
if (devId >= 0)
{
//Convert webcam texture to iplimage
Texture2DtoIplImage();
/*DO IMAGE MANIPULATION HERE*/
//do eye detection on iplimage
EyeDetection();
/*END IMAGE MANIPULATION*/
if (webcamTexture.didUpdateThisFrame)
{
//convert iplimage to texture
IplImageToTexture2D();
}
}
else
{
Debug.Log ("Can't find camera!");
}
}
void EyeDetection()
{
using(IplImage smallImg = new IplImage(new CvSize(Cv.Round (imWidth/Scale), Cv.Round(imHeight/Scale)),BitDepth.U8, 1))
{
using(IplImage gray = new IplImage(matrix.Size, BitDepth.U8, 1))
{
Cv.CvtColor (matrix, gray, ColorConversion.BgrToGray);
Cv.Resize(gray, smallImg, Interpolation.Linear);
Cv.EqualizeHist(smallImg, smallImg);
}
using(CvHaarClassifierCascade cascade = CvHaarClassifierCascade.FromFile (#"C:\Users\User\Documents\opencv\sources\data\haarcascades\haarcascade_eye.xml"))
using(CvMemStorage storage = new CvMemStorage())
{
storage.Clear ();
CvSeq<CvAvgComp> eyes = Cv.HaarDetectObjects(smallImg, cascade, storage, ScaleFactor, MinNeighbors, 0, new CvSize(30, 30));
for(int i = 0; i < eyes.Total; i++)
{
CvRect r = eyes[i].Value.Rect;
CvPoint center = new CvPoint{ X = Cv.Round ((r.X + r.Width * 0.5) * Scale), Y = Cv.Round((r.Y + r.Height * 0.5) * Scale) };
int radius = Cv.Round((r.Width + r.Height) * 0.25 * Scale);
matrix.Circle (center, radius, colors[i % 8], 3, LineType.AntiAlias, 0);
}
}
}
}
void OnGUI ()
{
GUI.Label (new Rect (200, 200, 100, 90), errorMsg);
}
void IplImageToTexture2D ()
{
int jBackwards = imHeight;
for (int i = 0; i < imHeight; i++) {
for (int j = 0; j < imWidth; j++) {
float b = (float)matrix [i, j].Val0;
float g = (float)matrix [i, j].Val1;
float r = (float)matrix [i, j].Val2;
Color color = new Color (r / 255.0f, g / 255.0f, b / 255.0f);
jBackwards = imHeight - i - 1; // notice it is jBackward and i
texImage.SetPixel (j, jBackwards, color);
}
}
texImage.Apply ();
planeObj.renderer.material.mainTexture = texImage;
}
void Texture2DtoIplImage ()
{
int jBackwards = imHeight;
for (int v=0; v<imHeight; ++v) {
for (int u=0; u<imWidth; ++u) {
CvScalar col = new CvScalar ();
col.Val0 = (double)webcamTexture.GetPixel (u, v).b * 255;
col.Val1 = (double)webcamTexture.GetPixel (u, v).g * 255;
col.Val2 = (double)webcamTexture.GetPixel (u, v).r * 255;
jBackwards = imHeight - v - 1;
matrix.Set2D (jBackwards, u, col);
//matrix [jBackwards, u] = col;
}
}
}
}
You can move these out of the per frame update loop :
using(CvHaarClassifierCascade cascade = CvHaarClassifierCascade.FromFile (#"C:\Users\User\Documents\opencv\sources\data\haarcascades\haarcascade_eye.xml"))
using(CvMemStorage storage = new CvMemStorage())
No reason to be building the recognizer graph each frame.
Threading is the logical way to go moving forward if you want real speed updates, unity itself is not threaded, but you can fold in other threads if your careful.
Do the texture -> ipl image on the main thread then trigger an event to fire off your thread.
The thread can do all the CV work, probably construct the tex2d and then push back to main to render.
You should also be able to gain some performance improvements if you use:
Color32[] pixels;
pixels = new Color32[webcamTexture.width * webcamTexture.height];
webcamTexture.GetPixels32(pixels);
The Unity doco suggests that this can be quite a bit faster than calling "GetPixels" (and certainly faster than calling GetPixel for each pixel), and then you don't need to scale each RGB channel against 255 manually.

How to use Clipper library to enlarge and fill paths

I am trying to use the Clipper library to modify a graphics path.
I have list of widths that represent outlines / strokes. I want to start with the largest first and work my way down to the smallest.
For this example, we will add 2 strokes with widths of 20 and 10.
I want to take take my graphics path, and expand / offset it by 20 pixels into a new graphics path. I do not want to alter the original path. Then I want to fill the new graphics path with a solid color.
Next, I want to take my original graphics path, and expand / offset it by 10 pixels into a new graphics path. I want to fill this new path with a different color.
Then I want to fill my original path with a different color.
What is the proper way to do this. I have the following method that I created to try and do this, but it is not working properly.
private void createImage(Graphics g, GraphicsPath gp, List<int> strokeWidths)
{
ClipperOffset pathConverter = new ClipperOffset();
Clipper c = new Clipper();
gp.Flatten();
foreach(int strokeSize in strokeWidths)
{
g.clear();
ClipperPolygons polyList = new ClipperPolygons();
GraphicsPath gpTest = (GraphicsPath)gp.Clone();
PathToPolygon(gpTest, polyList, 100);
gpTest.Reset();
c.Execute(ClipType.ctUnion, polyList, PolyFillType.pftPositive, PolyFillType.pftEvenOdd);
pathConverter.AddPaths(polyList, JoinType.jtMiter, EndType.etClosedPolygon);
pathConverter.Execute(ref polyList, strokeSize * 100);
for (int i = 0; i < polyList.Count; i++)
{
// reverses scaling
PointF[] pts2 = PolygonToPointFArray(polyList[i], 100);
gpTest.AddPolygon(pts2);
}
g.FillPath(new SolidBrush(Color.Red), gpTest);
}
}
private void PathToPolygon(GraphicsPath path, ClipperPolygons polys, Single scale)
{
GraphicsPathIterator pathIterator = new GraphicsPathIterator(path);
pathIterator.Rewind();
polys.Clear();
PointF[] points = new PointF[pathIterator.Count];
byte[] types = new byte[pathIterator.Count];
pathIterator.Enumerate(ref points, ref types);
int i = 0;
while (i < pathIterator.Count)
{
ClipperPolygon pg = new ClipperPolygon();
polys.Add(pg);
do
{
IntPoint pt = new IntPoint((int)(points[i].X * scale), (int)(points[i].Y * scale));
pg.Add(pt);
i++;
}
while (i < pathIterator.Count && types[i] != 0);
}
}
private PointF[] PolygonToPointFArray(ClipperPolygon pg, float scale)
{
PointF[] result = new PointF[pg.Count];
for (int i = 0; i < pg.Count; ++i)
{
result[i].X = (float)pg[i].X / scale;
result[i].Y = (float)pg[i].Y / scale;
}
return result;
}
While you've made a pretty reasonable start, you seem to be getting muddled in your createImage() function. You mention wanting different colors with the different offsets and so you're missing a colors array to match your strokeWidths array. Also, it's unclear to me what you're doing with the clipping (union) stuff, but it's probably unnecessary.
So in pseudo-code I suggest something like the following ....
static bool CreateImage(Graphics g, GraphicsPath gp,
List<int> offsets, List<Color> colors)
{
const scale = 100;
if (colors.Count < offsets.Count) return false;
//convert GraphicsPath path to Clipper paths ...
Clipper.Paths cpaths = GPathToCPaths(gp.Flatten(), scale);
//setup the ClipperOffset object ...
ClipperOffset co = new ClipperOffsets();
co.AddPaths(cpaths, JoinType.jtMiter, EndType.etClosedPolygon);
//now loop through each offset ...
foreach(offset in offsets, color in colors)
{
Clipper.Paths csolution = new Clipper.Paths();
co.Execute(csolution, offset);
if (csolution.IsEmpty) break; //useful for negative offsets
//now convert back to floating point coordinate array ...
PointF[] solution = CPathToPointFArray(csolution, scale);
DrawMyPaths(Graphics g, solution, color);
}
}
And something to watch for if you were to use increasingly larger offsets, each polygon drawn in the 'foreach' loop would hide previously drawn polygons.

Resize image 2x2 pixels to say 4x4 pixels keeping colours the same

Please imagine I have a very simple chequerboard image.
Here I represent the image with letters as pixels: black pixel 'B', white pixel 'W'
Here is the starting 2x2 pixel image:
BW
WB
I want to resize the image, lets say by scale of 2x to give me :
BBWW
BBWW
WWBB
WWBB
or scale 4x
BBBBWWWW
BBBBWWWW
WWWWBBBB
WWWWBBBB
So that the pixel colours are not dithered in any way at all.
I have tried this in C# but the image is a mess, all dithered, I need to learn how to do this without dithering/color change.
Here is my code so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
namespace PageTwine
{
public partial class RandonGiff : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// create 2x2 chequerboard:
Bitmap oBitmap = new Bitmap(2, 2);
Graphics oGraphic = Graphics.FromImage(oBitmap);
// color black pixels (i think the default is black but this helps to explain)
SolidBrush oBrush = new SolidBrush(Color.FromArgb(255, 255, 255));
oGraphic.FillRectangle(oBrush, 0, 0, 1, 1);
oGraphic.FillRectangle(oBrush, 1, 1, 1, 1);
//color white pixels
oBrush = new SolidBrush(Color.FromArgb(0, 0, 0));
oGraphic.FillRectangle(oBrush, 0, 1, 1, 1);
oGraphic.FillRectangle(oBrush, 1, 0, 1, 1);
// expand to 4x4
Bitmap result = new Bitmap(4, 4);
using (Graphics graphics = Graphics.FromImage(result))
{
// I don't know what these settings should be :
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
//draw the image into the target bitmap
graphics.DrawImage(oBitmap, 0, 0, result.Width, result.Height);
}
//send image directly to the browser
Response.ContentType = "image/gif";
result.Save(Response.OutputStream, ImageFormat.Gif);
}
}
}
The result is a dithered image, but I need a clean crisp chequerboard effect.
Please can you help me.
EDIT:07/02/12
Thanks for the suggestions so far but I am still searching without finding a solution.
I have created a demo page so you can see the results for yourself.
The URL for the demo is :
http://www.leansoftware.net/servicedesk/publicadhoc/randomgif.aspx?columns=3&rows=3
The demo will create a chequerboard with initial columns x rows as pixels, then enlarge to a gif image 300x300 px.
You will see that colours are distorted/bitty - this I am trying to solve.
Here is the source code for the demo:
using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Linq;
public partial class RandomGif : System.Web.UI.Page
{
public class Coordinates
{
public int x { get; set; }
public int y { get; set; }
}
static Random rand = new Random();
protected void Page_Load(object sender, EventArgs e)
{
// Output type gif
int iwidth;
int iheight;
int bits;
Response.ContentType = "image/gif";
// Get parameters
if (Request.QueryString["count"] != null)
{
bits = Convert.ToInt32(Request.QueryString["count"]);
int square = Convert.ToInt32(Math.Ceiling(Math.Sqrt(bits + Math.Floor(bits / 4.0))));
iwidth = square;
iheight = square;
bits = (square * square)-1;
}
else
if (Request.QueryString["rows"] != null && Request.QueryString["columns"] != null)
{
iwidth = Convert.ToInt32(Request.QueryString["columns"]);
iheight = Convert.ToInt32(Request.QueryString["rows"]);
bits = (iwidth * iheight);
}
else {
return;
}
if (bits > 1000){
Response.Write("Please specify a grid <= 1000 pixels");
return;
}
int plotCount = 0;
//web safe colors : 00, 33, 66, 99, CC, FF;
List<int> webSafeColor = new List<int>();
webSafeColor.Add(0); //00
webSafeColor.Add(51);//33
webSafeColor.Add(102);//66
webSafeColor.Add(153);//99
webSafeColor.Add(204);//CC
webSafeColor.Add(255);//FF
// Create a structure to hold all possible coordinates
var Grid = new List<Coordinates>();
for (int xi = 1; xi <= iwidth; xi++)
{
for (int yi = 1; yi <= iheight; yi++)
{
Grid.Add(new Coordinates { x = xi, y = yi });
plotCount++;
}
}
//create a new Bitmap object
Bitmap oBitmap = new Bitmap(iwidth, iheight);
//create a new Graphics object, which will allow us to draw on our bitmap:
Graphics oGraphic = Graphics.FromImage(oBitmap);
//fill the image rectangle with n bits
for (int i = 1; i <= bits; i++)
{
//Random rand = new Random();
int row = rand.Next(Grid.Count());
// random red
int ircolor = webSafeColor[rand.Next(5)];
// random green
int igcolor = webSafeColor[rand.Next(5)];
// random blue
int ibcolor = webSafeColor[rand.Next(5)];
Color randomColor = Color.FromArgb(ircolor, igcolor, ibcolor);
SolidBrush oBrush = new SolidBrush(randomColor);
oGraphic.FillRectangle(oBrush, Grid[row].x - 1, Grid[row].y - 1, 1, 1);
// Delete this coordinate#
Grid.Remove(Grid[row]);
}
// resize image
Bitmap result = new Bitmap(300, 300);
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.AssumeLinear;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
//draw the image into the target bitmap
graphics.DrawImage(oBitmap, 0, 0, result.Width, result.Height);
}
//send image directly to the browser
Response.ContentType = "image/gif";
result.Save(Response.OutputStream, ImageFormat.Gif);
}
}
If you can suggest modifications we can try them out and see if it fixes the problem.
Thanks Richard
This is a possible duplicate of c# Draw Image (Scaled) to Graphics, does not interpolate correctly. Fixes?
You need to add:
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
Alternately, you could use
graphics.DrawImage(oBitmap, 0, 0, result.Width * 2, result.Height * 2);
to achieve a similar effect of forcing the brush to fill without warping at the right-bottom image edges.
Update:
Adding link for creating indexed images with a custom color palette.
http://support.microsoft.com/kb/319061
Use:
System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;

Categories