using System;
using System.Collections.Generic;
using System.Drawing;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.IO;
using System.Drawing.Imaging;
using FastReport.Utils;
using System.Windows.Forms;
using System.Drawing.Design;
namespace FastReport
{
///
/// Represents a Picture object that can display pictures.
///
///
/// The Picture object can display the following kind of pictures:
///
/// -
/// picture that is embedded in the report file. Use the
/// property to do this;
///
/// -
/// picture that is stored in the database BLOb field. Use the
/// property to specify the name of data column you want to show;
///
/// -
/// picture that is stored in the local disk file. Use the
/// property to specify the name of the file;
///
/// -
/// picture that is stored in the Web. Use the
/// property to specify the picture's URL.
///
///
/// Use the property to specify a size mode. The
/// and properties can be used to restrict the image size if SizeMode
/// is set to AutoSize.
/// The property can be used to display an image with
/// transparent background. Use the property if you want to display
/// semi-transparent image.
///
public partial class PictureObject : PictureObjectBase
{
#region Fields
private Image image;
private int imageIndex;
private Color transparentColor;
private float transparency;
private bool tile;
private Bitmap transparentImage;
private byte[] imageData;
private bool shouldDisposeImage;
private Bitmap grayscaleBitmap;
private int grayscaleHash;
private ImageFormat imageFormat;
#endregion
#region Properties
///
/// Gets or sets the image.
///
///
/// By default, image that you assign to this property is never disposed - you should
/// take care about it. If you want to dispose the image when this PictureObject is disposed,
/// set the property to true right after you assign an image:
///
/// myPictureObject.Image = new Bitmap("file.bmp");
/// myPictureObject.ShouldDisposeImage = true;
///
///
[Category("Data")]
[Editor("FastReport.TypeEditors.ImageEditor, FastReport", typeof(UITypeEditor))]
public virtual Image Image
{
get { return image; }
set
{
image = value;
imageData = null;
UpdateAutoSize();
UpdateTransparentImage();
ResetImageIndex();
imageFormat = CheckImageFormat();
ShouldDisposeImage = false;
}
}
///
/// Gets or sets the extension of image.
///
[Category("Data")]
public virtual ImageFormat ImageFormat
{
get { return imageFormat; }
set
{
if (image == null)
return;
bool wasC = false;
using (MemoryStream stream = new MemoryStream())
{
wasC = ImageHelper.SaveAndConvert(Image, stream, value);
imageData = stream.ToArray();
}
if (!wasC)
return;
ForceLoadImage();
imageFormat = CheckImageFormat();
}
}
///
/// Gets or sets a value indicating that the image should be displayed in grayscale mode.
///
[DefaultValue(false)]
[Category("Appearance")]
public override bool Grayscale
{
get { return base.Grayscale; }
set
{
base.Grayscale = value;
if (!value && grayscaleBitmap != null)
{
grayscaleBitmap.Dispose();
grayscaleBitmap = null;
}
}
}
///
/// Gets or sets a hash of grayscale svg image
///
[Browsable(false)]
public int GrayscaleHash
{
get { return grayscaleHash; }
set { grayscaleHash = value; }
}
///
/// Gets or sets the color of the image that will be treated as transparent.
///
[Category("Appearance")]
[Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))]
public Color TransparentColor
{
get { return transparentColor; }
set
{
transparentColor = value;
UpdateTransparentImage();
}
}
///
/// Gets or sets the transparency of the PictureObject.
///
///
/// Valid range of values is 0..1. Default value is 0.
///
[DefaultValue(0f)]
[Category("Appearance")]
public float Transparency
{
get { return transparency; }
set
{
if (value < 0)
value = 0;
if (value > 1)
value = 1;
transparency = value;
UpdateTransparentImage();
}
}
///
/// Gets or sets a value indicating that the image should be tiled.
///
[DefaultValue(false)]
[Category("Appearance")]
public bool Tile
{
get { return tile; }
set { tile = value; }
}
///
/// Gets or sets a value indicating that the image stored in the
/// property should be disposed when this object is disposed.
///
///
/// By default, image assigned to the property is never disposed - you should
/// take care about it. If you want to dispose the image when this PictureObject is disposed,
/// set this property to true right after you assign an image to the property.
///
[Browsable(false)]
public bool ShouldDisposeImage
{
get { return shouldDisposeImage; }
set { shouldDisposeImage = value; }
}
///
/// Gets or sets a bitmap transparent image
///
[Browsable(false)]
public Bitmap TransparentImage
{
get { return transparentImage; }
set { transparentImage = value; }
}
///
[Browsable(false)]
protected override float ImageWidth
{
get
{
if (Image == null) return 0;
return Image.Width;
}
}
///
[Browsable(false)]
protected override float ImageHeight
{
get
{
if (Image == null) return 0;
return Image.Height;
}
}
#endregion
#region Private Methods
private ImageFormat CheckImageFormat()
{
if (Image == null || Image.RawFormat == null)
return null;
ImageFormat format = null;
if (ImageFormat.Jpeg.Equals(image.RawFormat))
{
format = ImageFormat.Jpeg;
}
else if (ImageFormat.Gif.Equals(image.RawFormat))
{
format = ImageFormat.Gif;
}
else if (ImageFormat.Png.Equals(image.RawFormat))
{
format = ImageFormat.Png;
}
else if (ImageFormat.Emf.Equals(image.RawFormat))
{
format = ImageFormat.Emf;
}
else if (ImageFormat.Icon.Equals(image.RawFormat))
{
format = ImageFormat.Icon;
}
else if (ImageFormat.Tiff.Equals(image.RawFormat))
{
format = ImageFormat.Tiff;
}
else if (ImageFormat.Bmp.Equals(image.RawFormat) || ImageFormat.MemoryBmp.Equals(image.RawFormat))
{
format = ImageFormat.Bmp;
}
else if (ImageFormat.Wmf.Equals(image.RawFormat))
{
format = ImageFormat.Wmf;
}
if (format != null)
return format;
return ImageFormat.Bmp;
}
private void UpdateTransparentImage()
{
if (transparentImage != null)
transparentImage.Dispose();
transparentImage = null;
if (Image is Bitmap)
{
if (TransparentColor != Color.Transparent)
{
transparentImage = new Bitmap(Image);
transparentImage.MakeTransparent(TransparentColor);
}
else if (Transparency != 0)
{
transparentImage = ImageHelper.GetTransparentBitmap(Image, Transparency);
}
}
}
#if MONO
private GraphicsPath GetRoundRectPath(RectangleF rectangleF, float radius)
{
GraphicsPath gp = new GraphicsPath();
if (radius < 1)
radius = 1;
gp.AddLine(rectangleF.X + radius, rectangleF.Y, rectangleF.Width + rectangleF.X - radius, rectangleF.Y);
gp.AddArc(rectangleF.Width + rectangleF.X - radius - 1, rectangleF.Y, radius + 1, radius + 1, 270, 90);
gp.AddLine(rectangleF.Width + rectangleF.X, rectangleF.Y + radius, rectangleF.Width + rectangleF.X, rectangleF.Height + rectangleF.Y - radius);
gp.AddArc(rectangleF.Width + rectangleF.X - radius - 1, rectangleF.Height + rectangleF.Y - radius - 1, radius + 1, radius + 1, 0, 90);
gp.AddLine(rectangleF.Width + rectangleF.X - radius, rectangleF.Height + rectangleF.Y, rectangleF.X + radius, rectangleF.Height + rectangleF.Y);
gp.AddArc(rectangleF.X, rectangleF.Height + rectangleF.Y - radius - 1, radius + 1, radius + 1, 90, 90);
gp.AddLine(rectangleF.X, rectangleF.Height + rectangleF.Y - radius, rectangleF.X, rectangleF.Y + radius);
gp.AddArc(rectangleF.X, rectangleF.Y, radius, radius, 180, 90);
gp.CloseFigure();
return gp;
}
#else
private GraphicsPath GetRoundRectPath(RectangleF rectangleF, float radius)
{
GraphicsPath gp = new GraphicsPath();
if (radius < 1)
radius = 1;
gp.AddArc(rectangleF.Width + rectangleF.X - radius - 1, rectangleF.Y, radius + 1, radius + 1, 270, 90);
gp.AddArc(rectangleF.Width + rectangleF.X - radius - 1, rectangleF.Height + rectangleF.Y - radius - 1, radius + 1, radius + 1, 0, 90);
gp.AddArc(rectangleF.X, rectangleF.Height + rectangleF.Y - radius - 1, radius + 1, radius + 1, 90, 90);
gp.AddArc(rectangleF.X, rectangleF.Y, radius, radius, 180, 90);
gp.CloseFigure();
return gp;
}
#endif
#endregion
#region Protected Methods
///
protected override void Dispose(bool disposing)
{
if (disposing)
DisposeImage();
base.Dispose(disposing);
}
#endregion
#region Public Methods
///
public override void Assign(Base source)
{
base.Assign(source);
PictureObject src = source as PictureObject;
if (src != null)
{
TransparentColor = src.TransparentColor;
Transparency = src.Transparency;
Tile = src.Tile;
Image = src.Image == null ? null : src.Image.Clone() as Image;
if (src.Image == null && src.imageData != null)
imageData = src.imageData;
ShouldDisposeImage = true;
ImageFormat = src.ImageFormat;
}
}
///
/// Draws the image.
///
/// Paint event args.
public override void DrawImage(FRPaintEventArgs e)
{
IGraphics g = e.Graphics;
if (Image == null)
ForceLoadImage();
if (Image == null)
{
DrawErrorImage(g, e);
return;
}
float drawLeft = (AbsLeft + Padding.Left) * e.ScaleX;
float drawTop = (AbsTop + Padding.Top) * e.ScaleY;
float drawWidth = (Width - Padding.Horizontal) * e.ScaleX;
float drawHeight = (Height - Padding.Vertical) * e.ScaleY;
RectangleF drawRect = new RectangleF(
drawLeft,
drawTop,
drawWidth,
drawHeight);
GraphicsPath path = new GraphicsPath();
IGraphicsState state = g.Save();
try
{
//if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one
g.ResetClip();
EstablishImageForm(path, drawLeft, drawTop, drawWidth, drawHeight);
g.SetClip(path, CombineMode.Replace);
Report report = Report;
if (report != null && report.SmoothGraphics)
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
}
if (!Tile)
DrawImageInternal(e, drawRect);
else
{
float y = drawRect.Top;
float width = Image.Width * e.ScaleX;
float height = Image.Height * e.ScaleY;
while (y < drawRect.Bottom)
{
float x = drawRect.Left;
while (x < drawRect.Right)
{
if (transparentImage != null)
g.DrawImage(transparentImage, x, y, width, height);
else
g.DrawImage(Image, x, y, width, height);
x += width;
}
y += height;
}
}
}
finally
{
g.Restore(state);
g.ResetClip();
#if !SKIA
path.Dispose();
#else
path = null;
#endif
}
if (IsPrinting)
{
DisposeImage();
}
}
protected override void DrawImageInternal2(IGraphics graphics, PointF upperLeft, PointF upperRight, PointF lowerLeft)
{
Image image = transparentImage != null ? transparentImage.Clone() as Image : Image.Clone() as Image;
if (image == null)
return;
if (Grayscale)
{
if (grayscaleHash != image.GetHashCode() || grayscaleBitmap == null)
{
if (grayscaleBitmap != null)
grayscaleBitmap.Dispose();
grayscaleBitmap = ImageHelper.GetGrayscaleBitmap(image);
grayscaleHash = image.GetHashCode();
}
image = grayscaleBitmap;
}
//graphics.DrawImage(image, new PointF[] { upperLeft, upperRight, lowerLeft });
DrawImage3Points(graphics, image, upperLeft, upperRight, lowerLeft);
image.Dispose();
}
// This is analogue of graphics.DrawImage(image, PointF[] points) method.
// The original gdi+ method does not work properly in mono on linux/macos.
private void DrawImage3Points(IGraphics g, Image image, PointF p0, PointF p1, PointF p2)
{
// Skip drawing image, when height or width of the image equal zero.
if (image == null || image.Width == 0 || image.Height == 0)
return;
// Skip drawing image, when height or width of the parallelogram for drawing equal zero.
if (p0 == p1 || p0 == p2)
return;
RectangleF rect = new RectangleF(0, 0, image.Width, image.Height);
float m11 = (p1.X - p0.X) / rect.Width;
float m12 = (p1.Y - p0.Y) / rect.Width;
float m21 = (p2.X - p0.X) / rect.Height;
float m22 = (p2.Y - p0.Y) / rect.Height;
g.MultiplyTransform(new System.Drawing.Drawing2D.Matrix(m11, m12, m21, m22, p0.X, p0.Y), MatrixOrder.Prepend);
g.DrawImage(image, rect);
}
///
/// Sets image data to FImageData
///
///
public void SetImageData(byte[] data)
{
imageData = data;
// if autosize is on, load the image.
if (SizeMode == PictureBoxSizeMode.AutoSize)
ForceLoadImage();
}
///
public override void Serialize(FRWriter writer)
{
PictureObject c = writer.DiffObject as PictureObject;
base.Serialize(writer);
#if PRINT_HOUSE
writer.WriteStr("ImageLocation", ImageLocation);
#endif
if (TransparentColor != c.TransparentColor)
writer.WriteValue("TransparentColor", TransparentColor);
if (FloatDiff(Transparency, c.Transparency))
writer.WriteFloat("Transparency", Transparency);
if (Tile != c.Tile)
writer.WriteBool("Tile", Tile);
if (ImageFormat != c.ImageFormat)
writer.WriteValue("ImageFormat", ImageFormat);
// store image data
if (writer.SerializeTo != SerializeTo.SourcePages)
{
if (writer.SerializeTo == SerializeTo.Preview ||
(String.IsNullOrEmpty(ImageLocation) && String.IsNullOrEmpty(DataColumn)))
{
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);
bytes = stream.ToArray();
}
}
if (bytes != null)
{
string imgHash = BitConverter.ToString(new Murmur3().ComputeHash(bytes));
imageIndex = writer.BlobStore.AddOrUpdate(bytes, imgHash);
}
}
}
else
{
if (Image == null && imageData != null)
writer.WriteStr("Image", Convert.ToBase64String(imageData));
else if (!writer.AreEqual(Image, c.Image))
writer.WriteValue("Image", Image);
}
if (writer.BlobStore != null || writer.SerializeTo == SerializeTo.Undo)
writer.WriteInt("ImageIndex", imageIndex);
}
}
}
///
public override void Deserialize(FRReader reader)
{
base.Deserialize(reader);
if (reader.HasProperty("ImageIndex"))
{
imageIndex = reader.ReadInt("ImageIndex");
if (reader.BlobStore != null && imageIndex != -1)
{
//int saveIndex = FImageIndex;
//Image = ImageHelper.Load(reader.BlobStore.Get(FImageIndex));
//FImageIndex = saveIndex;
SetImageData(reader.BlobStore.Get(imageIndex));
}
}
}
///
/// Loads image
///
public override void LoadImage()
{
if (!String.IsNullOrEmpty(ImageLocation))
{
//
try
{
Uri uri = CalculateUri();
if (uri.IsFile)
SetImageData(ImageHelper.Load(uri.LocalPath));
else
SetImageData(ImageHelper.LoadURL(uri.ToString()));
}
catch
{
Image = null;
}
ShouldDisposeImage = true;
}
}
///
/// Disposes image
///
public void DisposeImage()
{
if (Image != null && ShouldDisposeImage)
Image.Dispose();
Image = null;
}
protected override void ResetImageIndex()
{
imageIndex = -1;
}
///
/// The shape of the image is set using GraphicsPath
///
///
///
///
///
///
public void EstablishImageForm(GraphicsPath path, float drawLeft, float drawTop, float drawWidth, float drawHeight)
{
RectangleF drawRect = new RectangleF(
drawLeft,
drawTop,
drawWidth,
drawHeight);
switch (Shape)
{
case ShapeKind.Rectangle:
path.AddRectangle(drawRect);
break;
case ShapeKind.RoundRectangle:
float min = Math.Min(drawWidth, drawHeight) / 4;
path.AddPath(GetRoundRectPath(drawRect, min), false);
break;
case ShapeKind.Ellipse:
path.AddEllipse(drawLeft, drawTop, drawWidth, drawHeight);
break;
case ShapeKind.Triangle:
PointF[] triPoints =
{
new PointF(drawLeft + drawWidth, drawTop + drawHeight), new PointF(drawLeft, drawTop + drawHeight),
new PointF(drawLeft + drawWidth / 2, drawTop), new PointF(drawLeft + drawWidth, drawTop + drawHeight)
};
path.AddPolygon(triPoints);
break;
case ShapeKind.Diamond:
PointF[] diaPoints =
{
new PointF(drawLeft + drawWidth / 2, drawTop), new PointF(drawLeft + drawWidth, drawTop + drawHeight / 2),
new PointF(drawLeft + drawWidth / 2, drawTop + drawHeight), new PointF(drawLeft, drawTop + drawHeight / 2)
};
path.AddPolygon(diaPoints);
break;
}
}
#endregion
#region Report Engine
///
public override void InitializeComponent()
{
base.InitializeComponent();
ResetImageIndex();
}
///
public override void FinalizeComponent()
{
base.FinalizeComponent();
ResetImageIndex();
}
///
public override void GetData()
{
base.GetData();
if (!String.IsNullOrEmpty(DataColumn))
{
// reset the image
Image = null;
imageData = null;
object data = Report.GetColumnValueNullable(DataColumn);
if (data is byte[])
{
SetImageData((byte[])data);
}
else if (data is Image)
{
Image = data as Image;
}
else if (data is string)
{
ImageLocation = data.ToString();
}
}
}
///
/// Forces loading the image from a data column.
///
///
/// Call this method in the AfterData event handler to force loading an image
/// into the property. Normally, the image is stored internally as byte[] array
/// and never loaded into the Image property, to save the time. The side effect is that you
/// can't analyze the image properties such as width and height. If you need this, call this method
/// before you access the Image property. Note that this will significantly slow down the report.
///
public void ForceLoadImage()
{
if (imageData == null)
return;
byte[] saveImageData = imageData;
// FImageData will be reset after this line, keep it
Image = ImageHelper.Load(imageData);
imageData = saveImageData;
ShouldDisposeImage = true;
}
#endregion
///
/// Initializes a new instance of the class with default settings.
///
public PictureObject()
{
transparentColor = Color.Transparent;
SetFlags(Flags.HasSmartTag, true);
ResetImageIndex();
}
}
}