SVGs in C#, draw multiple complex rectangles - c#

I'm creating a gannt chart to show hundreds of calendars for individual instances of orders, currently using an algorithm to draw lines and rectangles to create a grid, the problem is I'm the bitmaps are becoming far to large to draw, taking up ram, I've tried multiple different methods including drawing the bitmaps at half size and scaling them up (comes out horribly fuzzy) and still to large.
I want to be able to draw SVGs as I figure for something that draws large simple shapes should reduce the size dramatically compared to bitmaps.
the problem is I cant find anything on msdn that includes any sort of c# library for drawing svgs and I dont want to use external code.
Do I need to create It in XAML or is there a library similar to how bitmaps are drawn ?

Windows Forms = GDI / GDI+
WPF/XAML = DirectX (where possible)
Best bet is to go with WPF/XAML which supports scalable vector graphics (not the same as the .svg file format)
You will need 3rd party code to do SVG in WinForms.
If you are sticking with WinForms, then bitmapping is the only way this can be achieved really. Take a look at PixelFormat - you might be able to reduce the size of your bitmap in memory by using a format which requires fewer bits-per-pixel for example.

There is no need to use external tools or SVGs. With a bit of simple math you can easily just render the necessary parts you want to display. All you need is to know the grid size, the range of dates and the range of line-items that are visible in your view. Let's call them:
DateTime dispStartDate;
DateTime dispEndDate;
int dispStartItem;
int dispEndItem;
int GridSize = 10; //nifty if you'd like a magnification factor
Let's also say you have a class for a Gantt chart item:
class gItem
{
DateTime StartDate{ get; set; }
DateTime EndDate{ get; set; }
int LineNumber{ get; set; }
int Length { get { return EndDate - StartDate; } }
//some other code and stuff you'd like to add
}
Now you need a list containing all of your Gantt chart entries:
List<gItem> GanttItems;
By now you should have assigned values to each of the above variables, now it's time to generate a list of rectangles that would be visible in the view and draw them:
List<Rectangle> EntryRects = new List<Rectangle>();
void UpdateDisplayBounds()
{
foreach(gItem gEntry in GanttItems)
{
if(gEntry.StartDate < dispEndDate && gEntry.EndDate > dispStartDate
&& gEntry.LineNumber >= dispStartItem && gEntry.LineNumber <= dispEndItem)
{
int x = (gEntry.StartDate - dispStartDate) * GridSize;
int y = (gEntry.LineNumber - dispStartItem) * GridSize;
int width = gEntry.Length * GridSize;
int height = GridSize;
EntryRects.Add(new Rectangle(x, y, width, height);
}
}
}
Now you have a list of rectangles only within the display bounds which you can render. So let's draw:
void DrawRectangles(Graphics canvas)//use a picturebox's graphics handler or something for the canvas
{
canvas.Clear(this.BackColor);
using(SolidBrush b = new SolidBrush(Color.Blue)) //Choose your color
{
foreach(Rectangle r in EntryRects)
{
canvas.FillRectangle(b, r);
}
}
}
The above code should get you started. With this you have a list of rectangles that you render on request and the only image taking space in memory is the currently displayed one.

Related

iText 7 ImageRenderInfo Matrix contains negative height on Even number Pages

I have a PDF with four pages. Two images on the first page, one on the second, and one on the third. When I retrieve the value of the image on the second page or fourth,, I get a negative height. I tried setting it to Absolute as a quick fix but the Y position of the image was still slightly off. Also, the height and positioning on page three was fine.
Update: So far, this only seems to be a problem with PDF's created in Google Docs.
My code to extract the PDF images was taken from this thread Using iText 7, what's the proper way to export a Flate encoded image?.
This is how I access the height
var currentPDFImageInfo = extractedImages[i];
var currentPDFImageMatrix = currentPDFImageInfo.RenderInfo.GetImageCtm();
float pdfImageWidth = currentPDFImageMatrix.Get(iText.Kernel.Geom.Matrix.I11);
How I retrieve the PDF image data
public static List<PDFImageInfo> ExtractImagesFromPDF(string filePath)
{
Reader = new PdfReader(filePath);
Document = new PdfDocument(Reader);
var strategy = new ImageRenderListener();
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (int pageNumber = 1; pageNumber <= Document.GetNumberOfPages(); pageNumber++)
{
strategy.CurrentPageNumber = pageNumber;
parser.ProcessPageContent(Document.GetPage(pageNumber));
}
return strategy.ImageInfoList;
}
And of course the Strategy class
public class ImageRenderListener : IEventListener
{
public void EventOccurred(IEventData data, EventType type)
{
if (data is ImageRenderInfo imageData)
{
try
{
if (imageData.GetImage() == null)
{
Console.WriteLine("Image could not be read.");
}
else
{
var pdfImageInfo = new PDFImageInfo(CurrentPageNumber, imageData);
ImageInfoList.Add(pdfImageInfo);
}
}
catch (Exception ex)
{
Console.WriteLine("Image could not be read: {0}.", ex.Message);
}
}
}
public ICollection<EventType> GetSupportedEvents()
{
return null;
}
public int CurrentPageNumber { get; set; }
public List<PDFImageInfo> ImageInfoList { get; set; } = new List<PDFImageInfo>();
}
This is how I access the height
var currentPDFImageInfo = extractedImages[i];
var currentPDFImageMatrix = currentPDFImageInfo.RenderInfo.GetImageCtm();
float pdfImageWidth = currentPDFImageMatrix.Get(iText.Kernel.Geom.Matrix.I11);
This value is the height only under certain circumstances.
Some backgrounds: The contents of a PDF page are drawn by a sequence of instructions in some content stream. Some of these instructions can manipulate the so called current transformation matrix (CTM) which represents an affine transformation, i.e. some combination of a rotation, translation, mirroring, and skewing. Everything other instructions draw is manipulated by the CTM value at the time that instruction is executed.
When a bitmap image is drawn, it is conceptually first reduced to a 1×1 square which then is transformed by the CTM to the final form of the image on the page.
If the image is displayed upright, no rotation or anything else involved, then indeed the I11 value is the width of the displayed image and the I22 value is the height. The I12 and I21 values are 0 then
But often bitmaps are displayed at 90° clockwise or counterclockwise (e.g. because someone held the camera at an 90° angle while shooting). In these cases I11 and I22 are 0 while I12 and I21 are the height and width respectively, with one or the other having a negative sign depending on the direction of the rotation.
If the bitmap is rotated by 180°, I11 and I22 again contain width and height, but both with a negative sign. If it's mirrored along the x axis or the y axis, one of them is negative.
And if the transformation is something else, e.g. a rotation by an angle that's not a multiple of 90°, finding the height and width becomes more complicated.
Actually then it is not even clear what height and width of the skewed, rotated, and mirrored form shall mean.
Thus, as a start please define which values you exactly are after; based on that you can try and determine them from arbitrary transformation matrices.
Another possible cause for unexplainable coordinate data for pages after the first one is that your code re-uses the PdfCanvasProcessor for each page without resetting:
var strategy = new ImageRenderListener();
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (int pageNumber = 1; pageNumber <= Document.GetNumberOfPages(); pageNumber++)
{
strategy.CurrentPageNumber = pageNumber;
parser.ProcessPageContent(Document.GetPage(pageNumber));
}
This causes the graphics state at the end of one page incorrectly to be used as starting graphics state of the next one. Instead you should either use a new PdfCanvasProcessor instance for each page or call parser.Reset() at the start of each page.

Creating a transparent portion of a control to see controls underneath it

I've modified the SuperContextMenuStrip found at CodeProject to meet some of my projects needs. I'm using it as a tooltip for map markers on a GMap.NET Map Control. Here is a sample of what it looks like:
What I would like to do is pretty this up a little by making it look more like a bubble. Similar to an old Google Maps stytle tooltip:
I've spent some time searching on control transparency and I know this isn't an easy thing. This SO question in particular illustrates that.
I have considered overriding the OnPaint method of the SuperContextMenuStrip to draw a background of the GMap.NET control that is underneath the SuperContextMenuStrip, but even that would fail in cases where the marker is hanging off the GMap.NET control:
What is the correct way to create the type of transparency I am looking for?
In Windows Forms, you achieve transparency (or draw irregularly shaped windows) by defining a region. To quote MSDN
The window region is a collection of pixels within the window where
the operating system permits drawing.
In your case, you should have a bitmap that you will use as a mask. The bitmap should have at least two distinct colors. One of these colors should represent the part of the control that you want to be transparent.
You would then create a region like this:
// this code assumes that the pixel 0, 0 (the pixel at the top, left corner)
// of the bitmap passed contains the color you wish to make transparent.
private static Region CreateRegion(Bitmap maskImage) {
Color mask = maskImage.GetPixel(0, 0);
GraphicsPath grapicsPath = new GraphicsPath();
for (int x = 0; x < maskImage.Width; x++) {
for (int y = 0; y < maskImage.Height; y++) {
if (!maskImage.GetPixel(x, y).Equals(mask)) {
grapicsPath.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
}
return new Region(grapicsPath);
}
You would then set the control’s Region to the Region returned by the CreateRegion method.
this.Region = CreateRegion(YourMaskBitmap);
to remove the transparency:
this.Region = new Region();
As you can probably tell from the code above, creating regions is expensive resource-wise. I'd advice saving regions in variables should you need to use them multiple times. If you use cached regions this way, you'd soon experience another problem. The assignment would work the first time but you would get an ObjectDisposedException on subsequent calls.
A little investigation with refrector would reveal the following code within the set accessor of the Region Property:
this.Properties.SetObject(PropRegion, value);
if (region != null)
{
region.Dispose();
}
The Region object is disposed after use!
Luckily, the Region is clonable and all you need to do to preserve your Region object is to assign a clone:
private Region _myRegion = null;
private void SomeMethod() {
_myRegion = CreateRegion(YourMaskBitmap);
}
private void SomeOtherMethod() {
this.Region = _myRegion.Clone();
}

How do you do a 3D transform (perspective) in C# or VB.Net?

What I am looking to do sounds really simple, but no where on the Internet so far have I found a way to do this in DotNet nor found a 3rd party component that does this either (without spending thousands on completely unnecessary features).
Here goes:
I have a jpeg of a floor tile (actual photo) that I create a checkerboard pattern with.
In dotnet, it is easy to rotate and stitch photos together and save the final image as a jpeg.
Next, I want to take that final picture and make it appear as if the "tiles" are laying on a floor for a generic "room scene". Basically adding a 3D perspective to make it appear as if it is actually in the room scene.
Heres a website that is doing something similar with carpeting, however I need to do this in a WinForms application:
Flor Website
Basically, I need to create a 3D perspective of a jpeg, then save it as a new jpeg (then I can put an overlay of the generic room scene).
Anyone have any idea on where to get a 3rd party DotNet image processing module that can do this seemingly simple task?
It is not so simple because you need a 3D transformation, which is more complicated and computationally expensive than a simple 2D transformation such as rotation, scaling or shearing. For you to have an idea of the difference in the math, 2D transformations require 2 by 2 matrices, whereas a projection transformation (which is more complicated than other 3D transforms) requires a 4 by 4 matrix...
What you need is some 3D rendering engine in which you can draw polygons (in a perspective view) and them cover them with a texture (like a carpet). For .Net 2.0, I'd recommend using SlimDX which is a port of DirectX that would allow you to render polygons, but there is some learning curve. If you are using WPF (.Net 3.0 and up), there is a built in 3D canvas that allows you to draw textured polygons in perspective. That might be easier/better to learn than SlimDX for your purposes. I'm sure that there is a way to redirect the output of the 3D canvas towards a jpeg...
You might simplify the problem a lot if you don't require great performance and if you restrict the orientation of the texture (eg. always a horizontal floor or always a vertical wall). If so, you could probably render it yourself with a simple drawing loop in .Net 2.0.
If you just want a plain floor, your code would look like this. WARNING: Obtaining your desired results will take some significant time and refinement, specially if you don't know the math very well. But on the other hand, it is always fun to play with code of this type... (:
Find some sample images below.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace floorDrawer
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
ResizeRedraw = DoubleBuffered = true;
Width = 800;
Height = 600;
Paint += new PaintEventHandler(Form1_Paint);
}
void Form1_Paint(object sender, PaintEventArgs e)
{
// a few parameters that control the projection transform
// these are the parameters that you can modify to change
// the output
double cz = 10; // distortion
double m = 1000; // magnification, usually around 1000 (the pixel width of the monitor)
double y0 = -100; // floor height
string texturePath = #"c:\pj\Hydrangeas.jpg";//#"c:\pj\Chrysanthemum.jpg";
// screen size
int height = ClientSize.Height;
int width = ClientSize.Width;
// center of screen
double cx = width / 2;
double cy = height / 2;
// render destination
var dst = new Bitmap(width, height);
// source texture
var src = Bitmap.FromFile(texturePath) as Bitmap;
// texture dimensions
int tw = src.Width;
int th = src.Height;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
double v = m * y0 / (y - cy) - cz;
double u = (x - cx) * (v + cz) / m;
int uu = ((int)u % tw + tw) % tw;
int vv = ((int)v % th + th) % th;
// The following .SetPixel() and .GetPixel() are painfully slow
// You can replace this whole loop with an equivalent implementation
// using pointers inside unsafe{} code to make it much faster.
// Note that by casting u and v into integers, we are performing
// a nearest pixel interpolation... It's sloppy but effective.
dst.SetPixel(x, y, src.GetPixel(uu, vv));
}
// draw result on the form
e.Graphics.DrawImage(dst, 0, 0);
}
}
}

