PictureObject.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.ComponentModel;
  5. using System.Drawing.Drawing2D;
  6. using System.IO;
  7. using System.Drawing.Imaging;
  8. using FastReport.Utils;
  9. using System.Windows.Forms;
  10. using System.Drawing.Design;
  11. namespace FastReport
  12. {
  13. /// <summary>
  14. /// Represents a Picture object that can display pictures.
  15. /// </summary>
  16. /// <remarks>
  17. /// The Picture object can display the following kind of pictures:
  18. /// <list type="bullet">
  19. /// <item>
  20. /// <description>picture that is embedded in the report file. Use the <see cref="Image"/>
  21. /// property to do this;</description>
  22. /// </item>
  23. /// <item>
  24. /// <description>picture that is stored in the database BLOb field. Use the <see cref="DataColumn"/>
  25. /// property to specify the name of data column you want to show;</description>
  26. /// </item>
  27. /// <item>
  28. /// <description>picture that is stored in the local disk file. Use the <see cref="ImageLocation"/>
  29. /// property to specify the name of the file;</description>
  30. /// </item>
  31. /// <item>
  32. /// <description>picture that is stored in the Web. Use the <see cref="ImageLocation"/>
  33. /// property to specify the picture's URL.</description>
  34. /// </item>
  35. /// </list>
  36. /// <para/>Use the <see cref="SizeMode"/> property to specify a size mode. The <see cref="MaxWidth"/>
  37. /// and <see cref="MaxHeight"/> properties can be used to restrict the image size if <b>SizeMode</b>
  38. /// is set to <b>AutoSize</b>.
  39. /// <para/>The <see cref="TransparentColor"/> property can be used to display an image with
  40. /// transparent background. Use the <see cref="Transparency"/> property if you want to display
  41. /// semi-transparent image.
  42. /// </remarks>
  43. public partial class PictureObject : PictureObjectBase
  44. {
  45. #region Fields
  46. private Image image;
  47. private int imageIndex;
  48. private Color transparentColor;
  49. private float transparency;
  50. private bool tile;
  51. private Bitmap transparentImage;
  52. private byte[] imageData;
  53. private bool shouldDisposeImage;
  54. private Bitmap grayscaleBitmap;
  55. private int grayscaleHash;
  56. private ImageFormat imageFormat;
  57. #endregion
  58. #region Properties
  59. /// <summary>
  60. /// Gets or sets the image.
  61. /// </summary>
  62. /// <remarks>
  63. /// By default, image that you assign to this property is never disposed - you should
  64. /// take care about it. If you want to dispose the image when this <b>PictureObject</b> is disposed,
  65. /// set the <see cref="ShouldDisposeImage"/> property to <b>true</b> right after you assign an image:
  66. /// <code>
  67. /// myPictureObject.Image = new Bitmap("file.bmp");
  68. /// myPictureObject.ShouldDisposeImage = true;
  69. /// </code>
  70. /// </remarks>
  71. [Category("Data")]
  72. [Editor("FastReport.TypeEditors.ImageEditor, FastReport", typeof(UITypeEditor))]
  73. public virtual Image Image
  74. {
  75. get { return image; }
  76. set
  77. {
  78. image = value;
  79. imageData = null;
  80. UpdateAutoSize();
  81. UpdateTransparentImage();
  82. ResetImageIndex();
  83. imageFormat = CheckImageFormat();
  84. ShouldDisposeImage = false;
  85. }
  86. }
  87. /// <summary>
  88. /// Gets or sets the expansion of image.
  89. /// </summary>
  90. [Category("Data")]
  91. public virtual ImageFormat ImageFormat
  92. {
  93. get { return imageFormat; }
  94. set
  95. {
  96. if (image == null)
  97. return;
  98. bool wasC = false;
  99. using (MemoryStream stream = new MemoryStream())
  100. {
  101. wasC = ImageHelper.SaveAndConvert(Image, stream, value);
  102. imageData = stream.ToArray();
  103. }
  104. if (!wasC)
  105. return;
  106. ForceLoadImage();
  107. imageFormat = CheckImageFormat();
  108. }
  109. }
  110. /// <summary>
  111. /// Gets or sets a value indicating that the image should be displayed in grayscale mode.
  112. /// </summary>
  113. [DefaultValue(false)]
  114. [Category("Appearance")]
  115. public override bool Grayscale
  116. {
  117. get { return base.Grayscale; }
  118. set
  119. {
  120. base.Grayscale = value;
  121. if (!value && grayscaleBitmap != null)
  122. {
  123. grayscaleBitmap.Dispose();
  124. grayscaleBitmap = null;
  125. }
  126. }
  127. }
  128. /// <summary>
  129. /// Gets or sets a hash of grayscale svg image
  130. /// </summary>
  131. [Browsable(false)]
  132. public int GrayscaleHash
  133. {
  134. get { return grayscaleHash; }
  135. set { grayscaleHash = value; }
  136. }
  137. /// <summary>
  138. /// Gets or sets the color of the image that will be treated as transparent.
  139. /// </summary>
  140. [Category("Appearance")]
  141. [Editor("FastReport.TypeEditors.ColorEditor, FastReport", typeof(UITypeEditor))]
  142. public Color TransparentColor
  143. {
  144. get { return transparentColor; }
  145. set
  146. {
  147. transparentColor = value;
  148. UpdateTransparentImage();
  149. }
  150. }
  151. /// <summary>
  152. /// Gets or sets the transparency of the PictureObject.
  153. /// </summary>
  154. /// <remarks>
  155. /// Valid range of values is 0..1. Default value is 0.
  156. /// </remarks>
  157. [DefaultValue(0f)]
  158. [Category("Appearance")]
  159. public float Transparency
  160. {
  161. get { return transparency; }
  162. set
  163. {
  164. if (value < 0)
  165. value = 0;
  166. if (value > 1)
  167. value = 1;
  168. transparency = value;
  169. UpdateTransparentImage();
  170. }
  171. }
  172. /// <summary>
  173. /// Gets or sets a value indicating that the image should be tiled.
  174. /// </summary>
  175. [DefaultValue(false)]
  176. [Category("Appearance")]
  177. public bool Tile
  178. {
  179. get { return tile; }
  180. set { tile = value; }
  181. }
  182. /// <summary>
  183. /// Gets or sets a value indicating that the image stored in the <see cref="Image"/>
  184. /// property should be disposed when this object is disposed.
  185. /// </summary>
  186. /// <remarks>
  187. /// By default, image assigned to the <see cref="Image"/> property is never disposed - you should
  188. /// take care about it. If you want to dispose the image when this <b>PictureObject</b> is disposed,
  189. /// set this property to <b>true</b> right after you assign an image to the <see cref="Image"/> property.
  190. /// </remarks>
  191. [Browsable(false)]
  192. public bool ShouldDisposeImage
  193. {
  194. get { return shouldDisposeImage; }
  195. set { shouldDisposeImage = value; }
  196. }
  197. /// <summary>
  198. /// Gets or sets a bitmap transparent image
  199. /// </summary>
  200. [Browsable(false)]
  201. public Bitmap TransparentImage
  202. {
  203. get { return transparentImage; }
  204. set { transparentImage = value; }
  205. }
  206. /// <inheritdoc/>
  207. [Browsable(false)]
  208. protected override float ImageWidth
  209. {
  210. get
  211. {
  212. if (Image == null) return 0;
  213. return Image.Width;
  214. }
  215. }
  216. /// <inheritdoc/>
  217. [Browsable(false)]
  218. protected override float ImageHeight
  219. {
  220. get
  221. {
  222. if (Image == null) return 0;
  223. return Image.Height;
  224. }
  225. }
  226. #endregion
  227. #region Private Methods
  228. private ImageFormat CheckImageFormat()
  229. {
  230. if (Image == null || Image.RawFormat == null)
  231. return null;
  232. ImageFormat format = null;
  233. if (ImageFormat.Jpeg.Equals(image.RawFormat))
  234. {
  235. format = ImageFormat.Jpeg;
  236. }
  237. else if (ImageFormat.Gif.Equals(image.RawFormat))
  238. {
  239. format = ImageFormat.Gif;
  240. }
  241. else if (ImageFormat.Png.Equals(image.RawFormat))
  242. {
  243. format = ImageFormat.Png;
  244. }
  245. else if (ImageFormat.Emf.Equals(image.RawFormat))
  246. {
  247. format = ImageFormat.Emf;
  248. }
  249. else if (ImageFormat.Icon.Equals(image.RawFormat))
  250. {
  251. format = ImageFormat.Icon;
  252. }
  253. else if (ImageFormat.Tiff.Equals(image.RawFormat))
  254. {
  255. format = ImageFormat.Tiff;
  256. }
  257. else if (ImageFormat.Bmp.Equals(image.RawFormat) || ImageFormat.MemoryBmp.Equals(image.RawFormat))
  258. {
  259. format = ImageFormat.Bmp;
  260. }
  261. else if (ImageFormat.Wmf.Equals(image.RawFormat))
  262. {
  263. format = ImageFormat.Wmf;
  264. }
  265. if (format != null)
  266. return format;
  267. return ImageFormat.Bmp;
  268. }
  269. private void UpdateTransparentImage()
  270. {
  271. if (transparentImage != null)
  272. transparentImage.Dispose();
  273. transparentImage = null;
  274. if (Image is Bitmap)
  275. {
  276. if (TransparentColor != Color.Transparent)
  277. {
  278. transparentImage = new Bitmap(Image);
  279. transparentImage.MakeTransparent(TransparentColor);
  280. }
  281. else if (Transparency != 0)
  282. {
  283. transparentImage = ImageHelper.GetTransparentBitmap(Image, Transparency);
  284. }
  285. }
  286. }
  287. #endregion
  288. #region Protected Methods
  289. /// <inheritdoc/>
  290. protected override void Dispose(bool disposing)
  291. {
  292. if (disposing)
  293. DisposeImage();
  294. base.Dispose(disposing);
  295. }
  296. #endregion
  297. #region Public Methods
  298. /// <inheritdoc/>
  299. public override void Assign(Base source)
  300. {
  301. base.Assign(source);
  302. PictureObject src = source as PictureObject;
  303. if (src != null)
  304. {
  305. TransparentColor = src.TransparentColor;
  306. Transparency = src.Transparency;
  307. Tile = src.Tile;
  308. Image = src.Image == null ? null : src.Image.Clone() as Image;
  309. if (src.Image == null && src.imageData != null)
  310. imageData = src.imageData;
  311. ShouldDisposeImage = true;
  312. ImageFormat = src.ImageFormat;
  313. }
  314. }
  315. /// <summary>
  316. /// Draws the image.
  317. /// </summary>
  318. /// <param name="e">Paint event args.</param>
  319. public override void DrawImage(FRPaintEventArgs e)
  320. {
  321. IGraphics g = e.Graphics;
  322. if (Image == null)
  323. ForceLoadImage();
  324. if (Image == null)
  325. {
  326. DrawErrorImage(g, e);
  327. return;
  328. }
  329. float drawLeft = (AbsLeft + Padding.Left) * e.ScaleX;
  330. float drawTop = (AbsTop + Padding.Top) * e.ScaleY;
  331. float drawWidth = (Width - Padding.Horizontal) * e.ScaleX;
  332. float drawHeight = (Height - Padding.Vertical) * e.ScaleY;
  333. RectangleF drawRect = new RectangleF(
  334. drawLeft,
  335. drawTop,
  336. drawWidth,
  337. drawHeight);
  338. IGraphicsState state = g.Save();
  339. try
  340. {
  341. //if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one
  342. g.ResetClip();
  343. g.SetClip(drawRect);
  344. Report report = Report;
  345. if (report != null && report.SmoothGraphics)
  346. {
  347. g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  348. g.SmoothingMode = SmoothingMode.AntiAlias;
  349. }
  350. if (!Tile)
  351. DrawImageInternal(e, drawRect);
  352. else
  353. {
  354. float y = drawRect.Top;
  355. float width = Image.Width * e.ScaleX;
  356. float height = Image.Height * e.ScaleY;
  357. while (y < drawRect.Bottom)
  358. {
  359. float x = drawRect.Left;
  360. while (x < drawRect.Right)
  361. {
  362. if (transparentImage != null)
  363. g.DrawImage(transparentImage, x, y, width, height);
  364. else
  365. g.DrawImage(Image, x, y, width, height);
  366. x += width;
  367. }
  368. y += height;
  369. }
  370. }
  371. }
  372. finally
  373. {
  374. g.Restore(state);
  375. }
  376. if (IsPrinting)
  377. {
  378. DisposeImage();
  379. }
  380. }
  381. protected override void DrawImageInternal2(IGraphics graphics, PointF upperLeft, PointF upperRight, PointF lowerLeft)
  382. {
  383. Image image = transparentImage != null ? transparentImage.Clone() as Image : Image.Clone() as Image;
  384. if (image == null)
  385. return;
  386. if (Grayscale)
  387. {
  388. if (grayscaleHash != image.GetHashCode() || grayscaleBitmap == null)
  389. {
  390. if (grayscaleBitmap != null)
  391. grayscaleBitmap.Dispose();
  392. grayscaleBitmap = ImageHelper.GetGrayscaleBitmap(image);
  393. grayscaleHash = image.GetHashCode();
  394. }
  395. image = grayscaleBitmap;
  396. }
  397. //graphics.DrawImage(image, new PointF[] { upperLeft, upperRight, lowerLeft });
  398. DrawImage3Points(graphics, image, upperLeft, upperRight, lowerLeft);
  399. image.Dispose();
  400. }
  401. // This is analogue of graphics.DrawImage(image, PointF[] points) method.
  402. // The original gdi+ method does not work properly in mono on linux/macos.
  403. private void DrawImage3Points(IGraphics g, Image image, PointF p0, PointF p1, PointF p2)
  404. {
  405. // Skip drawing image, when height or width of the image equal zero.
  406. if (image == null || image.Width == 0 || image.Height == 0)
  407. return;
  408. // Skip drawing image, when height or width of the parallelogram for drawing equal zero.
  409. if (p0 == p1 || p0 == p2)
  410. return;
  411. RectangleF rect = new RectangleF(0, 0, image.Width, image.Height);
  412. float m11 = (p1.X - p0.X) / rect.Width;
  413. float m12 = (p1.Y - p0.Y) / rect.Width;
  414. float m21 = (p2.X - p0.X) / rect.Height;
  415. float m22 = (p2.Y - p0.Y) / rect.Height;
  416. g.MultiplyTransform(new System.Drawing.Drawing2D.Matrix(m11, m12, m21, m22, p0.X, p0.Y), MatrixOrder.Prepend);
  417. g.DrawImage(image, rect);
  418. }
  419. /// <summary>
  420. /// Sets image data to FImageData
  421. /// </summary>
  422. /// <param name="data"></param>
  423. public void SetImageData(byte[] data)
  424. {
  425. imageData = data;
  426. // if autosize is on, load the image.
  427. if (SizeMode == PictureBoxSizeMode.AutoSize)
  428. ForceLoadImage();
  429. }
  430. /// <inheritdoc/>
  431. public override void Serialize(FRWriter writer)
  432. {
  433. PictureObject c = writer.DiffObject as PictureObject;
  434. base.Serialize(writer);
  435. #if PRINT_HOUSE
  436. writer.WriteStr("ImageLocation", ImageLocation);
  437. #endif
  438. if (TransparentColor != c.TransparentColor)
  439. writer.WriteValue("TransparentColor", TransparentColor);
  440. if (FloatDiff(Transparency, c.Transparency))
  441. writer.WriteFloat("Transparency", Transparency);
  442. if (Tile != c.Tile)
  443. writer.WriteBool("Tile", Tile);
  444. if (ImageFormat != c.ImageFormat)
  445. writer.WriteValue("ImageFormat", ImageFormat);
  446. // store image data
  447. if (writer.SerializeTo != SerializeTo.SourcePages)
  448. {
  449. if (writer.SerializeTo == SerializeTo.Preview ||
  450. (String.IsNullOrEmpty(ImageLocation) && String.IsNullOrEmpty(DataColumn)))
  451. {
  452. if (writer.BlobStore != null)
  453. {
  454. // check FImageIndex >= writer.BlobStore.Count is needed when we close the designer
  455. // and run it again, the BlobStore is empty, but FImageIndex is pointing to
  456. // previous BlobStore item and is not -1.
  457. if (imageIndex == -1 || imageIndex >= writer.BlobStore.Count)
  458. {
  459. byte[] bytes = imageData;
  460. if (bytes == null)
  461. {
  462. using (MemoryStream stream = new MemoryStream())
  463. {
  464. ImageHelper.Save(Image, stream, imageFormat);
  465. bytes = stream.ToArray();
  466. }
  467. }
  468. if (bytes != null)
  469. {
  470. string imgHash = BitConverter.ToString(new Murmur3().ComputeHash(bytes));
  471. imageIndex = writer.BlobStore.AddOrUpdate(bytes, imgHash);
  472. }
  473. }
  474. }
  475. else
  476. {
  477. if (Image == null && imageData != null)
  478. writer.WriteStr("Image", Convert.ToBase64String(imageData));
  479. else if (!writer.AreEqual(Image, c.Image))
  480. writer.WriteValue("Image", Image);
  481. }
  482. if (writer.BlobStore != null || writer.SerializeTo == SerializeTo.Undo)
  483. writer.WriteInt("ImageIndex", imageIndex);
  484. }
  485. }
  486. }
  487. /// <inheritdoc/>
  488. public override void Deserialize(FRReader reader)
  489. {
  490. base.Deserialize(reader);
  491. if (reader.HasProperty("ImageIndex"))
  492. {
  493. imageIndex = reader.ReadInt("ImageIndex");
  494. if (reader.BlobStore != null && imageIndex != -1)
  495. {
  496. //int saveIndex = FImageIndex;
  497. //Image = ImageHelper.Load(reader.BlobStore.Get(FImageIndex));
  498. //FImageIndex = saveIndex;
  499. SetImageData(reader.BlobStore.Get(imageIndex));
  500. }
  501. }
  502. }
  503. //static int number = 0;
  504. /// <summary>
  505. /// Loads image
  506. /// </summary>
  507. public override void LoadImage()
  508. {
  509. if (!String.IsNullOrEmpty(ImageLocation))
  510. {
  511. //
  512. try
  513. {
  514. Uri uri = CalculateUri();
  515. if (uri.IsFile)
  516. SetImageData(ImageHelper.Load(uri.LocalPath));
  517. else
  518. SetImageData(ImageHelper.LoadURL(uri.ToString()));
  519. }
  520. catch
  521. {
  522. Image = null;
  523. }
  524. ShouldDisposeImage = true;
  525. }
  526. }
  527. /// <summary>
  528. /// Disposes image
  529. /// </summary>
  530. public void DisposeImage()
  531. {
  532. if (Image != null && ShouldDisposeImage)
  533. Image.Dispose();
  534. Image = null;
  535. }
  536. protected override void ResetImageIndex()
  537. {
  538. imageIndex = -1;
  539. }
  540. #endregion
  541. #region Report Engine
  542. /// <inheritdoc/>
  543. public override void InitializeComponent()
  544. {
  545. base.InitializeComponent();
  546. ResetImageIndex();
  547. }
  548. /// <inheritdoc/>
  549. public override void FinalizeComponent()
  550. {
  551. base.FinalizeComponent();
  552. ResetImageIndex();
  553. }
  554. /// <inheritdoc/>
  555. public override void GetData()
  556. {
  557. base.GetData();
  558. if (!String.IsNullOrEmpty(DataColumn))
  559. {
  560. // reset the image
  561. Image = null;
  562. imageData = null;
  563. object data = Report.GetColumnValueNullable(DataColumn);
  564. if (data is byte[])
  565. {
  566. SetImageData((byte[])data);
  567. }
  568. else if (data is Image)
  569. {
  570. Image = data as Image;
  571. }
  572. else if (data is string)
  573. {
  574. ImageLocation = data.ToString();
  575. }
  576. }
  577. }
  578. /// <summary>
  579. /// Forces loading the image from a data column.
  580. /// </summary>
  581. /// <remarks>
  582. /// Call this method in the <b>AfterData</b> event handler to force loading an image
  583. /// into the <see cref="Image"/> property. Normally, the image is stored internally as byte[] array
  584. /// and never loaded into the <b>Image</b> property, to save the time. The side effect is that you
  585. /// can't analyze the image properties such as width and height. If you need this, call this method
  586. /// before you access the <b>Image</b> property. Note that this will significantly slow down the report.
  587. /// </remarks>
  588. public void ForceLoadImage()
  589. {
  590. if (imageData == null)
  591. return;
  592. byte[] saveImageData = imageData;
  593. // FImageData will be reset after this line, keep it
  594. Image = ImageHelper.Load(imageData);
  595. imageData = saveImageData;
  596. ShouldDisposeImage = true;
  597. }
  598. #endregion
  599. /// <summary>
  600. /// Initializes a new instance of the <see cref="PictureObject"/> class with default settings.
  601. /// </summary>
  602. public PictureObject()
  603. {
  604. transparentColor = Color.Transparent;
  605. SetFlags(Flags.HasSmartTag, true);
  606. ResetImageIndex();
  607. }
  608. }
  609. }