/*
i-net software provides programming examples for illustration only, without warranty
either expressed or implied, including, but not limited to, the implied warranties
of merchantability and/or fitness for a particular purpose. This programming example
assumes that you are familiar with the programming language being demonstrated and
the tools used to create and debug procedures. i-net software support professionals
can help explain the functionality of a particular procedure, but they will not modify
these examples to provide added functionality or construct procedures to meet your
specific needs.
© i-net software 1998-2013
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Linq;
using Inet.Viewer.Data;
namespace Inet.Viewer.Data
{
///
/// Enum for the glyph orientation
///
public enum GlyphOrientation
{
///
/// The glyphs always point upwards, regardless of text or block progression.
///
GLYPH_UP = 0,
///
/// The glyphs always point to the right, regardless of text or block progression.
///
GLYPH_RIGHT = 1,
///
/// The glyphs always point to the left, regardless of text or block progression.
///
GLYPH_LEFT = 2,
///
/// The glyphs always point downwards, regardless of text or block progression.
///
GLYPH_DOWN = 3
}
///
/// enum for the different line styles
///
public enum LineStyle
{
///
/// Line has no style, not visible. Not valid for line element.
NoLine = 0,
///
/// Line has single line style.
Single = 1,
///
/// Line has double line style. Not valid for line element.
Double = 2,
///
/// Line has dashed line style.
Dashed = 3,
///
/// Line has dotted line style.
Dotted = 4
}
///
/// This class implements the IPagePainter interface and does all the painting. This class wraps the painting methods of the Graphics
///
internal class Graphics2DPainter
{
private const int MaxHighQualityImagePixelCount = 1000000;
///
/// For a resolution of 92 dpi
///
public const int TwipsToPixel = 15;
private readonly Dictionary allGraphics = new Dictionary();
private readonly bool isPrinting;
private Graphics graphics;
private int graphicsID;
private PageInfo pageInfo;
private System.Drawing.Drawing2D.Matrix originalTransform;
private Region originalClip;
///
/// If transparent colors will be supported.
/// If set to false, the "transparent" Color will be set to the white background
///
private bool transparency = true;
private bool fontAutoScaling = true;
// when this painter is reused the helper objects won't be loaded again, as they are already available
private bool onFirstPaint = true;
// make sure reset() is done
private IList fonts = new List(8);
private IList adorns = new List(8);
private IList links = new List(8);
private float zoom = 1f;
private Brush currFillBrush;
private Pen currPen;
private Adornment currAdornment;
private int currFontID = 0;
private int pageNr = 0;
private Image graphicsImage;
private StringFormat format = new StringFormat(StringFormat.GenericTypographic);
public Matrix internalTransform = new Matrix();
internal Graphics2DPainter(bool isPrinting)
{
this.TextBlocks = new List();
this.isPrinting = isPrinting;
format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.NoWrap | StringFormatFlags.FitBlackBox;
format.SetMeasurableCharacterRanges(new CharacterRange[] { new CharacterRange(0, 1) });
}
///
/// Sets the current adornment.
///
public virtual int Adornment
{
set
{
if (value >= adorns.Count)
{
value = 0;
}
currAdornment = (Adornment)adorns[value];
}
}
///
/// Sets the current pen.
///
public virtual Pen Pen
{
get
{
return currPen;
}
set
{
value.Color = GetOpaquePaint(value.Color);
currPen = value;
}
}
///
/// Sets the current brush.
///
public virtual Brush Brush
{
get
{
return currFillBrush;
}
set
{
currFillBrush = GetOpaquePaint(value);
}
}
///
/// Sets the current font.
///
public virtual int Font
{
set
{
if (value >= fonts.Count)
{
// Exception for very exoctic case: if page n of m has a field with html-text. Afterwards it will be tried to add the pixel based font
// to the amp. But at this time it is to late.
value = 0;
}
currFontID = value;
}
}
///
/// Gets or sets the image needed to create the different graphics objects
///
public Image GraphicsImage
{
get
{
return graphicsImage;
}
set
{
graphicsImage = value;
}
}
///
/// Sets the zoom factor for this painter.
///
/// new zoom factor
public virtual float ZoomFactor
{
get {
return zoom;
}
set
{
if (zoom != value)
{
Scale(value / zoom);
zoom = value;
}
}
}
///
///
///
public virtual void AddFont(Font embeddedFont, int style, int sizeTwips)
{
if (!onFirstPaint)
{
return;
}
// Embedding
Font font = new Font(embeddedFont.FontFamily, sizeTwips, GetFontStyle(style, embeddedFont.FontFamily), GraphicsUnit.Pixel);
fonts.Add(font);
}
///
///
///
public virtual void AddFont(string name, int style, int sizeTwips)
{
if (!onFirstPaint)
{
return;
}
// No Embedding
try
{
FontFamily fontFam = null;
// handle generic fonts
if ("serif".Equals(name.ToLower()))
{
fontFam = FontFamily.GenericSerif;
}
else if ("sansserif".Equals(name.ToLower()))
{
fontFam = FontFamily.GenericSansSerif;
}
else if ("monospaced".Equals(name.ToLower()))
{
fontFam = FontFamily.GenericMonospace;
}
else
{
// no generic font ( in java logical font)
// set to get the FontFamily to check the fontStyle
fontFam = new FontFamily(name);
}
FontStyle fontStyle = GetFontStyle(style, fontFam);
Font font = new Font(fontFam.Name, sizeTwips, fontStyle, GraphicsUnit.Pixel);
fonts.Add(font);
}
catch (ArgumentException)
{
// try font instead of font family
Font font = new Font(name, sizeTwips, GraphicsUnit.Pixel);
FontStyle fontStyle = GetFontStyle(style, font);
font = new Font(name, sizeTwips, fontStyle, GraphicsUnit.Pixel);
fonts.Add(font);
}
}
///
/// This method gets the FontFamily from the font and invokes the GetFontStyle(int style, FontFamily family) method
///
/// the font style in ints that represent the enum
/// The font to get the FontStyle from
///
private static FontStyle GetFontStyle(int style, Font font)
{
return GetFontStyle(style, font.FontFamily);
}
///
/// Converts the inet-Clear Reports fontstyle to the C# FontStyle
///
/// The font style in ints that represent the enum
/// The Fontfaimly
/// The c# FontStyle
private static FontStyle GetFontStyle(int style, FontFamily family)
{
FontStyle fStyle = FontStyle.Regular;
if ((style & 1) > 0)
{
fStyle += (int)FontStyle.Bold;
}
if ((style & 2) > 0)
{
fStyle += (int)FontStyle.Italic;
}
if ((style & 4) > 0)
{
fStyle += (int)FontStyle.Underline;
}
if ((style & 8) > 0)
{
fStyle += (int)FontStyle.Strikeout;
}
if (!family.IsStyleAvailable(fStyle))
{
// Some Fonts (for example Aharoni/ Segui) don't support Regular style. This would normally throw an exception,
// so we have to check the available style first before setting it.
if (family.IsStyleAvailable(FontStyle.Regular))
{
fStyle = FontStyle.Regular;
}
else if (family.IsStyleAvailable(FontStyle.Bold))
{
fStyle = FontStyle.Bold;
}
else if (family.IsStyleAvailable(FontStyle.Italic))
{
fStyle = FontStyle.Italic;
}
}
return fStyle;
}
///
///
///
public virtual void AddAdornment(Adornment adornment)
{
if (onFirstPaint)
{
adorns.Add(adornment);
}
}
///
///
///
public virtual void WriteReportInfo(ReportInfo info)
{
fontAutoScaling = info.IsFontScaling;
}
///
///
/// Also
///
public virtual void WritePageInfo(PageInfo info)
{
this.pageNr = info.PageNr;
this.pageInfo = info;
if (!isPrinting)
{
CreateImage(info);
}
}
///
/// Creates the Image with the new image size
///
///
private void CreateImage(PageInfo info)
{
if (info != null)
{
int width = (int)(info.Width / TwipsToPixel * zoom);
int height = (int)(info.Height / TwipsToPixel * zoom);
this.GraphicsImage = new Bitmap(width, height,PixelFormat.Format24bppRgb);
this.Graphics = Graphics.FromImage(this.GraphicsImage);
this.Graphics.Clear(Color.White);
}
}
///
/// Sets the clipping.
///
/// the clipping outline
public virtual void SetClip(GraphicsPath path)
{
graphics.SetClip(path);
}
///
/// Sets the clipping to a rectangle.
///
/// x coordinate
/// y coordinate
/// the width
/// the height
public virtual void SetClip(int x, int y, int width, int height)
{
graphics.SetClip(new Rectangle(x, y, width, height));
}
///
/// Resets the clipping.
///
public virtual void ResetClip()
{
graphics.Clip = originalClip;
}
///
/// Draws a string.
///
/// the string to draw
/// x coordinate
/// y coordinate
/// rotation degree
/// alignment
/// glyph orientation
/// the width
/// the maximum width
public virtual void DrawString(string str, int x, int y, int rotDeg, int align, int glyphOrientation, int width, int maxWidth)
{
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
Font font = (Font)fonts[currFontID];
if ((int)graphicsID == 0)
{
SizeF size = graphics.MeasureString(str, font);
// Don't draw if font is smaller than one pixel
if (size.Height * zoom < 10)
{
return;
}
}
Point[] p = { new Point(x, y) };
internalTransform.TransformPoints(p);
int docX = p[0].X;
int docY = p[0].Y;
// Textrotation
System.Drawing.Drawing2D.Matrix restore = graphics.Transform;
try
{
Font scaledFont = font;
if (fontAutoScaling && maxWidth > 0)
{
scaledFont = AutoscaleFont(font, str, width, glyphOrientation);
}
graphics.TranslateTransform(x, y);
graphics.RotateTransform(-rotDeg);
graphics.TranslateTransform(-x, -y);
if (glyphOrientation != (int)GlyphOrientation.GLYPH_UP)
{
DrawVerticalText(str, scaledFont, x, y, glyphOrientation, docX, docY);
}
else
{
// the y-position is based on the baseline, but in C# it is based on the toptline
int ascent = font.FontFamily.GetCellAscent(font.Style);
float emHeight = font.FontFamily.GetEmHeight(font.Style);
float factor = font.Size / emHeight;
float baseline = ascent * factor;
graphics.TranslateTransform(x, y - baseline);
graphics.DrawString(str, scaledFont, this.Pen.Brush, 0, 0, format);
TextBlocks.Add(new TextBlock(str, graphics.Transform, scaledFont, GetStringBounds(str, scaledFont, glyphOrientation).ToSize(), docX, docY));
}
}
catch (Exception exc)
{
ViewerUtils.PrintStackTrace(exc);
}
graphics.Transform = restore;
}
///
/// Draws a string with floating-point precession.
///
/// the string to draw
/// x coordinate
/// y coordinate
/// the width
internal void DrawString(string str, float x, float y, float width)
{
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
Font font = (Font)fonts[currFontID];
if (fontAutoScaling && width > 0)
{
font = AutoscaleFont(font, str, width, (int)GlyphOrientation.GLYPH_UP);
}
int ascent = font.FontFamily.GetCellAscent(font.Style);
float emHeight = font.FontFamily.GetEmHeight(font.Style);
float factor = font.Size / emHeight;
float baseline = ascent * factor;
System.Drawing.Drawing2D.Matrix restore = graphics.Transform;
graphics.TranslateTransform(x, y - baseline);
graphics.DrawString(str, font, this.Pen.Brush, 0, 0, format);
graphics.Transform = restore;
}
///
/// Computes an autoscaled font which matches the specified width when drawing the
/// specified string.
///
/// the font to scale
/// the text string
/// the width the autoscaled font will draw the string
/// the glyph orientation
/// the autoscaled font
private System.Drawing.Font AutoscaleFont(System.Drawing.Font font, string str, float width, int glyphOrientation)
{
Font scaledFont = font;
SizeF strBounds;
strBounds = GetStringBounds(str, scaledFont, glyphOrientation);
float actualWidth = (float)strBounds.Width;
int counter = 0;
// To scale with a 2/3 pixel precision. The transformation has to be considered here.
// Clear Reports has a default transformation of 1:115 besides advanced-HTML where it is set to 1:1
double xScale = Math.Abs(graphics.Transform.Elements[3]);
if (xScale == 0)
{
xScale = 1;
}
// If xScale is in the twips area, it is enough to have it to make pixel exactly.
// Without it it would ongoingly scale because the width with cutting the after commas transmitted
double limit = (xScale < 0.1d ? 0.67d : 1d) / xScale;
while (Math.Abs(actualWidth - width) > limit && counter < 100 && actualWidth >= 0.01f)
{
float tmpSize = scaledFont.Size;
// make the steps smaller, to avoid a jumping back and forth of the width. This could happen for very wide lines
float factor = (width / actualWidth + counter) / (counter + 1);
float newSize = scaledFont.Size * factor;
scaledFont = new Font(scaledFont.FontFamily, newSize, scaledFont.Style, GraphicsUnit.Pixel);
strBounds = GetStringBounds(str, scaledFont /*,frc*/, glyphOrientation);
actualWidth = (float)strBounds.Width;
counter++;
}
return scaledFont;
}
///
/// Computes the bounds of the given string, depending on its glyph orientation
/// String whose bounds are to be computed
/// Font to use to compute the bounds
/// Glyph orientation of the individual glyphs
/// Bounds of string, depending on its glyph orientation
private SizeF GetStringBounds(string str, Font tmpFont, int glyphOrientation)
{
if (glyphOrientation == (int)GlyphOrientation.GLYPH_UP)
{
// standard, as before
return graphics.MeasureString(str, tmpFont, int.MaxValue, format);
}
else if (glyphOrientation == (int)GlyphOrientation.GLYPH_DOWN)
{
// go through character by charater. For the glyph rotation the glyphs have to be separated.
// For example in arabic two connect charactes won't be connected anymore.
float totalWidth = 0;
for (int i = 0; i < str.Length; i++)
{
totalWidth += MeasureCharacter(tmpFont, str[i]).Width;
}
return new SizeF(totalWidth, graphics.MeasureString(str, tmpFont, int.MaxValue, format).Height);
}
else
{
// sidewards glyph rotation
// Calculate the amount of characters multiplied Ascent+Descent, according to the drawVerticalText() algorithm
return graphics.MeasureString(str, tmpFont, int.MaxValue, format);
}
}
///
/// Measures the size of a single letter.
///
/// the font
/// the char
/// the size
private SizeF MeasureCharacter(Font font, char chr)
{
RectangleF r = graphics.MeasureCharacterRanges(chr + string.Empty, font, new Rectangle(0, 0, int.MaxValue, int.MaxValue), format)[0].GetBounds(graphics);
return new SizeF(r.Width, r.Height);
}
///
/// Draws the vertical text with the different glyph Orientations
///
/// The string to be drawn
/// the font the string should be drawn with
/// top left corner
/// top left corner
/// The glyph orientation
/// the original x coordinate in the document
/// the original y coordinate in the document
private void DrawVerticalText(string str, Font font, int x, int y, int glyphOrientation, int docX, int docY)
{
char[] txt = str.ToCharArray();
float emHeight = font.FontFamily.GetEmHeight(font.Style);
float factor = font.Size / emHeight;
float ascent = font.FontFamily.GetCellAscent(font.Style) * factor;
float descent = font.FontFamily.GetCellDescent(font.Style) * factor;
float baseline = ascent;
Graphics g = graphics;
System.Drawing.Drawing2D.Matrix restore = g.Transform;
RectangleF[] charBounds = new RectangleF[txt.Length];
for (int i = 0; i < txt.Length; i++)
{
SizeF b = MeasureCharacter(font, txt[i]);
int width = (int)b.Width;
int height = (int)b.Height;
switch (glyphOrientation)
{
case (int)GlyphOrientation.GLYPH_RIGHT:
g.TranslateTransform(x, y);
g.RotateTransform(90f);
g.TranslateTransform(-x, -y);
g.TranslateTransform((width - ascent) / 2 - width, -descent - baseline);
break;
case (int)GlyphOrientation.GLYPH_LEFT:
g.TranslateTransform(x, y);
g.RotateTransform(-90f);
g.TranslateTransform(-x, -y);
g.TranslateTransform((ascent - width) / 2, ascent - baseline);
break;
case (int)GlyphOrientation.GLYPH_DOWN:
g.TranslateTransform(x, y);
g.RotateTransform(180f);
g.TranslateTransform(-x, -y);
g.TranslateTransform(-width, ascent / 2 - baseline);
goto default;
default:
break;
}
// In Java the Paint is used and not FillPaint, that is why we need to use the Pen as a Brush
g.DrawString(txt[i] + string.Empty, font, new SolidBrush(this.Pen.Color), x, y, format);
Region reg = new Region(new RectangleF(x, y, width, height));
reg.Transform(g.Transform);
charBounds[i] = reg.GetBounds(g);
if (glyphOrientation != (int)GlyphOrientation.GLYPH_DOWN)
{
x += height;
}
else
{
x += width;
}
g.Transform = restore;
}
TextBlocks.Add(new TextBlock(str, g.Transform, font, charBounds, docX, docY));
}
///
/// Draws a line.
///
/// x coordinate of the first point
/// y coordinate of the first point
/// x coordinate of the second point
/// y coordinate of the second point
public virtual void DrawLine(int x1, int y1, int x2, int y2)
{
x1 = PixelX(x1);
x2 = PixelX(x2);
y1 = PixelY(y1);
y2 = PixelY(y2);
graphics.DrawLine(this.Pen, x1, y1, x2, y2);
}
///
/// Draws a line with a specified style and width.
///
/// x coordinate of the first point
/// y coordinate of the first point
/// x coordinate of the second point
/// y coordinate of the second point
/// the style
/// the width
public virtual void DrawLine(int x1, int y1, int x2, int y2, LineStyle style, int width)
{
if (style <= 0)
{
return; // no Line
}
Pen pen = SetLineStyle(style, width, false);
x1 = PixelX(x1);
x2 = PixelX(x2);
y1 = PixelY(y1);
y2 = PixelY(y2);
graphics.DrawLine(pen, x1, y1, x2, y2);
if (style == LineStyle.Double)
{
if (width == 0)
{
// hairline
width = 1;
}
// double line
if (y1 == y2)
{
graphics.DrawLine(pen, x1, y1 + 2 * width, x2, y2 + 2 * width);
}
else
{
graphics.DrawLine(pen, x1 + 2 * width, y1, x2 + 2 * width, y2);
}
}
}
///
/// Sets the linetype for lines and box elements
/// all valid line types besides NO_LINE
/// width in twips
/// If the line ends with a square
private Pen SetLineStyle(LineStyle style, float width, bool styleBeSquare)
{
Pen pen = new Pen(this.Pen.Color);
float[] dashes;
switch (style)
{
case LineStyle.Dashed:
{
float dash = Math.Max(75, width); // 75(twips) so that is 5px dahs for a 1px line
dashes = new float[] { dash / 15f, dash / 15f };
pen.DashPattern = dashes;
pen.DashStyle = DashStyle.Custom;
break;
}
case LineStyle.Dotted:
{
// not needed as the Dot rendering in .Net renders is equals the Java implementation
pen.DashStyle = DashStyle.Dot;
break;
}
default:
dashes = null;
break;
}
pen.Width = width;
pen.DashCap = DashCap.Flat;
return pen;
}
///
/// Draws a rectangle.
///
/// x coordinate of the top left point
/// y coordinate of the top left point
/// the width
/// the height
public virtual void DrawRect(int x, int y, int width, int height)
{
x = PixelX(x);
y = PixelY(y);
// performance optimization, only draw when visible
if (!graphics.ClipBounds.IntersectsWith(new Rectangle(x, y, Math.Abs(width) + 1, Math.Abs(height + 1))))
{
return;
}
graphics.DrawRectangle(this.Pen, x, y, width, height);
}
///
/// Draws a rounded rectangle.
///
/// x coordinate of the top left point
/// y coordinate of the top left point
/// the width
/// the height
/// the width of an arc
/// the height of an arc
public virtual void DrawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
{
x = PixelX(x);
y = PixelY(y);
// performance optimization, only draw when visible
if (!graphics.ClipBounds.IntersectsWith(new Rectangle(x, y, Math.Abs(width) + 1, Math.Abs(height + 1))))
{
return;
}
DrawRoundedRectangle(graphics, this.Pen, x, y, width, height, arcWidth, arcHeight);
}
///
/// Fills a rounded rectangle.
///
/// x coordinate of the top left point
/// y coordinate of the top left point
/// the width
/// the height
/// the width of an arc
/// the height of an arc
public virtual void FillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
{
// performance optimization, only draw when visible
if (!graphics.ClipBounds.IntersectsWith(new Rectangle(x, y, Math.Abs(width) + 1, Math.Abs(height + 1))))
{
return;
}
FillRoundedRectangle(graphics, Brush, new RoundedRectangle(x, y, width, height, arcWidth, arcHeight));
}
///
/// Draws a blured shadow for the specified rounded rectangle.
///
/// the rounded rectangle
/// the line width
private void DrawShadow(RoundedRectangle rec, int lineWidth)
{
Brush brush = new Pen(Color.FromArgb(192, this.Pen.Color)).Brush;
int shadowOffset = 20;
int blurSize = 20;
float scale = 1 / 15f;
using (Bitmap bitmap = new Bitmap((int)(scale * rec.Width + 2 * blurSize), (int)(scale * rec.Height + 2 * blurSize)))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.ScaleTransform(scale, scale, MatrixOrder.Append);
GraphicsPath pathShadow = GetRoundedRectanglePath(new RoundedRectangle(blurSize, blurSize, rec.Width + lineWidth, rec.Height + lineWidth, rec.ArcWidth, rec.ArcHeight));
g.FillPath(brush, pathShadow);
}
BlurAlpha(bitmap, 1);
Region clipPre = graphics.Clip;
graphics.ExcludeClip(new Region(GetRoundedRectanglePath(rec)));
graphics.DrawImage(bitmap, rec.X + shadowOffset, rec.Y + shadowOffset, bitmap.Width / scale, bitmap.Height / scale);
graphics.Clip = clipPre;
}
}
///
/// Draws and fills a rounded rectangle.
///
/// x coordinate of the top left point
/// y coordinate of the top left point
/// the width
/// the height
public virtual void DrawFillRect(int x, int y, int width, int height)
{
x = PixelX(x);
y = PixelY(y);
// performance optimization, only draw when visible
if (!graphics.ClipBounds.IntersectsWith(new RectangleF(x, y, Math.Abs(width) + 1, Math.Abs(height) + 1)))
{
return;
}
// to avoid NullPointerException when a wrong protocol is used. The Adornment should be there always
if (currAdornment == null)
{
return;
}
int arcWidth = currAdornment.EllipseWidth;
int arcHeight = currAdornment.EllipseHeight;
int lineWidth = currAdornment.LineWidth;
RoundedRectangle rec = new RoundedRectangle(x, y, width, height, arcWidth, arcHeight);
if (IsVisible(Brush))
{
FillRoundedRectangle(graphics, Brush, rec);
}
if (!IsVisible(this.Pen.Color))
{
// don't draw if transparent
return;
}
if ((arcWidth | arcHeight) != 0)
{
// rounded rectangle will be drawn with one style
if (currAdornment.TopStyle != LineStyle.NoLine)
{
Pen linePen = SetLineStyle(currAdornment.TopStyle, lineWidth, true);
DrawRoundedRectangle(graphics, linePen, rec);
if (currAdornment.TopStyle == LineStyle.Double)
{
rec = new RoundedRectangle(x - 2 * lineWidth, y - 2 * lineWidth, width + 4 * lineWidth, height + 4 * lineWidth, arcWidth + 4 * lineWidth, arcHeight + 4 * lineWidth);
DrawRoundedRectangle(graphics, linePen, rec);
}
}
// draw shadow first
if (currAdornment.ShowShadow)
{
DrawShadow(rec, lineWidth);
}
return;
}
int l = x;
int r = x + width;
int t = y;
int b = y + height;
// positions for double borders
int dl = l;
int dr = r;
int dt = t;
int db = b;
if (currAdornment.TopStyle == LineStyle.Double)
{
dt -= 2 * lineWidth;
}
if (currAdornment.RightStyle == LineStyle.Double)
{
dr += 2 * lineWidth;
}
if (currAdornment.BottomStyle == LineStyle.Double)
{
db += 2 * lineWidth;
}
if (currAdornment.LeftStyle == LineStyle.Double)
{
dl -= 2 * lineWidth;
}
if (currAdornment.ShowShadow)
{
DrawShadow(new RoundedRectangle(dl, dt, dr - dl, db - dt, 0, 0), lineWidth);
}
Pen rectPen = (Pen)Pen.Clone();
rectPen.Width = lineWidth;
if (currAdornment.TopStyle != LineStyle.NoLine && (currAdornment.TopStyle == currAdornment.RightStyle &&
currAdornment.TopStyle == currAdornment.LeftStyle && currAdornment.TopStyle == currAdornment.BottomStyle))
{
// use drawRect if all lines have the same style. The dotted lines will be displayed better
rectPen = SetLineStyle(currAdornment.TopStyle, lineWidth, true);
graphics.DrawRectangle(rectPen, l, t, r - l, b - t);
if (t != dt)
{
graphics.DrawRectangle(rectPen, dl, dt, dr - dl, db - dt);
}
}
else
{
// Top
if (currAdornment.TopStyle != LineStyle.NoLine)
{
rectPen = SetLineStyle(currAdornment.TopStyle, lineWidth, true);
this.Pen.Width = lineWidth;
graphics.DrawLine(rectPen, l, t, r, t);
if (t != dt)
{
graphics.DrawLine(rectPen, dl, dt, dr, dt);
}
}
// Right
if (currAdornment.RightStyle != LineStyle.NoLine)
{
rectPen = SetLineStyle(currAdornment.RightStyle, lineWidth, true);
graphics.DrawLine(rectPen, r, t, r, b);
if (r != dr)
{
graphics.DrawLine(rectPen, dr, dt, dr, db);
}
}
// Bottom
if (currAdornment.BottomStyle != LineStyle.NoLine)
{
rectPen = SetLineStyle(currAdornment.BottomStyle, lineWidth, true);
graphics.DrawLine(rectPen, l, b, r, b);
if (b != db)
{
graphics.DrawLine(rectPen, dl, db, dr, db);
}
}
// Left
if (currAdornment.LeftStyle != LineStyle.NoLine)
{
rectPen = SetLineStyle(currAdornment.LeftStyle, lineWidth, true);
graphics.DrawLine(rectPen, l, t, l, b);
if (l != dl)
{
graphics.DrawLine(rectPen, dl, dt, dl, db);
}
}
}
}
///
/// Fills a rectangle.
///
/// x coordinate of the top left point
/// y coordinate of the top left point
/// the width
/// the height
public virtual void FillRect(int x, int y, int width, int height)
{
FillRect(x, y, width, height, false);
}
///
/// Fills a rectangle.
///
/// x coordinate of the top left point
/// y coordinate of the top left point
/// the width
/// the height
/// smooth flag
public virtual void FillRect(int x, int y, int width, int height, bool smooth)
{
// Performance: only draw when visible
if (!graphics.ClipBounds.IntersectsWith(new Rectangle(x, y, Math.Abs(width) + 1, Math.Abs(height + 1))))
{
return;
}
if (IsVisible(Brush))
{
TextRenderingHint origAntiAliasing = graphics.TextRenderingHint;
if (!smooth)
{
graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
}
graphics.FillRectangle(Brush, x, y, width, height);
if (!smooth)
{
graphics.TextRenderingHint = origAntiAliasing;
}
}
}
///
/// Draws an image.
///
/// the image data as array of bytes
/// x coordinate of the top left point
/// y coordinate of the top left point
/// the width
/// the height
/// the hash value
public virtual void DrawImage(byte[] imgBytes, int x, int y, int width, int height, int hashValue)
{
// hash Value is currently ignored
if (!graphics.ClipBounds.IntersectsWith(new RectangleF(x, y, Math.Abs(width) + 1, Math.Abs(height + 1))))
{
return;
}
Bitmap img = ReadImage(imgBytes);
if (img == null)
{
return;
}
try
{
System.Drawing.Drawing2D.Matrix oldTransform = graphics.Transform;
// use of the Matrix of WPF as it contains caluclation for the Determinatn
System.Windows.Media.Matrix mat = new System.Windows.Media.Matrix();
double determinant = mat.Determinant;
double scaleFactor = 1d;
if (determinant >= 0)
{
scaleFactor = Math.Sqrt(determinant);
}
int pixelsWidth = (int)(width * scaleFactor);
int pixelsHeight = (int)(height * scaleFactor);
if (pixelsWidth * pixelsHeight > MaxHighQualityImagePixelCount)
{
// image to big ( > 1000x1000 ) - to avoid OutOfMemoryException
graphics.DrawImage(img, x, y, width, height);
return;
}
// Picture would not be shown anyways. Additionaly helps to avoid endless loop
if (pixelsWidth <= 0 || pixelsHeight <= 0)
{
return;
}
try
{
graphics.ScaleTransform((float)(1 / scaleFactor), (float)(1 / scaleFactor));
graphics.InterpolationMode = InterpolationMode.High;
graphics.DrawImage(img, x, y, pixelsWidth, pixelsHeight);
}
finally
{
graphics.Transform = oldTransform;
}
}
catch (Exception th)
{
ViewerUtils.PrintStackTrace(th);
}
}
///
/// Create a Bitmap out of the byte data
/// the byte data of the image
/// A bitmap and if something went wrong null
private Bitmap ReadImage(byte[] imgBytes)
{
try
{
MemoryStream ms = new MemoryStream(imgBytes);
Bitmap img = new Bitmap(Image.FromStream(ms));
return img;
}
catch (Exception e1)
{
// IOException - when error during reading imagedata
ViewerUtils.PrintStackTrace(e1);
return null;
}
}
///
/// Draws a polygon
///
/// array with points of the polygon
public virtual void DrawPolygon(Point[] polygon)
{
// performance optimization, only draw when visible
GraphicsPath path = new GraphicsPath();
path.AddPolygon(polygon);
if (!graphics.ClipBounds.IntersectsWith(path.GetBounds()))
{
return;
}
graphics.DrawPolygon(this.Pen, polygon);
}
///
/// Fills a polygon
///
/// array with points of the polygon
public virtual void FillPolygon(Point[] polygon)
{
GraphicsPath path = new GraphicsPath();
path.AddPolygon(polygon);
if (!graphics.ClipBounds.IntersectsWith(path.GetBounds()))
{
return;
}
if (IsVisible(Brush))
{
graphics.FillPolygon(Brush, polygon);
}
}
///
/// Draws an oval.
///
/// x coordinate
/// y coordinate
/// the width
/// the height
public virtual void DrawOval(int x, int y, int width, int height)
{
if (!graphics.ClipBounds.IntersectsWith(new Rectangle(x, y, Math.Abs(width) + 1, Math.Abs(height + 1))))
{
return;
}
graphics.DrawEllipse(this.Pen, x, y, width, height);
}
///
/// Fills an oval.
///
/// x coordinate
/// y coordinate
/// the width
/// the height
public virtual void FillOval(int x, int y, int width, int height)
{
if (!graphics.ClipBounds.IntersectsWith(new Rectangle(x, y, Math.Abs(width) + 1, Math.Abs(height + 1))))
{
return;
}
if (IsVisible(Brush))
{
graphics.FillEllipse(Brush, x, y, width, height);
}
}
///
/// Draws an arc.
///
/// x coordinate
/// y coordinate
/// the width
/// the height
/// start arngle
/// arc angle
public virtual void DrawArc(int x, int y, int width, int height, int startAngle, int arcAngle)
{
// performance optimization, only draw when visible
if (!graphics.ClipBounds.IntersectsWith(new Rectangle(x, y, Math.Abs(width) + 1, Math.Abs(height + 1))))
{
return;
}
graphics.DrawArc(this.Pen, x, y, width, height, startAngle, arcAngle);
}
///
/// Fills an arc.
///
/// x coordinate
/// y coordinate
/// the width
/// the height
/// start arngle
/// arc angle
public virtual void FillArc(int x, int y, int width, int height, int startAngle, int arcAngle)
{
// performance optimization, only draw when visible
if (!graphics.ClipBounds.IntersectsWith(new Rectangle(x, y, Math.Abs(width) + 1, Math.Abs(height + 1))))
{
return;
}
if (IsVisible(Brush))
{
graphics.FillPie(Brush, x, y, width, height, 360 - startAngle - arcAngle, arcAngle);
}
}
///
/// Adds a subreport on-demand.
///
/// the page clip of the subreport
public virtual void AddSubreportOnDemand(PageClip clip)
{
if (onFirstPaint)
{
links.Add(clip);
}
}
///
/// Adds a hyperlink.
///
/// the page clip of the hyperlink
public virtual void AddHyperlink(PageClip clip)
{
if (onFirstPaint)
{
if (clip.LinkType == LinkType.Hyperlink)
{
// merge hyperlink with a tooltip at the same location if any exists
try
{
PageClip toolTipClip = links.First(l => l.LinkType == LinkType.ToolTip && l.EqualLocation(clip));
links.Remove(toolTipClip);
clip.ToolTip = toolTipClip.ToolTip;
}
catch (InvalidOperationException)
{
// no such tooltip exists => ignore
}
}
links.Add(clip);
}
}
///
/// Called after the rendering is done.
///
public virtual void FinishPage()
{
onFirstPaint = false;
}
///
/// Gets and sets the graphics instance.
///
public virtual Graphics Graphics
{
get
{
return graphics;
}
set
{
graphics = (Graphics)value;
graphicsID = 0;
allGraphics.Clear();
if (graphics != null)
{
// Create Graphics with ID 0
Create(0);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
Scale(1 / 15.0 * zoom);
}
}
}
///
/// Invokes a ScaleTransform on the grahpics with this factor
///
/// 100% = 1.0
private void Scale(double factor)
{
if (graphics != null)
{
graphics.ScaleTransform((float)factor, (float)factor);
originalTransform = graphics.Transform;
originalClip = graphics.Clip;
}
}
///
/// Clears any added elements (links/tooltips).
///
public virtual void ClearElements()
{
foreach (Font font in fonts)
{
font.Dispose();
}
fonts.Clear();
// Synchronized, as access from MouseListener from SwingPageView
lock (links)
{
links.Clear();
}
adorns.Clear();
onFirstPaint = true;
}
///
/// Creates a graphics state.
///
/// the ID of the graphics state
public virtual void Create(int newGraphicsID)
{
if (graphics == null)
{
this.graphics = Graphics.FromImage(graphicsImage);
}
// store current state
StoreState();
this.graphicsID = newGraphicsID;
}
///
/// Stores the current graphics state (brush, pen) in the map under the current graphics ID.
///
private void StoreState()
{
if (graphics == null)
{
return;
}
GraphicsState oldState;
allGraphics.TryGetValue(graphicsID, out oldState);
if (oldState == null)
{
oldState = new GraphicsState();
allGraphics[graphicsID] = oldState;
}
oldState.SaveDataFromGraphics(this.graphics, this, internalTransform);
}
///
/// is invoked to set a chart or a bean to the normal state.
/// the main grapics will be switched to the current graphics.
/// Doesen't dispose but delete the Graphics State out of the Hahstable and switches to index 0
///
public virtual void Dispose()
{
allGraphics.Remove(graphicsID);
graphicsID = 0;
((GraphicsState)allGraphics[0]).RestoreDataToGrapics(graphics, this);
}
///
/// Switches to a specified graphics state.
///
/// the graphics state to switch to
public virtual void ChangeCurrentGraphics(int newGraphicsID)
{
if (newGraphicsID == graphicsID)
{
return;
}
StoreState();
// restore state
this.graphicsID = newGraphicsID;
GraphicsState newState = (GraphicsState)allGraphics[this.graphicsID];
newState.RestoreDataToGrapics(this.graphics, this);
}
///
/// Applies a transform matrix via multiplication.
///
/// the matrix to multiplicate
public virtual void Transform(System.Drawing.Drawing2D.Matrix transform)
{
System.Drawing.Drawing2D.Matrix t = graphics.Transform;
t.Multiply(transform);
graphics.Transform = t;
internalTransform.Multiply(transform);
}
///
/// Sets the transform.
///
/// the transform to set
public virtual void SetTransform(System.Drawing.Drawing2D.Matrix transform)
{
internalTransform = transform;
System.Drawing.Drawing2D.Matrix newTransform = originalTransform.Clone();
newTransform.Multiply(transform, MatrixOrder.Prepend);
try
{
graphics.Transform = newTransform;
}
catch (ArgumentException ae)
{
Console.WriteLine("Something wrong with Set Transform\n\n" + ae.StackTrace);
}
}
///
/// Sets the clip.
///
public virtual Region Clip
{
set
{
graphics.Clip = value;
}
}
///
/// Draws a shape.
///
/// the shape to draw
public virtual void Draw(GraphicsPath shape)
{
RectangleF clip = graphics.ClipBounds;
Pen pen = this.Pen;
if (clip.Size.Width != 0 && clip.Size.Height != 0)
{
graphics.DrawPath(this.Pen, shape);
}
}
///
/// Fills a shape.
///
/// the shape to draw
public virtual void Fill(GraphicsPath shape)
{
RectangleF clip = graphics.ClipBounds;
if (clip.Size.Width != 0 && clip.Size.Height != 0)
{
graphics.FillPath(Brush, shape);
}
}
///
/// Checks if a color is visible (not transparent)
///
private bool IsVisible(Color c)
{
return ((Color)c).A != 0;
}
///
/// Checks if a Brush is visible (not transparent)
///
private bool IsVisible(Brush brush)
{
if (brush is SolidBrush)
{
return IsVisible(((SolidBrush)brush).Color);
}
else if (brush is LinearGradientBrush)
{
LinearGradientBrush lgBrush = (LinearGradientBrush)brush;
foreach (Color col in lgBrush.LinearColors)
{
// if one color is visible the gradient will be visible
if (IsVisible(col))
{
return true;
}
}
// only if no color is visible it will be false
return false;
}
else
{
return IsVisible(new Pen(brush).Color);
}
}
///
/// Returns a non transparent Color if transparency is not allowed
///
private Color GetOpaquePaint(Color color)
{
if (transparency)
{
return color;
}
Color aColor = (Color)color;
float alpha = aColor.A / 255F;
int back = (int)(255 * (1 - alpha));
return Color.FromArgb(back + (int)(aColor.R * alpha), back + (int)(aColor.G * alpha), back + (int)(aColor.B * alpha));
}
///
/// Returns a non transparent Brush if transparency is not allowed
///
private Brush GetOpaquePaint(Brush p)
{
if (transparency)
{
return p;
}
if (p is LinearGradientBrush)
{
LinearGradientBrush gradient = (LinearGradientBrush)p;
Color color1 = (Color)GetOpaquePaint(gradient.LinearColors[0]);
if (!IsVisible(color1))
{
color1 = Color.White;
}
Color color2 = (Color)GetOpaquePaint(gradient.LinearColors[1]);
if (!IsVisible(color2))
{
color2 = Color.White;
}
return new LinearGradientBrush(gradient.Rectangle, color1, color2, LinearGradientMode.Horizontal);
}
return p;
}
///
/// Gets the links.
///
internal virtual IList Links
{
get
{
return links;
}
}
///
/// Sets the graphics object to null
///
internal virtual void ClearGraphics()
{
graphics = null;
}
///
/// Automatic scaling of text blocks to their original sizes.
///
internal bool FontAutoScaling
{
set
{
this.fontAutoScaling = value;
}
}
///
/// Implementation for RoundedRectangles as C# does not offer this class as Java does
///
/// the graphics to paint into
/// the pen to use
/// x coordinate
/// y coordinate
/// the width
/// the height
/// the width of an arc
/// the height of an arc
private static void DrawRoundedRectangle(Graphics graphics, Pen pen, int x, int y, int width, int height, int arcWidth, int arcHeight)
{
GraphicsPath path = GetRoundedRectanglePath(x, y, width, height, arcWidth, arcHeight);
graphics.DrawPath(pen, path);
}
///
/// Overload for Rectangle
///
/// the graphics to paint into
/// the pen to use
/// the rectangle to draw
private static void DrawRoundedRectangle(Graphics graphics, Pen pen, RoundedRectangle rec)
{
DrawRoundedRectangle(graphics, pen, rec.X, rec.Y, rec.Width, rec.Height, rec.ArcWidth, rec.ArcHeight);
}
///
/// Fills the grapchis with the RoundedRectangle
/// The RoundedRectangle will be transformed into a GraphicsPath and than drawn into the graphics objec
///
/// the graphics to paint into
/// the brush to use
/// the rounded rectangle
private static void FillRoundedRectangle(Graphics graphics, Brush brush, RoundedRectangle rec)
{
GraphicsPath path = GetRoundedRectanglePath(rec);
RectangleF clip = graphics.ClipBounds;
if (clip.Size.Width != 0 && clip.Size.Height != 0)
{
graphics.FillPath(brush, path);
}
}
///
/// Method that creates a Graphics Path for a RoundedRectangle
///
/// the rounded rectangle
/// the created graphics path
private static GraphicsPath GetRoundedRectanglePath(RoundedRectangle rec)
{
return GetRoundedRectanglePath(rec.X, rec.Y, rec.Width, rec.Height, rec.ArcWidth, rec.ArcHeight);
}
///
/// Method that creates a Graphics Path for a RoundedRectangle as a RoundedRectangle does not exist in System.Drawings
///
/// x coordinate
/// y coordinate
/// the width
/// the height
/// the width of an arc
/// the height of an arc
/// the created graphics path
private static GraphicsPath GetRoundedRectanglePath(int x, int y, int width, int height, int arcWidth, int arcHeight)
{
// to make sure the rounded corneders are the maximum a circle (wouldn't render proberly otherwise)
arcWidth = Math.Min(arcWidth, width);
arcHeight = Math.Min(arcHeight, height);
GraphicsPath path = new GraphicsPath();
bool drawArc = arcWidth > 0 && arcHeight > 0;
int arcWidthHalf = arcWidth / 2;
int arcHeightHalf = arcHeight / 2;
// draw straight lines only if there is no arc. It would cause strange graphic effects if it would be drawn
if (!drawArc)
{
// top
path.AddLine(x + arcWidthHalf, y, x + width - arcWidthHalf, y);
}
if (drawArc)
{
// upper right arc
path.AddArc(x + width - arcWidth, y, arcWidth, arcHeight, 270, 90);
}
else
{
// right
path.AddLine(x + width, y + arcHeightHalf, x + width, y + height - arcHeightHalf);
}
if (drawArc)
{
// lower right arc
path.AddArc(x + width - arcWidth, y + height - arcHeight, arcWidth, arcHeight, 0, 90);
}
else
{
// bottom
path.AddLine(x + width - arcWidthHalf, y + height, x + arcWidthHalf, y + height);
}
if (drawArc)
{
// lower left arc
path.AddArc(x, y + height - arcHeight, arcWidth, arcHeight, 90, 90);
}
else
{
// left
path.AddLine(x, y + height - arcHeightHalf, x, y + arcHeightHalf);
}
if (drawArc)
{
path.AddArc(x, y, arcWidth, arcHeight, 180, 90); // upper left arc
}
path.CloseFigure();
return path;
}
///
/// Blurs the alpha channel of the specified image. All RGB values are set
/// to 0 (black).
///
/// the image to blur
/// the blur radius
private static void BlurAlpha(Bitmap image, int radius)
{
if (radius < 1)
{
return;
}
int w = image.Width;
int h = image.Height;
int size = w * h;
Rectangle rect = new Rectangle(0, 0, w, h);
int[] dst = new int[size];
int[] src = new int[size];
BitmapData bitmapData = image.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
Marshal.Copy(bitmapData.Scan0, src, 0, src.Length);
image.UnlockBits(bitmapData);
int w1 = w - 1;
int h1 = h - 1;
int div = 2 * radius + 1;
int[] alpha = new int[size];
int[] lookupSum = new int[256 * div];
for (int i = 0; i < lookupSum.Length; i++)
{
lookupSum[i] = i / div;
}
int[] vmin = new int[Math.Max(w, h)];
int[] vmax = new int[Math.Max(w, h)];
int yi = 0, yw = 0;
const int XFF000000 = unchecked((int)0xff000000);
for (int y = 0; y < h; y++)
{
int sum = 0;
for (int i = -radius; i <= radius; i++)
{
int p = src[yi + Math.Min(w1, Math.Max(i, 0))];
sum += (int)((uint)(p & XFF000000) >> 24);
}
for (int x = 0; x < w; x++)
{
alpha[yi] = lookupSum[sum];
if (y == 0)
{
vmin[x] = Math.Min(x + radius + 1, w1);
vmax[x] = Math.Max(x - radius, 0);
}
sum += (int)((uint)(src[yw + vmin[x]] & XFF000000) >> 24) - (int)((uint)(src[yw + vmax[x]] & XFF000000) >> 24);
yi++;
}
yw += w;
}
for (int x = 0; x < w; x++)
{
int sum = 0;
int yp = -radius * w;
for (int i = -radius; i <= radius; i++)
{
yi = Math.Max(0, yp) + x;
sum += alpha[yi];
yp += w;
}
yi = x;
for (int y = 0; y < h; y++)
{
dst[yi] = (int)((uint)(lookupSum[sum] << 24));
if (x == 0)
{
vmin[y] = Math.Min(y + radius + 1, h1) * w;
vmax[y] = Math.Max(y - radius, 0) * w;
}
sum += alpha[x + vmin[y]] - alpha[x + vmax[y]];
yi += w;
}
}
bitmapData = image.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
Marshal.Copy(dst, 0, bitmapData.Scan0, dst.Length);
image.UnlockBits(bitmapData);
}
///
/// Returns the nearest full pixel coordinate in terms of the device space
/// for the specified X coordinate.
/// Used to avoid rendering at sub pixel positions when scaling down.
///
/// the x coordinate
/// the full pixel x coordinate
private int PixelX(int x)
{
return (int)PixelX((float)x);
}
///
/// Returns the nearest full pixel coordinate in terms of the device space
/// for the specified Y coordinate.
/// Used to avoid rendering at subpixel positions when scaling down.
///
/// the y coordinate
/// the full pixel y coordinate
private int PixelY(int y)
{
return (int)PixelY((float)y);
}
///
/// Returns the nearest full pixel coordinate in terms of the device space
/// for the specified X coordinate.
/// Used to avoid rendering at subpixel positions when scaling down.
///
/// the x coordinate
/// the full pixel x coordinate
private float PixelX(float x)
{
if (!isPrinting)
{
float f = graphics.Transform.Elements[0];
if (f > 0 && f < 1f)
{
return (float)(Math.Round(x * f) / f);
}
}
return x;
}
///
/// Returns the nearest full pixel coordinate in terms of the device space
/// for the specified Y coordinate.
/// Used to avoid rendering at subpixel positions when scaling down.
///
/// the y coordinate
/// the full pixel y coordinate
private float PixelY(float y)
{
if (!isPrinting)
{
float f = graphics.Transform.Elements[3];
if (f > 0 && f < 1f)
{
return (float)(Math.Round(y * f) / f);
}
}
return y;
}
///
/// List of all text blocks present on this page.
///
public IList TextBlocks { get; set; }
///
/// Represents a Rectangle with rounded corners with Integer values. The arcs for the 4 corners are the same. Setting of the arc Properties
/// and is only needed once and applies for all corners.
///
/// All Properties use Integer values. For use with float values use the class
private class RoundedRectangle
{
private int x;
private int y;
private int width;
private int height;
private int arcWidth;
private int arcHeight;
///
/// Constructor to fill all the neccesary Properties of this class
///
/// x coordinate
/// y coordinate
/// the width
/// the height
/// the width of an arc
/// the height of an arc
public RoundedRectangle(int x, int y, int width, int height, int arcWidth, int arcHeight)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.arcWidth = arcWidth;
this.arcHeight = arcHeight;
}
///
/// Gets or sets the x coordinate.
///
public int X
{
get { return x; }
set { x = value; }
}
///
/// Gets or sets the y coordinate.
///
public int Y
{
get { return y; }
set { y = value; }
}
///
/// Gets or sets the width of the rectangle
///
public int Width
{
get { return width; }
set { width = value; }
}
///
/// Gets or sets the height of the rectangle
///
public int Height
{
get { return height; }
set { height = value; }
}
///
/// Gets or sets the width of one arc. (Is used for all 4 arcs)
///
public int ArcWidth
{
get { return arcWidth; }
set { arcWidth = value; }
}
///
/// Gets or sets the height of one arc. (Is used for all 4 arcs)
///
public int ArcHeight
{
get { return arcHeight; }
set { arcHeight = value; }
}
}
///
/// Represents a Rectangle with rounded corners with Float values. The arcs for the 4 corners are the same. Setting of the arc Properties
/// and is only needed once and applies for all corners.
///
/// All Properties use Float values. For use with Integer values use the class
private class RoundedRectangleF
{
private float x;
private float y;
private float width;
private float height;
private float arcWidth;
private float arcHeight;
///
/// Constructor to fill all the neccesary Properties of this class
///
/// X Property
/// Y Property
/// Width Property
/// Height Property
/// ArcWidth
/// ArcHeight
public RoundedRectangleF(float x, float y, float width, float height, float arcWidth, float arcHeight)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.arcWidth = arcWidth;
this.arcHeight = arcHeight;
}
///
/// Gets or sets the x coordinate.
///
public float X
{
get { return x; }
set { x = value; }
}
///
/// Gets or sets the y coordinate.
///
public float Y
{
get { return y; }
set { y = value; }
}
///
/// Gets or sets the width of the rectangle
///
public float Width
{
get { return width; }
set { width = value; }
}
///
/// Gets or sets the height of the rectangle
///
public float Height
{
get { return height; }
set { height = value; }
}
///
/// Gets or sets the width of one arc. (Is used for all 4 arcs)
///
public float ArcWidth
{
get { return arcWidth; }
set { arcWidth = value; }
}
///
/// Gets or sets the height of one arc. (Is used for all 4 arcs)
///
public float ArcHeight
{
get { return arcHeight; }
set { arcHeight = value; }
}
}
}
}