Plot ECG in Winforms

i have no previous experience in plotting in winforms, in one form i want to plot ecg. or lets say a sin wave or any wave function in a specific area, but what i am doing is e.c.g.. rest of the form will be normal form with buttons and labels,
can anybody be nice enough to through in a tutorial
:)
You have few choices, you can write your own control, that will process data and render it. For more complicated plots, that can be a bit complicated, but the basics are always the same, setting X and Y values ranges and then just draw a line using GDI going from left to right, nothing fancy.
As this can get a bit complicated for more advanced features, you could use some charting controls, I'd read this post or check codeproject.com, I remember, that I saw few attempts to write some decent charting controls, which are open source, new articles will probably be coded in WPF, but you should find something older as well.
Edit:
Some links that you can find useful: Graph plotting lib that's main goal is to simulate ECG or another graph plotting lib
You need to create a custom control.
public class MyECGDrawer : Control{}
In it, you override the OnPaint event
protect override OnPaint(PaintEventArgs pe ){}
Then in the paint function, you draw your graphics the way you want it, let's say sin(x)
// refresh background
pe.Graphics.FillRectangle( Brushes.White, 0, 0, Width, Height );
int prevX = -1, prevY = -1;
for(int x = 0; x < Width; x++ )
{
if( prevX >= 0 )
{
pe.Graphics.DrawLine( Pens.Black, prevX, prevY, x, Math.sin(x) );
}
prevX = x;
prevY = Math.sin(x);
}
To force the ECG to redraw, you call the .Invalidate() function on the control. You should be able to drag and drop the control in your form from the designer.
In the end, the class would look like
public class MyECGDrawer : Control{}
In it, you override the OnPaint event
public class MyECGDrawer : Control
{
protect override OnPaint(PaintEventArgs pe )
{
// refresh background
pe.Graphics.FillRectangle( Brushes.White, 0, 0, Width, Height );
int prevX = -1, prevY = -1;
for(int x = 0; x < Width; x++ )
{
if( prevX >= 0 )
pe.Graphics.DrawLine( Pens.Black, prevX, prevY, x, Math.sin(x) );
prevX = x;
prevY = Math.sin(x);
}
}
}
I wrote up the following and tested it. It seems to do what you want, but note that it is simply plotting sin(x) in a loop with no delay - i.e. the plot for sin(x) streams off the left edge so fast you can hardly see it. You can, however, put a break on any line inside the loop and then step through the loop with F5 to see it work slowly - presumably your streaming ECG data will only arrive at some fixed speed so this should not be a problem in your implementation.
In the following, monitor is a PictureBox on a winforms form. Everything else is local.
private void drawStream(){
const int scaleX = 40;
const int scaleY = 40;
Point monitorTopLeft = new Point(0, 0);
Point MonitorTopLeftMinus1 = new Point(-1, 0);
int halfX = monitor.Width / 2;
int halfY = monitor.Height / 2;
Size size = new Size(halfX + 20, monitor.Height);
Graphics g = monitor.CreateGraphics();
g.TranslateTransform(halfX, halfY);
g.ScaleTransform(scaleX, scaleY);
g.Clear(Color.Black);
g.ResetClip();
float lastY = (float)Math.Sin(0);
float y = lastY;
Pen p = new Pen(Color.White, 0.01F);
float stepX = 1F / scaleX;
for (float x = 0; x < 10; x += stepX) {
g.CopyFromScreen(monitor.PointToScreen(monitorTopLeft), MonitorTopLeftMinus1, size, CopyPixelOperation.SourceCopy);
y = (float)Math.Sin(x);
g.DrawLine(p, -stepX, lastY, 0, y);
lastY = y;
}
}
Some additional info that may be helpful:
The origin in a picture box starts
out at the top left corner.
TranslateTransform allows you to
translate (i.e. move) the origin.
In the example, I translate it by
half the picture box's width and
half its height.
ScaleTransform changes the magnification of the picturebox - note that it even magnifies the width of the pen used to draw on the picturebox - this is why the pen's width is set to 0.01.
CopyFromScreen performs a bitblt. Its source point is relative to the screen, the destination is relative to the picturebox and the size of the rectangle to move disregards any transforms (like the scale and translation transforms we added).
Notice that the X coordinates in the DrawLine method are -stepx and 0. All drawing basically occurs right on the y axis (i.e. x = 0) and then CopyFromScreen moves the drawn portion to the left so that it "streams" off to the left.
Unless you are doing this as a learning experience, you may want to consider looking at the free Microsoft Chart Controls for .NET available here.
http://www.microsoft.com/downloads/details.aspx?FamilyID=130f7986-bf49-4fe5-9ca8-910ae6ea442c&displaylang=en#QuickInfoContainer
That being said, I would offer the following guidelines if you want to roll your own.
Create a user control to encapsulate the plot rendering rather than render directly on the form.
In your control, expose properties to get/set the data you wish to render and add any other properties you want to control the rendering (scaling, panning, colors, etc.)
In you control, either override the OnPaint method or create an event handler for the Paint event. These methods will have a PaintEventArgs object passed to them, which contains a Graphics object as a property. The methods of the Graphics object are used to render points, lines, etc onto the control when it needs to be painted. Most of the drawing operations require either a pen (outlines / lines) or a brush (filled areas). You can use stock objects for these operations (e.g. Pens.Black or Brushes.Red) or you can create your own (see documentation). If you create you own objects, make sure you dispose of them after using them (e.g. using the "using" statement or by calling Dispose).
There are a couple good books on GDI+. I suggest picking one up if you are going in deep.

