using System; using System.Drawing; using System.Drawing.Drawing2D; using System.ComponentModel; using FastReport.Utils; using System.Drawing.Design; using System.IO; using System.Drawing.Imaging; namespace FastReport { /// /// Base class for all fills. /// [TypeConverter(typeof(FastReport.TypeConverters.FillConverter))] public abstract class FillBase { internal string Name { get { return GetType().Name.Replace("Fill", ""); } } /// /// Returned true if Color = Transparent /// public abstract bool IsTransparent { get; } internal bool FloatDiff(float f1, float f2) { return Math.Abs(f1 - f2) > 1e-4; } /// /// Creates exact copy of this fill. /// /// Copy of this object. public abstract FillBase Clone(); /// /// Creates the GDI+ Brush object. /// /// Drawing rectangle. /// Brush object. public abstract Brush CreateBrush(RectangleF rect); /// /// Creates the GDI+ Brush object with scaling. /// /// Drawing rectangle. /// X scaling coefficient. /// Y scaling coefficient. /// Brush object. public virtual Brush CreateBrush(RectangleF rect, float scaleX, float scaleY) { return CreateBrush(rect); } /// /// Serializes the fill. /// /// Writer object. /// Name of the fill property. /// Fill object to compare with. /// /// This method is for internal use only. /// public virtual void Serialize(FRWriter writer, string prefix, FillBase fill) { if (fill.GetType() != GetType()) writer.WriteStr(prefix, Name); } public virtual void Deserialize(FRReader reader, string prefix) { } public virtual void FinalizeComponent() { } public virtual void InitializeComponent() { } /// /// Fills the specified rectangle. /// /// Draw event arguments. /// Drawing rectangle. public virtual void Draw(FRPaintEventArgs e, RectangleF rect) { rect = new RectangleF(rect.Left * e.ScaleX, rect.Top * e.ScaleY, rect.Width * e.ScaleX, rect.Height * e.ScaleY); using (Brush brush = CreateBrush(rect, e.ScaleX, e.ScaleY)) { e.Graphics.FillRectangle(brush, rect.Left, rect.Top, rect.Width, rect.Height); } } } /// /// Class represents the solid fill. /// public class SolidFill : FillBase { private Color color; /// /// Gets or sets the fill color. /// [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))] public Color Color { get { return color; } set { color = value; } } public override bool IsTransparent { get { return color.A == 0; } } /// public override FillBase Clone() { return new SolidFill(Color); } /// public override int GetHashCode() { return Color.GetHashCode(); } /// public override bool Equals(object obj) { SolidFill f = obj as SolidFill; return f != null && Color == f.Color; } /// public override Brush CreateBrush(RectangleF rect) { return new SolidBrush(Color); } /// public override void Serialize(FRWriter writer, string prefix, FillBase fill) { base.Serialize(writer, prefix, fill); SolidFill c = fill as SolidFill; if (c == null || c.Color != Color) writer.WriteValue(prefix + ".Color", Color); } /// public override void Draw(FRPaintEventArgs e, RectangleF rect) { if (Color == Color.Transparent) return; Brush brush = e.Cache.GetBrush(Color); e.Graphics.FillRectangle(brush, rect.Left * e.ScaleX, rect.Top * e.ScaleY, rect.Width * e.ScaleX, rect.Height * e.ScaleY); } /// /// Initializes the class with Transparent color. /// public SolidFill() : this(Color.Transparent) { } /// /// Initializes the class with specified color. /// /// public SolidFill(Color color) { Color = color; } } /// /// Class represents the linear gradient fill. /// public class LinearGradientFill : FillBase { private Color startColor; private Color endColor; private int angle; private float focus; private float contrast; /// /// Gets or sets the start color of the gradient. /// [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))] public Color StartColor { get { return startColor; } set { startColor = value; } } /// /// Gets or sets the end color of the gradient. /// [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))] public Color EndColor { get { return endColor; } set { endColor = value; } } public override bool IsTransparent { get { return startColor.A == 0 && endColor.A == 0; } } /// /// Gets or sets the angle of the gradient. /// [Editor("FastReport.TypeEditors.AngleEditor, FastReport", typeof(UITypeEditor))] public int Angle { get { return angle; } set { angle = value % 360; } } /// /// Gets or sets the focus point of the gradient. /// /// /// Value is a floating point value from 0 to 1. /// public float Focus { get { return focus; } set { if (value < 0) value = 0; if (value > 1) value = 1; focus = value; } } /// /// Gets or sets the gradient contrast. /// /// /// Value is a floating point value from 0 to 1. /// public float Contrast { get { return contrast; } set { if (value < 0) value = 0; if (value > 1) value = 1; contrast = value; } } /// public override FillBase Clone() { return new LinearGradientFill(StartColor, EndColor, Angle, Focus, Contrast); } /// public override int GetHashCode() { return StartColor.GetHashCode() ^ (EndColor.GetHashCode() << 1) ^ ((Angle.GetHashCode() + 1) << 2) ^ ((Focus.GetHashCode() + 1) << 10) ^ ((Contrast.GetHashCode() + 1) << 20); } /// public override bool Equals(object obj) { LinearGradientFill f = obj as LinearGradientFill; return f != null && StartColor == f.StartColor && EndColor == f.EndColor && Angle == f.Angle && !FloatDiff(Focus, f.Focus) && !FloatDiff(Contrast, f.Contrast); } /// public override Brush CreateBrush(RectangleF rect) { // workaround the gradient bug rect.Inflate(1, 1); LinearGradientBrush result = new LinearGradientBrush(rect, StartColor, EndColor, Angle); result.SetSigmaBellShape(Focus, Contrast); return result; } /// public override void Serialize(FRWriter writer, string prefix, FillBase fill) { base.Serialize(writer, prefix, fill); LinearGradientFill c = fill as LinearGradientFill; if (c == null || c.StartColor != StartColor) writer.WriteValue(prefix + ".StartColor", StartColor); if (c == null || c.EndColor != EndColor) writer.WriteValue(prefix + ".EndColor", EndColor); if (c == null || c.Angle != Angle) writer.WriteInt(prefix + ".Angle", Angle); if (c == null || FloatDiff(c.Focus, Focus)) writer.WriteFloat(prefix + ".Focus", Focus); if (c == null || FloatDiff(c.Contrast, Contrast)) writer.WriteFloat(prefix + ".Contrast", Contrast); } /// /// Initializes the class with default settings. /// public LinearGradientFill() : this(Color.Black, Color.White, 0, 100, 100) { } /// /// Initializes the class with start and end colors. /// /// Start color. /// End color. public LinearGradientFill(Color startColor, Color endColor) : this(startColor, endColor, 0) { } /// /// Initializes the class with start, end colors and angle. /// /// Start color. /// End color. /// Angle. public LinearGradientFill(Color startColor, Color endColor, int angle) : this(startColor, endColor, angle, 0, 100) { } /// /// Initializes the class with start and end colors, angle, focus and contrast. /// /// Start color. /// End color. /// Angle. /// Focus. /// Contrast. public LinearGradientFill(Color startColor, Color endColor, int angle, float focus, float contrast) { StartColor = startColor; EndColor = endColor; Angle = angle; Focus = focus; Contrast = contrast; } } /// /// The style of the path gradient. /// public enum PathGradientStyle { /// /// Elliptic gradient. /// Elliptic, /// /// Rectangular gradient. /// Rectangular } /// /// Class represents the path gradient fill. /// public class PathGradientFill : FillBase { private Color centerColor; private Color edgeColor; private PathGradientStyle style; /// /// Gets or sets the center color of the gradient. /// [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))] public Color CenterColor { get { return centerColor; } set { centerColor = value; } } /// /// Gets or sets the edge color of the gradient. /// [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))] public Color EdgeColor { get { return edgeColor; } set { edgeColor = value; } } /// /// Gets or sets the style of the gradient. /// public PathGradientStyle Style { get { return style; } set { style = value; } } public override bool IsTransparent { get { return centerColor.A == 0 && edgeColor.A == 0; } } /// public override FillBase Clone() { return new PathGradientFill(CenterColor, EdgeColor, Style); } /// public override int GetHashCode() { return CenterColor.GetHashCode() ^ (EdgeColor.GetHashCode() << 1) ^ ((Style.GetHashCode() + 1) << 2); } /// public override bool Equals(object obj) { PathGradientFill f = obj as PathGradientFill; return f != null && CenterColor == f.CenterColor && EdgeColor == f.EdgeColor && Style == f.Style; } /// public override Brush CreateBrush(RectangleF rect) { GraphicsPath path = new GraphicsPath(); if (Style == PathGradientStyle.Rectangular) path.AddRectangle(rect); else { float radius = (float)Math.Sqrt(rect.Width * rect.Width + rect.Height * rect.Height) / 2 + 1; PointF center = new PointF(rect.Left + rect.Width / 2 - 1, rect.Top + rect.Height / 2 - 1); RectangleF r = new RectangleF(center.X - radius, center.Y - radius, radius * 2, radius * 2); path.AddEllipse(r); } PathGradientBrush result = new PathGradientBrush(path); path.Dispose(); result.CenterColor = CenterColor; result.SurroundColors = new Color[] { EdgeColor }; return result; } /// public override void Serialize(FRWriter writer, string prefix, FillBase fill) { base.Serialize(writer, prefix, fill); PathGradientFill c = fill as PathGradientFill; if (c == null || c.CenterColor != CenterColor) writer.WriteValue(prefix + ".CenterColor", CenterColor); if (c == null || c.EdgeColor != EdgeColor) writer.WriteValue(prefix + ".EdgeColor", EdgeColor); if (c == null || c.Style != Style) writer.WriteValue(prefix + ".Style", Style); } /// /// Initializes the class with default settings. /// public PathGradientFill() : this(Color.Black, Color.White, PathGradientStyle.Elliptic) { } /// /// Initializes the class with center, edge colors and style. /// /// Center color. /// Edge color. /// Gradient style. public PathGradientFill(Color centerColor, Color edgeColor, PathGradientStyle style) { CenterColor = centerColor; EdgeColor = edgeColor; Style = style; } } /// /// Class represents the hatch fill. /// public class HatchFill : FillBase { private Color foreColor; private Color backColor; private HatchStyle style; /// /// Gets or sets the foreground color. /// [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))] public Color ForeColor { get { return foreColor; } set { foreColor = value; } } /// /// Gets or sets the background color. /// [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))] public Color BackColor { get { return backColor; } set { backColor = value; } } /// /// Gets or sets the hatch style. /// public HatchStyle Style { get { return style; } set { style = value; } } public override bool IsTransparent { get { return foreColor.A == 0 && backColor.A == 0; } } /// public override FillBase Clone() { return new HatchFill(ForeColor, BackColor, Style); } /// public override int GetHashCode() { return ForeColor.GetHashCode() ^ (BackColor.GetHashCode() << 1) ^ ((Style.GetHashCode() + 1) << 2); } /// public override bool Equals(object obj) { HatchFill f = obj as HatchFill; return f != null && ForeColor == f.ForeColor && BackColor == f.BackColor && Style == f.Style; } /// public override Brush CreateBrush(RectangleF rect) { return new HatchBrush(Style, ForeColor, BackColor); } /// public override void Serialize(FRWriter writer, string prefix, FillBase fill) { base.Serialize(writer, prefix, fill); HatchFill c = fill as HatchFill; if (c == null || c.ForeColor != ForeColor) writer.WriteValue(prefix + ".ForeColor", ForeColor); if (c == null || c.BackColor != BackColor) writer.WriteValue(prefix + ".BackColor", BackColor); if (c == null || c.Style != Style) writer.WriteValue(prefix + ".Style", Style); } /// /// Initializes the class with default settings. /// public HatchFill() : this(Color.Black, Color.White, HatchStyle.BackwardDiagonal) { } /// /// Initializes the class with foreground, background colors and hatch style. /// /// Foreground color. /// Background color. /// Hatch style. public HatchFill(Color foreColor, Color backColor, HatchStyle style) { ForeColor = foreColor; BackColor = backColor; Style = style; } } /// /// Class represents the glass fill. /// public class GlassFill : FillBase { private Color color; private float blend; private bool hatch; /// /// Gets or sets the fill color. /// [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))] public Color Color { get { return color; } set { color = value; } } /// /// Gets or sets the blend value. /// /// Value must be between 0 and 1. /// [DefaultValue(0.2f)] public float Blend { get { return blend; } set { blend = value < 0 ? 0 : value > 1 ? 1 : value; } } /// /// Gets or sets a value determines whether to draw a hatch or not. /// [DefaultValue(true)] public bool Hatch { get { return hatch; } set { hatch = value; } } public override bool IsTransparent { get { return color.A == 0; } } /// public override FillBase Clone() { return new GlassFill(Color, Blend, Hatch); } /// public override int GetHashCode() { return Color.GetHashCode() ^ (Blend.GetHashCode() + 1) ^ ((Hatch.GetHashCode() + 1) << 2); } /// public override bool Equals(object obj) { GlassFill f = obj as GlassFill; return f != null && Color == f.Color && Blend == f.Blend && Hatch == f.Hatch; } /// public override void Draw(FRPaintEventArgs e, RectangleF rect) { rect = new RectangleF(rect.Left * e.ScaleX, rect.Top * e.ScaleY, rect.Width * e.ScaleX, rect.Height * e.ScaleY); // draw fill using (SolidBrush b = new SolidBrush(Color)) { e.Graphics.FillRectangle(b, rect.Left, rect.Top, rect.Width, rect.Height); } // draw hatch if (Hatch) { using (HatchBrush b = new HatchBrush(HatchStyle.DarkUpwardDiagonal, Color.FromArgb(40, Color.White), Color.Transparent)) { e.Graphics.FillRectangle(b, rect.Left, rect.Top, rect.Width, rect.Height); } } // draw blend using (SolidBrush b = new SolidBrush(Color.FromArgb((int)(Blend * 255), Color.White))) { e.Graphics.FillRectangle(b, rect.Left, rect.Top, rect.Width, rect.Height / 2); } } /// public override Brush CreateBrush(RectangleF rect) { return new SolidBrush(Color); } /// public override void Serialize(FRWriter writer, string prefix, FillBase fill) { base.Serialize(writer, prefix, fill); GlassFill c = fill as GlassFill; if (c == null || c.Color != Color) writer.WriteValue(prefix + ".Color", Color); if (c == null || c.Blend != Blend) writer.WriteFloat(prefix + ".Blend", Blend); if (c == null || c.Hatch != Hatch) writer.WriteBool(prefix + ".Hatch", Hatch); } /// /// Initializes the class with default settings. /// public GlassFill() : this(Color.White, 0.2f, true) { } /// /// Initializes the class with given color, blend ratio and hatch style. /// /// Color. /// Blend ratio (0..1). /// Display the hatch. public GlassFill(Color color, float blend, bool hatch) { Color = color; Blend = blend; Hatch = hatch; } } /// /// Class represents the Texture fill. /// public class TextureFill : FillBase { #region Fields private Image image; private int imageWidth; private int imageHeight; private bool preserveAspectRatio; private WrapMode wrapMode; private int imageIndex; private byte[] imageData; private int imageOffsetX; private int imageOffsetY; private static string dummyImageHash; #endregion // Fields #region Properties /// /// Gets or sets value, indicating that image should preserve aspect ratio /// public bool PreserveAspectRatio { get { return preserveAspectRatio; } set { preserveAspectRatio = value; } } /// /// Gets or sets the image width /// public int ImageWidth { get { if (imageWidth <= 0) ForceLoadImage(); return imageWidth; } set { if (value != imageWidth && value > 0) { if (PreserveAspectRatio && imageHeight > 0 && imageWidth > 0) { imageHeight = (int)(imageHeight * (float)value / imageWidth); } imageWidth = value; ResizeImage(imageWidth, ImageHeight); } } } /// /// Gets or sets the image height /// public int ImageHeight { get { if (imageHeight <= 0) ForceLoadImage(); return imageHeight; } set { if (value != imageHeight && value > 0) { if (PreserveAspectRatio && imageWidth > 0 && imageHeight > 0) { imageWidth = (int)(imageWidth * (float)value / imageHeight); } imageHeight = value; ResizeImage(imageWidth, ImageHeight); } } } /// /// Gets or sets the texture wrap mode /// public WrapMode WrapMode { get { return wrapMode; } set { wrapMode = value; } } /// /// Gets or sets the image index /// public int ImageIndex { get { return imageIndex; } set { imageIndex = value; } } /// /// Gets or sets the image data /// public byte[] ImageData { get { return imageData; } set { SetImageData(value); } } /// /// Image left offset /// public int ImageOffsetX { get { return imageOffsetX; } set { imageOffsetX = value; } } /// /// Image top offset /// public int ImageOffsetY { get { return imageOffsetY; } set { imageOffsetY = value; } } public override bool IsTransparent { get { return false; } } #endregion // Properties #region Private Methods private void Clear() { if (image != null) image.Dispose(); image = null; imageData = null; } private void ResizeImage(int width, int height) { if (imageData == null || width <= 0 || height <= 0) return; else { image = ImageHelper.Load(imageData); image = new Bitmap(image, width, height); } } private void ResetImageIndex() { imageIndex = -1; } private void ForceLoadImage() { byte[] data = imageData; if (data == null) return; byte[] saveImageData = data; // imageData will be reset after this line, keep it image = ImageHelper.Load(data); if (imageWidth <= 0 && imageHeight <= 0) { imageWidth = image.Width; imageHeight = image.Height; } else if (imageWidth != image.Width || imageHeight != image.Height) { ResizeImage(imageWidth, imageHeight); } data = saveImageData; } #endregion // Private Methods #region Public Methods /// /// Sets image data to imageData /// /// input image data public void SetImageData(byte[] data) { ResetImageIndex(); image = null; imageData = data; ResizeImage(imageWidth, imageHeight); } /// /// Set image /// /// input image public void SetImage(Image image) { using (MemoryStream ms = new MemoryStream()) { image.Save(ms, image.RawFormat); SetImageData(ms.ToArray()); } } /// public override FillBase Clone() { TextureFill f = new TextureFill(imageData.Clone() as byte[], ImageWidth, ImageHeight, PreserveAspectRatio, WrapMode, ImageOffsetX, ImageOffsetY); //f.ImageIndex = ImageIndex; return f; } /// public override int GetHashCode() { return ImageData.GetHashCode() ^ (ImageWidth.GetHashCode() << 1) ^ ((ImageHeight.GetHashCode() + 1) << 2) ^ ((PreserveAspectRatio.GetHashCode() + 1) << 10) ^ ((WrapMode.GetHashCode() + 1) << 20) ^ ((ImageOffsetX.GetHashCode() + 1) << 40) ^ ((ImageOffsetY.GetHashCode() + 1) << 60); } /// public override bool Equals(object obj) { TextureFill f = obj as TextureFill; return f != null && ImageData == f.ImageData && ImageWidth == f.ImageWidth && ImageHeight == f.ImageHeight && PreserveAspectRatio == f.PreserveAspectRatio && WrapMode == f.WrapMode && ImageOffsetX == f.ImageOffsetX && ImageOffsetY == f.ImageOffsetY; } /// public override Brush CreateBrush(RectangleF rect) { if (image == null) ForceLoadImage(); TextureBrush brush = new TextureBrush(image, WrapMode); brush.TranslateTransform(rect.Left + ImageOffsetX, rect.Top + ImageOffsetY); return brush; } /// public override Brush CreateBrush(RectangleF rect, float scaleX, float scaleY) { if (image == null) ForceLoadImage(); TextureBrush brush = new TextureBrush(image, WrapMode); brush.TranslateTransform(rect.Left + ImageOffsetX * scaleX, rect.Top + ImageOffsetY * scaleY); brush.ScaleTransform(scaleX, scaleY); return brush; } /// public override void Serialize(FRWriter writer, string prefix, FillBase fill) { base.Serialize(writer, prefix, fill); TextureFill c = fill as TextureFill; if (c == null || c.ImageWidth != ImageWidth) writer.WriteValue(prefix + ".ImageWidth", ImageWidth); if (c == null || c.ImageHeight != ImageHeight) writer.WriteValue(prefix + ".ImageHeight", ImageHeight); if (c == null || c.PreserveAspectRatio != PreserveAspectRatio) writer.WriteBool(prefix + ".PreserveAspectRatio", PreserveAspectRatio); if (c == null || c.WrapMode != WrapMode) writer.WriteValue(prefix + ".WrapMode", WrapMode); if (c == null || c.ImageOffsetX != ImageOffsetX) writer.WriteValue(prefix + ".ImageOffsetX", ImageOffsetX); if (c == null || c.ImageOffsetY != ImageOffsetY) writer.WriteValue(prefix + ".ImageOffsetY", ImageOffsetY); // store image data if (writer.SerializeTo != SerializeTo.SourcePages) { if (writer.BlobStore != null) { // check FImageIndex >= writer.BlobStore.Count is needed when we close the designer // and run it again, the BlobStore is empty, but FImageIndex is pointing to // previous BlobStore item and is not -1. if (imageIndex == -1 || imageIndex >= writer.BlobStore.Count) { byte[] bytes = imageData; if (bytes == null) { using (MemoryStream stream = new MemoryStream()) { ImageHelper.Save(image, stream, ImageFormat.Png); bytes = stream.ToArray(); } } if (bytes != null) { string imgHash = BitConverter.ToString(new Murmur3().ComputeHash(bytes)); if (imgHash != dummyImageHash) imageIndex = writer.BlobStore.AddOrUpdate(bytes, imgHash.Replace("-", String.Empty)); } } } else { if (imageData != null) { string hash = BitConverter.ToString(new Murmur3().ComputeHash(imageData)); if (hash != dummyImageHash) { if (c == null || !writer.AreEqual(ImageData, c.ImageData)) writer.WriteStr(prefix + ".ImageData", Convert.ToBase64String(ImageData)); } } } if (writer.BlobStore != null || writer.SerializeTo == SerializeTo.Undo) writer.WriteInt(prefix + ".ImageIndex", imageIndex); } } /// public override void Deserialize(FRReader reader, string prefix) { base.Deserialize(reader, prefix); if (reader.HasProperty(prefix + ".ImageIndex")) { imageIndex = reader.ReadInt(prefix + ".ImageIndex"); } if (reader.BlobStore != null && imageIndex != -1) { SetImageData(reader.BlobStore.Get(imageIndex)); } } /// public override void FinalizeComponent() { base.FinalizeComponent(); Clear(); ResetImageIndex(); } /// public override void InitializeComponent() { base.InitializeComponent(); ResetImageIndex(); } /// public override void Draw(FRPaintEventArgs e, RectangleF rect) { if (image == null) ForceLoadImage(); if (image == null) return; else base.Draw(e, rect); } #endregion //Public Methods #region Constructors /// /// Initializes the class with default texture. /// public TextureFill() { ResetImageIndex(); SetImageData(null); Stream dummy = ResourceLoader.GetStream("FastReport", "icon16.ico"); using (MemoryStream ms = new MemoryStream()) { const int BUFFER_SIZE = 4 * 1024; dummy.CopyTo(ms, BUFFER_SIZE); SetImageData(ms.ToArray()); } WrapMode = WrapMode.Tile; PreserveAspectRatio = true; } /// /// Initializes the class with specified image. /// /// public TextureFill(byte[] imageBytes) { ResetImageIndex(); SetImageData(imageBytes); WrapMode = WrapMode.Tile; PreserveAspectRatio = true; } /// /// Initializes the class with specified image. /// /// public TextureFill(byte[] imageBytes, int width, int height, bool preserveAspectRatio, WrapMode wrapMode, int imageOffsetX, int imageOffsetY) : this(imageBytes) { PreserveAspectRatio = preserveAspectRatio; WrapMode = wrapMode; imageWidth = width; imageHeight = height; ImageOffsetX = imageOffsetX; ImageOffsetY = imageOffsetY; } static TextureFill() { dummyImageHash = "62-57-78-BF-92-9F-81-12-C0-43-6B-5D-B1-D8-04-DD"; } #endregion //Constructors } }