image processing techniques - direct manipulation of destination image or virtualized?

i need to re-project a series of ariel images that have been referenced in a geographical coordinate system into a UTM projection. I had read that using getPixel and setPixel might be slow - should set up a series of 2 dimensional arrays for intermediate access and then flush the values to the destination image when I am done.
Is this normally this sort of image processing is done by the professionals?
Most image processing is feature detection, segmentation of a scene, fault finding, classification and tracking ....
You might want to take a peek at the book:
Image Processing in C (applicable for other languages too)
Image Processing - Principles and Applications
Which describes many fast and effective means of many image transformations. These two books helped me when I was processing images :)
If I understand your question ... If you are re-aligning or assembling many images, and you don't have orientation as well as position, you can use these algorithms for re-alignment of edges and common features. If you are stitching by position then these algorithms will help in re-sampling/resizing your images for more efficient assembly. There are also some open source libraries for these kinds of things. (OpenCV comes to mind)
edit: If I were re-projecting large images into new projections based on position conversion (and it were dynamic, not static) I would look into building an on-demand application that will refactor images given required resolution and desired position. The application can then pull the nearest resolution of the relative neighbourhood images and provide a result at the desired resolution.
Without more background, I hope this helps!
edit 2:
Comment from answer below:
Depends on the images. If they are fixed size then an array might be good. If they vary then it might be better to implement a system that provides get/setpixel using relative sampling/averaging to match up images of differing res?
I don't know the ins and outs of the images you are working with, and what you are doing, but often abstracting what a 'pixel' is rather than accessing values in an array is useful. This way you can implement conversion, sampling, rotating, correcting algorithms on the backend. Like GetVPixel() or SetVPixel(). This may be more useful when working with multiple, differing res/format images. Like
SetVPixel(img1, coord1, GetVPixel(img2, coord2))
Obviously in an OOP/C# manner. img1 and img2 can be different in size, res, geographics, alignment or anything else providing your backend understands both.
If you don't mind using unsafe code, you can wrap the Bitmap's BitmapData in an object that allows you to efficiently get and set pixels. The below code is mostly taken from a gaussian blur filter, with a couple of modifications of my own. It's not the most flexible code if your bitmap formats differ but I hope it illustrates how you can manipulate bitmaps more efficiently.
public unsafe class RawBitmap : IDisposable
{
private BitmapData _bitmapData;
private byte* _begin;
public RawBitmap(Bitmap originBitmap)
{
OriginBitmap = originBitmap;
_bitmapData = OriginBitmap.LockBits(new Rectangle(0, 0, OriginBitmap.Width, OriginBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
_begin = (byte*)(void*)_bitmapData.Scan0;
}
#region IDisposable Members
public void Dispose()
{
OriginBitmap.UnlockBits(_bitmapData);
}
#endregion
public unsafe byte* Begin
{
get { return _begin; }
}
public unsafe byte* this[int x, int y]
{
get
{
return _begin + y * (_bitmapData.Stride) + x * 3;
}
}
public unsafe byte* this[int x, int y, int offset]
{
get
{
return _begin + y * (_bitmapData.Stride) + x * 3 + offset;
}
}
public unsafe void SetColor(int x, int y, Color color)
{
byte* p = this[x, y];
p[0] = color.B;
p[1] = color.G;
p[2] = color.R;
}
public unsafe Color GetColor(int x, int y)
{
byte* p = this[x, y];
return new Color
(
p[2],
p[1],
p[0]
);
}
public int Stride
{
get { return _bitmapData.Stride; }
}
public int Width
{
get { return _bitmapData.Width; }
}
public int Height
{
get { return _bitmapData.Height; }
}
public int GetOffset()
{
return _bitmapData.Stride - _bitmapData.Width * 3;
}
public Bitmap OriginBitmap { get; private set; }
}
The FreeImage library is pretty fast and offers a Cut and Paste that might be useful. The distribution comes with a C# wrapper.
AFAIK the overhead of GetPixel/SetPixel is the call to it, when accessing an array there is no call hence less overhead.
You should start with GetPixel/SetPixel, you can alway override those calls later to use direct data access.

Categories

Resources