Surface.cs 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507
  1. using PaintDotNet.SystemLayer;
  2. using System;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Drawing.Imaging;
  6. namespace PaintDotNet
  7. {
  8. /// <summary>
  9. /// This is our Surface type. We allocate our own blocks of memory for this,
  10. /// and provide ways to create a GDI+ Bitmap object that aliases our surface.
  11. /// That way we can do everything fast, in memory and have complete control,
  12. /// and still have the ability to use GDI+ for drawing and rendering where
  13. /// appropriate.
  14. /// </summary>
  15. [Serializable]
  16. public sealed class Surface : IDisposable, ICloneable
  17. {
  18. /// <summary>
  19. /// 持有的mat
  20. /// </summary>
  21. public OpenCvSharp.Mat mat;
  22. /// <summary>
  23. /// 图像内存块
  24. /// </summary>
  25. public MemoryBlock scan0;
  26. /// <summary>
  27. /// 宽
  28. /// </summary>
  29. private int width;
  30. /// <summary>
  31. /// 高
  32. /// </summary>
  33. private int height;
  34. /// <summary>
  35. /// 扫描宽度
  36. /// </summary>
  37. private int stride;
  38. /// <summary>
  39. /// 释放标记
  40. /// </summary>
  41. private bool disposed = false;
  42. /// <summary>
  43. /// 存储打开时图片的格式
  44. /// </summary>
  45. private PixelFormat pixelFormat = PixelFormat.Format32bppArgb;
  46. /// <summary>
  47. /// 缩略图
  48. /// </summary>
  49. private Bitmap thumbnail;
  50. /// <summary>
  51. /// 备份的mat
  52. /// </summary>
  53. private OpenCvSharp.Mat backUpMat;
  54. public OpenCvSharp.Mat BackUpMat
  55. {
  56. get
  57. {
  58. if (this.backUpMat == null)
  59. this.backUpMat = this.mat.Clone();
  60. return this.backUpMat.Clone();
  61. }
  62. set
  63. {
  64. this.backUpMat = value.Clone();
  65. }
  66. }
  67. public Bitmap Thumbnail
  68. {
  69. get
  70. {
  71. if (this.thumbnail == null)
  72. CreateThumbnail();
  73. return this.thumbnail;
  74. }
  75. }
  76. /// <summary>
  77. /// 创建缩略图
  78. /// </summary>
  79. public void CreateThumbnail()
  80. {
  81. //TODO 回头去掉
  82. if (this.mat == null)
  83. this.mat = PaintDotNet.Camera.Tools.ToMat(CreateAliasedBitmap());
  84. //Bitmap origin = CreateAliasedBitmap();
  85. Bitmap bitmap = null;
  86. if (this.mat.Width > this.mat.Height)
  87. {
  88. bitmap = MakeThumbnail(this.mat, 90, 90, "W");
  89. }
  90. else if (this.mat.Height > this.mat.Width)
  91. {
  92. bitmap = MakeThumbnail(this.mat, 90, 90, "H");
  93. }
  94. else
  95. {
  96. bitmap = MakeThumbnail(this.mat, 90, 90, "W");
  97. }
  98. this.thumbnail = bitmap;
  99. }
  100. public PixelFormat PixelFormat
  101. {
  102. get
  103. {
  104. return this.pixelFormat;
  105. }
  106. set
  107. {
  108. this.pixelFormat = value;
  109. }
  110. }
  111. public bool IsDisposed
  112. {
  113. get
  114. {
  115. return this.disposed;
  116. }
  117. }
  118. /// <summary>
  119. /// Gets a MemoryBlock which is the buffer holding the pixels associated
  120. /// with this Surface.
  121. /// </summary>
  122. public MemoryBlock Scan0
  123. {
  124. get
  125. {
  126. if (this.disposed)
  127. {
  128. throw new ObjectDisposedException("Surface");
  129. }
  130. return this.scan0;
  131. }
  132. }
  133. /// <summary>
  134. /// Gets the width, in pixels, of this Surface.
  135. /// </summary>
  136. /// <remarks>
  137. /// This property will never throw an ObjectDisposedException.
  138. /// </remarks>
  139. public int Width
  140. {
  141. get
  142. {
  143. return this.width;
  144. }
  145. }
  146. /// <summary>
  147. /// Gets the height, in pixels, of this Surface.
  148. /// </summary>
  149. /// <remarks>
  150. /// This property will never throw an ObjectDisposedException.
  151. /// </remarks>
  152. public int Height
  153. {
  154. get
  155. {
  156. return this.height;
  157. }
  158. }
  159. /// <summary>
  160. /// Gets the stride, in bytes, for this Surface.
  161. /// </summary>
  162. /// <remarks>
  163. /// Stride is defined as the number of bytes between the beginning of a row and
  164. /// the beginning of the next row. Thus, in loose C notation: stride = (byte *)&this[0, 1] - (byte *)&this[0, 0].
  165. /// Stride will always be equal to <b>or greater than</b> Width * ColorBgra.SizeOf.
  166. /// This property will never throw an ObjectDisposedException.
  167. /// </remarks>
  168. public int Stride
  169. {
  170. get
  171. {
  172. return this.stride;
  173. }
  174. set
  175. {
  176. this.stride = value;
  177. }
  178. }
  179. /// <summary>
  180. /// Gets the size, in pixels, of this Surface.
  181. /// </summary>
  182. /// <remarks>
  183. /// This is a convenience function that creates a new Size instance based
  184. /// on the values of the Width and Height properties.
  185. /// This property will never throw an ObjectDisposedException.
  186. /// </remarks>
  187. public Size Size
  188. {
  189. get
  190. {
  191. return new Size(this.width, this.height);
  192. }
  193. }
  194. /// <summary>
  195. /// Gets the bounds of this Surface, in pixels.
  196. /// </summary>
  197. /// <remarks>
  198. /// This is a convenience function that returns Rectangle(0, 0, Width, Height).
  199. /// This property will never throw an ObjectDisposedException.
  200. /// </remarks>
  201. public Rectangle Bounds
  202. {
  203. get
  204. {
  205. return new Rectangle(0, 0, width, height);
  206. }
  207. }
  208. /// <summary>
  209. /// Creates a new instance of the Surface class.
  210. /// </summary>
  211. /// <param name="size">The size, in pixels, of the new Surface.</param>
  212. public Surface(Size size)
  213. : this(size.Width, size.Height)
  214. {
  215. }
  216. public Surface(Size size, PixelFormat pixelFormat)
  217. : this(size.Width, size.Height, pixelFormat)
  218. {
  219. }
  220. /// <summary>
  221. /// Creates a new instance of the Surface class.
  222. /// </summary>
  223. /// <param name="width">The width, in pixels, of the new Surface.</param>
  224. /// <param name="height">The height, in pixels, of the new Surface.</param>
  225. public Surface(int width, int height)
  226. {
  227. int stride = 0;
  228. long bytes;
  229. try
  230. {
  231. stride = checked(width * ColorBgra.SizeOf);
  232. bytes = (long)height * (long)stride;
  233. }
  234. catch (OverflowException ex)
  235. {
  236. throw new OutOfMemoryException("Dimensions are too large - not enough memory, width=" + width.ToString() + ", height=" + height.ToString(), ex);
  237. }
  238. MemoryBlock scan0 = new MemoryBlock(width, height);
  239. Create(width, height, stride, scan0);
  240. }
  241. public Surface(int width, int height, PixelFormat pixelFormat)
  242. {
  243. int ch;
  244. PixelFormat = pixelFormat;
  245. try
  246. {
  247. if (pixelFormat == PixelFormat.Format24bppRgb)
  248. ch = 3;
  249. else if (pixelFormat == PixelFormat.Format8bppIndexed)
  250. ch = 1;
  251. else
  252. ch = 4;
  253. }
  254. catch (OverflowException ex)
  255. {
  256. throw new OutOfMemoryException("Dimensions are too large - not enough memory, width=" + width.ToString() + ", height=" + height.ToString(), ex);
  257. }
  258. MemoryBlock scan0 = new MemoryBlock(width, height, ch);
  259. Create(width, height, width * ch, scan0);
  260. }
  261. /// <summary>
  262. /// Creates a new instance of the Surface class that reuses a block of memory that was previously allocated.
  263. /// </summary>
  264. /// <param name="width">The width, in pixels, for the Surface.</param>
  265. /// <param name="height">The height, in pixels, for the Surface.</param>
  266. /// <param name="stride">The stride, in bytes, for the Surface.</param>
  267. /// <param name="scan0">The MemoryBlock to use. The beginning of this buffer defines the upper left (0, 0) pixel of the Surface.</param>
  268. private Surface(int width, int height, int stride, MemoryBlock scan0)
  269. {
  270. Create(width, height, stride, scan0);
  271. }
  272. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "width")]
  273. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "height")]
  274. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "stride")]
  275. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "scan0")]
  276. private void Create(int width, int height, int stride, MemoryBlock scan0)
  277. {
  278. this.width = width;
  279. this.height = height;
  280. this.stride = stride;
  281. this.scan0 = scan0;
  282. }
  283. ~Surface()
  284. {
  285. Dispose(false);
  286. }
  287. /// <summary>
  288. /// Creates a Surface that aliases a portion of this Surface.
  289. /// </summary>
  290. /// <param name="bounds">The portion of this Surface that will be aliased.</param>
  291. /// <remarks>The upper left corner of the new Surface will correspond to the
  292. /// upper left corner of this rectangle in the original Surface.</remarks>
  293. /// <returns>A Surface that aliases the requested portion of this Surface.</returns>
  294. public Surface CreateWindow(Rectangle bounds)
  295. {
  296. return CreateWindow(bounds.X, bounds.Y, bounds.Width, bounds.Height);
  297. }
  298. public Surface CreateWindow(int x, int y, int windowWidth, int windowHeight)
  299. {
  300. if (disposed)
  301. {
  302. throw new ObjectDisposedException("Surface");
  303. }
  304. if (windowHeight == 0)
  305. {
  306. throw new ArgumentOutOfRangeException("windowHeight", "must be greater than zero");
  307. }
  308. Rectangle original = this.Bounds;
  309. Rectangle sub = new Rectangle(x, y, windowWidth, windowHeight);
  310. Rectangle clipped = Rectangle.Intersect(original, sub);
  311. if (clipped != sub)
  312. {
  313. throw new ArgumentOutOfRangeException("bounds", new Rectangle(x, y, windowWidth, windowHeight),
  314. "bounds parameters must be a subset of this Surface's bounds");
  315. }
  316. long offset = ((long)stride * (long)y) + ((long)ColorBgra.SizeOf * (long)x);
  317. long length = ((windowHeight - 1) * (long)stride) + (long)windowWidth * (long)ColorBgra.SizeOf;
  318. MemoryBlock block = new MemoryBlock(this.scan0, offset, length);
  319. return new Surface(windowWidth, windowHeight, this.stride, block);
  320. }
  321. /// <summary>
  322. /// Gets the offset, in bytes, of the requested row from the start of the surface.
  323. /// </summary>
  324. /// <param name="y">The row.</param>
  325. /// <returns>The number of bytes between (0,0) and (0,y).</returns>
  326. public long GetRowByteOffset(int y)
  327. {
  328. if (y < 0 || y >= height)
  329. {
  330. throw new ArgumentOutOfRangeException("y", "Out of bounds: y=" + y.ToString());
  331. }
  332. return (long)y * (long)stride;
  333. }
  334. /// <summary>
  335. /// Gets the offset, in bytes, of the requested row from the start of the surface.
  336. /// </summary>
  337. /// <param name="y">The row.</param>
  338. /// <returns>The number of bytes between (0,0) and (0,y)</returns>
  339. /// <remarks>
  340. /// This method does not do any bounds checking and is potentially unsafe to use,
  341. /// but faster than GetRowByteOffset().
  342. /// </remarks>
  343. public unsafe long GetRowByteOffsetUnchecked(int y)
  344. {
  345. return (long)y * (long)stride;
  346. }
  347. /// <summary>
  348. /// Gets a pointer to the beginning of the requested row in the surface.
  349. /// </summary>
  350. /// <param name="y">The row</param>
  351. /// <returns>A pointer that references (0,y) in this surface.</returns>
  352. /// <remarks>Since this returns a pointer, it is potentially unsafe to use.</remarks>
  353. public unsafe ColorBgra* GetRowAddress(int y)
  354. {
  355. return (ColorBgra*)(((byte*)scan0.VoidStar) + GetRowByteOffset(y));
  356. }
  357. /// <summary>
  358. /// Gets a pointer to the beginning of the requested row in the surface.
  359. /// </summary>
  360. /// <param name="y">The row</param>
  361. /// <returns>A pointer that references (0,y) in this surface.</returns>
  362. /// <remarks>
  363. /// This method does not do any bounds checking and is potentially unsafe to use,
  364. /// but faster than GetRowAddress().
  365. /// </remarks>
  366. public unsafe ColorBgra* GetRowAddressUnchecked(int y)
  367. {
  368. return (ColorBgra*)(((byte*)scan0.VoidStar) + GetRowByteOffsetUnchecked(y));
  369. }
  370. /// <summary>
  371. /// Gets the number of bytes from the beginning of a row to the requested column.
  372. /// </summary>
  373. /// <param name="x">The column.</param>
  374. /// <returns>
  375. /// The number of bytes between (0,n) and (x,n) where n is in the range [0, Height).
  376. /// </returns>
  377. /// <remarks>
  378. /// This method does not do any bounds checking and is potentially unsafe to use,
  379. /// but faster than GetColumnByteOffset().
  380. /// </remarks>
  381. public long GetColumnByteOffsetUnchecked(int x)
  382. {
  383. return (long)x * (long)ColorBgra.SizeOf;
  384. }
  385. /// <summary>
  386. /// Gets the number of bytes from the beginning of the surface's buffer to
  387. /// the requested point.
  388. /// </summary>
  389. /// <param name="x">The x offset.</param>
  390. /// <param name="y">The y offset.</param>
  391. /// <returns>
  392. /// The number of bytes between (0,0) and (x,y).
  393. /// </returns>
  394. /// <remarks>
  395. /// This method does not do any bounds checking and is potentially unsafe to use,
  396. /// but faster than GetPointByteOffset().
  397. /// </remarks>
  398. public long GetPointByteOffsetUnchecked(int x, int y)
  399. {
  400. return GetRowByteOffsetUnchecked(y) + GetColumnByteOffsetUnchecked(x);
  401. }
  402. /// <summary>
  403. /// Gets the color at a specified point in the surface.
  404. /// </summary>
  405. /// <param name="x">The x offset.</param>
  406. /// <param name="y">The y offset.</param>
  407. /// <returns>The color at the requested location.</returns>
  408. public ColorBgra GetPoint(int x, int y)
  409. {
  410. return this[x, y];
  411. }
  412. /// <summary>
  413. /// Gets the address in memory of the requested point.
  414. /// </summary>
  415. /// <param name="x">The x offset.</param>
  416. /// <param name="y">The y offset.</param>
  417. /// <returns>A pointer to the requested point in the surface.</returns>
  418. /// <remarks>Since this method returns a pointer, it is potentially unsafe to use.</remarks>
  419. public unsafe ColorBgra* GetPointAddress(int x, int y)
  420. {
  421. if (x < 0 || x >= Width)
  422. {
  423. throw new ArgumentOutOfRangeException("x", "Out of bounds: x=" + x.ToString());
  424. }
  425. return GetRowAddress(y) + x;
  426. }
  427. /// <summary>
  428. /// Gets the address in memory of the requested point.
  429. /// </summary>
  430. /// <param name="pt">The point to retrieve.</param>
  431. /// <returns>A pointer to the requested point in the surface.</returns>
  432. /// <remarks>Since this method returns a pointer, it is potentially unsafe to use.</remarks>
  433. public unsafe ColorBgra* GetPointAddress(Point pt)
  434. {
  435. return GetPointAddress(pt.X, pt.Y);
  436. }
  437. /// <summary>
  438. /// Gets the address in memory of the requested point.
  439. /// </summary>
  440. /// <param name="x">The x offset.</param>
  441. /// <param name="y">The y offset.</param>
  442. /// <returns>A pointer to the requested point in the surface.</returns>
  443. /// <remarks>
  444. /// This method does not do any bounds checking and is potentially unsafe to use,
  445. /// but faster than GetPointAddress().
  446. /// </remarks>
  447. public unsafe ColorBgra* GetPointAddressUnchecked(int x, int y)
  448. {
  449. return unchecked(x + (ColorBgra*)(((byte*)scan0.VoidStar) + (y * stride)));
  450. }
  451. /// <summary>
  452. /// Gets a MemoryBlock that references the row requested.
  453. /// </summary>
  454. /// <param name="y">The row.</param>
  455. /// <returns>A MemoryBlock that gives access to the bytes in the specified row.</returns>
  456. /// <remarks>This method is the safest to use for direct memory access to a row's pixel data.</remarks>
  457. public MemoryBlock GetRow(int y)
  458. {
  459. return new MemoryBlock(scan0, GetRowByteOffset(y), (long)width * (long)ColorBgra.SizeOf);
  460. }
  461. /// <summary>
  462. /// Determines if the requested pixel coordinate is within bounds.
  463. /// </summary>
  464. /// <param name="x">The x coordinate.</param>
  465. /// <param name="y">The y coordinate.</param>
  466. /// <returns>true if (x,y) is in bounds, false if it's not.</returns>
  467. public bool IsVisible(int x, int y)
  468. {
  469. return x >= 0 && x < width && y >= 0 && y < height;
  470. }
  471. /// <summary>
  472. /// Determines if the requested row offset is within bounds.
  473. /// </summary>
  474. /// <param name="y">The row.</param>
  475. /// <returns>true if y &gt;= 0 and y &lt; height, otherwise false</returns>
  476. public bool IsRowVisible(int y)
  477. {
  478. return y >= 0 && y < Height;
  479. }
  480. /// <summary>
  481. /// Determines if the requested column offset is within bounds.
  482. /// </summary>
  483. /// <param name="x">The column.</param>
  484. /// <returns>true if x &gt;= 0 and x &lt; width, otherwise false.</returns>
  485. public bool IsColumnVisible(int x)
  486. {
  487. return x >= 0 && x < Width;
  488. }
  489. /// <summary>
  490. /// Gets or sets the pixel value at the requested offset.
  491. /// </summary>
  492. /// <remarks>
  493. /// This property is implemented with correctness and error checking in mind. If performance
  494. /// is a concern, do not use it.
  495. /// </remarks>
  496. public ColorBgra this[int x, int y]
  497. {
  498. get
  499. {
  500. if (disposed)
  501. {
  502. throw new ObjectDisposedException("Surface");
  503. }
  504. if (x < 0 || y < 0 || x >= this.width || y >= this.height)
  505. {
  506. throw new ArgumentOutOfRangeException("(x,y)", new Point(x, y), "Coordinates out of range, max=" + new Size(width - 1, height - 1).ToString());
  507. }
  508. unsafe
  509. {
  510. return *GetPointAddressUnchecked(x, y);
  511. }
  512. }
  513. set
  514. {
  515. if (disposed)
  516. {
  517. throw new ObjectDisposedException("Surface");
  518. }
  519. if (x < 0 || y < 0 || x >= this.width || y >= this.height)
  520. {
  521. throw new ArgumentOutOfRangeException("(x,y)", new Point(x, y), "Coordinates out of range, max=" + new Size(width - 1, height - 1).ToString());
  522. }
  523. unsafe
  524. {
  525. *GetPointAddressUnchecked(x, y) = value;
  526. }
  527. }
  528. }
  529. /// <summary>
  530. /// Helper function. Same as calling CreateAliasedBounds(Bounds).
  531. /// </summary>
  532. /// <returns>A GDI+ Bitmap that aliases the entire Surface.</returns>
  533. public Bitmap CreateAliasedBitmap(bool alpha)
  534. {
  535. return CreateAliasedBitmap(this.Bounds, alpha);
  536. }
  537. public OpenCvSharp.Mat CreatedAliasedMat()
  538. {
  539. if (mat == null)//<-CreateAliasedBitmap
  540. return Camera.Tools.ToMat(this.CreateAliasedBitmap());// OpenCvSharp.Extensions.BitmapConverter.ToMat(this.CreateAliasedBitmap());
  541. return mat/*.Clone()*/;//待测试
  542. }
  543. /// <summary>
  544. /// Helper function. Same as calling CreateAliasedBounds(Bounds).
  545. /// </summary>
  546. /// <returns>A GDI+ Bitmap that aliases the entire Surface.</returns>
  547. public Bitmap CreateAliasedBitmap()
  548. {
  549. return CreateAliasedBitmap(this.Bounds);
  550. }
  551. /// <summary>
  552. /// Helper function. Same as calling CreateAliasedBounds(bounds, true).
  553. /// </summary>
  554. /// <returns>A GDI+ Bitmap that aliases the entire Surface.</returns>
  555. public Bitmap CreateAliasedBitmap(Rectangle bounds)
  556. {
  557. return CreateAliasedBitmap(bounds, true);
  558. }
  559. /// <summary>
  560. /// Creates a GDI+ Bitmap object that aliases the same memory that this Surface does.
  561. /// Then you can use GDI+ to draw on to this surface.
  562. /// Note: Since the Bitmap does not hold a reference to this Surface object, nor to
  563. /// the MemoryBlock that it contains, you must hold a reference to the Surface object
  564. /// for as long as you wish to use the aliased Bitmap. Otherwise the memory may be
  565. /// freed and the Bitmap will look corrupt or cause other errors. You may use the
  566. /// RenderArgs class to help manage this lifetime instead.
  567. /// </summary>
  568. /// <param name="bounds">The rectangle of interest within this Surface that you wish to alias.</param>
  569. /// <param name="alpha">If true, the returned bitmap will use PixelFormat.Format32bppArgb.
  570. /// If false, the returned bitmap will use PixelFormat.Format32bppRgb.</param>
  571. /// <returns>A GDI+ Bitmap that aliases the requested portion of the Surface.</returns>
  572. /// <exception cref="ArgumentOutOfRangeException"><b>bounds</b> was not entirely within the boundaries of the Surface</exception>
  573. /// <exception cref="ObjectDisposedException">This Surface instance is already disposed.</exception>
  574. public Bitmap CreateAliasedBitmap(Rectangle bounds, bool alpha = true)
  575. {
  576. if (disposed)
  577. {
  578. throw new ObjectDisposedException("Surface");
  579. }
  580. if (bounds.IsEmpty)
  581. {
  582. throw new ArgumentOutOfRangeException();
  583. }
  584. Rectangle clipped = Rectangle.Intersect(this.Bounds, bounds);
  585. if (clipped != bounds)
  586. {
  587. throw new ArgumentOutOfRangeException();
  588. }
  589. unsafe
  590. {
  591. return new Bitmap(bounds.Width, bounds.Height, stride, alpha ? PixelFormat.Format32bppArgb : this.PixelFormat,
  592. new IntPtr((void*)((byte*)scan0.VoidStar + GetPointByteOffsetUnchecked(bounds.X, bounds.Y))));
  593. }
  594. }
  595. /// <summary>
  596. /// Creates a new Surface and copies the pixels from a Bitmap to it.
  597. /// </summary>
  598. /// <param name="bitmap">The Bitmap to duplicate.</param>
  599. /// <returns>A new Surface that is the same size as the given Bitmap and that has the same pixel values.</returns>
  600. public static Surface CopyFromBitmap(Bitmap bitmap)
  601. {
  602. Surface surface = new Surface(bitmap.Width, bitmap.Height);
  603. BitmapData bd = bitmap.LockBits(surface.Bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
  604. unsafe
  605. {
  606. for (int y = 0; y < bd.Height; ++y)
  607. {
  608. Memory.Copy((void*)surface.GetRowAddress(y),
  609. (byte*)bd.Scan0.ToPointer() + (y * bd.Stride), (ulong)bd.Width * ColorBgra.SizeOf);
  610. }
  611. }
  612. bitmap.UnlockBits(bd);
  613. return surface;
  614. }
  615. /// <summary>
  616. /// Copies the contents of the given surface to the upper left corner of this surface.
  617. /// </summary>
  618. /// <param name="source">The surface to copy pixels from.</param>
  619. /// <remarks>
  620. /// The source surface does not need to have the same dimensions as this surface. Clipping
  621. /// will be handled automatically. No resizing will be done.
  622. /// </remarks>
  623. public void CopySurface(Surface source)
  624. {
  625. if (disposed)
  626. {
  627. throw new ObjectDisposedException("Surface");
  628. }
  629. if (this.stride == source.stride &&
  630. (this.width * ColorBgra.SizeOf) == this.stride &&
  631. this.width == source.width &&
  632. this.height == source.height)
  633. {
  634. unsafe
  635. {
  636. Memory.Copy(this.scan0.VoidStar,
  637. source.scan0.VoidStar,
  638. ((ulong)(height - 1) * (ulong)stride) + ((ulong)width * (ulong)ColorBgra.SizeOf));
  639. }
  640. }
  641. else
  642. {
  643. int copyWidth = Math.Min(width, source.width);
  644. int copyHeight = Math.Min(height, source.height);
  645. unsafe
  646. {
  647. for (int y = 0; y < copyHeight; ++y)
  648. {
  649. Memory.Copy(GetRowAddressUnchecked(y), source.GetRowAddressUnchecked(y), (ulong)copyWidth * (ulong)ColorBgra.SizeOf);
  650. }
  651. }
  652. }
  653. }
  654. object ICloneable.Clone()
  655. {
  656. return Clone();
  657. }
  658. /// <summary>
  659. /// Creates a new surface with the same dimensions and pixel values as this one.
  660. /// </summary>
  661. /// <returns>A new surface that is a clone of the current one.</returns>
  662. public Surface Clone()
  663. {
  664. if (disposed)
  665. {
  666. throw new ObjectDisposedException("Surface");
  667. }
  668. Surface ret = new Surface(this.Size);
  669. ret.CopySurface(this);
  670. return ret;
  671. }
  672. /// <summary>
  673. /// Clears the surface to all-white (BGRA = [255,255,255,255]).
  674. /// </summary>
  675. public void Clear()
  676. {
  677. Clear(ColorBgra.FromBgra(255, 255, 255, 255));
  678. }
  679. /// <summary>
  680. /// Clears the surface to the given color value.
  681. /// </summary>
  682. /// <param name="color">The color value to fill the surface with.</param>
  683. public void Clear(ColorBgra color)
  684. {
  685. //new UnaryPixelOps.Constant(color).Apply(this, this.Bounds);
  686. }
  687. /// <summary>
  688. /// Fits the source surface to this surface using super sampling. If the source surface is less wide
  689. /// or less tall than this surface (i.e. magnification), bicubic resampling is used instead. If either
  690. /// the source or destination has a dimension that is only 1 pixel, nearest neighbor is used.
  691. /// </summary>
  692. /// <param name="source">The surface to read pixels from.</param>
  693. /// <param name="dstRoi">The rectangle to clip rendering to.</param>
  694. /// <remarks>This method was implemented with correctness, not performance, in mind.</remarks>
  695. public void SuperSamplingFitSurface(Surface source, Rectangle dstRoi)
  696. {
  697. if (source.Width == Width && source.Height == Height)
  698. {
  699. CopySurface(source);
  700. }
  701. else if (source.Width <= Width || source.Height <= Height)
  702. {
  703. if (source.width < 2 || source.height < 2 || this.width < 2 || this.height < 2)
  704. {
  705. this.NearestNeighborFitSurface(source, dstRoi);
  706. }
  707. else
  708. {
  709. this.BicubicFitSurface(source, dstRoi);
  710. }
  711. }
  712. else unsafe
  713. {
  714. Rectangle dstRoi2 = Rectangle.Intersect(dstRoi, this.Bounds);
  715. for (int dstY = dstRoi2.Top; dstY < dstRoi2.Bottom; ++dstY)
  716. {
  717. double srcTop = (double)(dstY * source.height) / (double)height;
  718. double srcTopFloor = Math.Floor(srcTop);
  719. double srcTopWeight = 1 - (srcTop - srcTopFloor);
  720. int srcTopInt = (int)srcTopFloor;
  721. double srcBottom = (double)((dstY + 1) * source.height) / (double)height;
  722. double srcBottomFloor = Math.Floor(srcBottom - 0.00001);
  723. double srcBottomWeight = srcBottom - srcBottomFloor;
  724. int srcBottomInt = (int)srcBottomFloor;
  725. ColorBgra* dstPtr = this.GetPointAddressUnchecked(dstRoi2.Left, dstY);
  726. for (int dstX = dstRoi2.Left; dstX < dstRoi2.Right; ++dstX)
  727. {
  728. double srcLeft = (double)(dstX * source.width) / (double)width;
  729. double srcLeftFloor = Math.Floor(srcLeft);
  730. double srcLeftWeight = 1 - (srcLeft - srcLeftFloor);
  731. int srcLeftInt = (int)srcLeftFloor;
  732. double srcRight = (double)((dstX + 1) * source.width) / (double)width;
  733. double srcRightFloor = Math.Floor(srcRight - 0.00001);
  734. double srcRightWeight = srcRight - srcRightFloor;
  735. int srcRightInt = (int)srcRightFloor;
  736. double blueSum = 0;
  737. double greenSum = 0;
  738. double redSum = 0;
  739. double alphaSum = 0;
  740. // left fractional edge
  741. ColorBgra* srcLeftPtr = source.GetPointAddressUnchecked(srcLeftInt, srcTopInt + 1);
  742. for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY)
  743. {
  744. double a = srcLeftPtr->A;
  745. blueSum += srcLeftPtr->B * srcLeftWeight * a;
  746. greenSum += srcLeftPtr->G * srcLeftWeight * a;
  747. redSum += srcLeftPtr->R * srcLeftWeight * a;
  748. alphaSum += srcLeftPtr->A * srcLeftWeight;
  749. srcLeftPtr = (ColorBgra*)((byte*)srcLeftPtr + source.stride);
  750. }
  751. // right fractional edge
  752. ColorBgra* srcRightPtr = source.GetPointAddressUnchecked(srcRightInt, srcTopInt + 1);
  753. for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY)
  754. {
  755. double a = srcRightPtr->A;
  756. blueSum += srcRightPtr->B * srcRightWeight * a;
  757. greenSum += srcRightPtr->G * srcRightWeight * a;
  758. redSum += srcRightPtr->R * srcRightWeight * a;
  759. alphaSum += srcRightPtr->A * srcRightWeight;
  760. srcRightPtr = (ColorBgra*)((byte*)srcRightPtr + source.stride);
  761. }
  762. // top fractional edge
  763. ColorBgra* srcTopPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcTopInt);
  764. for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX)
  765. {
  766. double a = srcTopPtr->A;
  767. blueSum += srcTopPtr->B * srcTopWeight * a;
  768. greenSum += srcTopPtr->G * srcTopWeight * a;
  769. redSum += srcTopPtr->R * srcTopWeight * a;
  770. alphaSum += srcTopPtr->A * srcTopWeight;
  771. ++srcTopPtr;
  772. }
  773. // bottom fractional edge
  774. ColorBgra* srcBottomPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcBottomInt);
  775. for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX)
  776. {
  777. double a = srcBottomPtr->A;
  778. blueSum += srcBottomPtr->B * srcBottomWeight * a;
  779. greenSum += srcBottomPtr->G * srcBottomWeight * a;
  780. redSum += srcBottomPtr->R * srcBottomWeight * a;
  781. alphaSum += srcBottomPtr->A * srcBottomWeight;
  782. ++srcBottomPtr;
  783. }
  784. // center area
  785. for (int srcY = srcTopInt + 1; srcY < srcBottomInt; ++srcY)
  786. {
  787. ColorBgra* srcPtr = source.GetPointAddressUnchecked(srcLeftInt + 1, srcY);
  788. for (int srcX = srcLeftInt + 1; srcX < srcRightInt; ++srcX)
  789. {
  790. double a = srcPtr->A;
  791. blueSum += (double)srcPtr->B * a;
  792. greenSum += (double)srcPtr->G * a;
  793. redSum += (double)srcPtr->R * a;
  794. alphaSum += (double)srcPtr->A;
  795. ++srcPtr;
  796. }
  797. }
  798. // four corner pixels
  799. ColorBgra srcTL = source.GetPoint(srcLeftInt, srcTopInt);
  800. double srcTLA = srcTL.A;
  801. blueSum += srcTL.B * (srcTopWeight * srcLeftWeight) * srcTLA;
  802. greenSum += srcTL.G * (srcTopWeight * srcLeftWeight) * srcTLA;
  803. redSum += srcTL.R * (srcTopWeight * srcLeftWeight) * srcTLA;
  804. alphaSum += srcTL.A * (srcTopWeight * srcLeftWeight);
  805. ColorBgra srcTR = source.GetPoint(srcRightInt, srcTopInt);
  806. double srcTRA = srcTR.A;
  807. blueSum += srcTR.B * (srcTopWeight * srcRightWeight) * srcTRA;
  808. greenSum += srcTR.G * (srcTopWeight * srcRightWeight) * srcTRA;
  809. redSum += srcTR.R * (srcTopWeight * srcRightWeight) * srcTRA;
  810. alphaSum += srcTR.A * (srcTopWeight * srcRightWeight);
  811. ColorBgra srcBL = source.GetPoint(srcLeftInt, srcBottomInt);
  812. double srcBLA = srcBL.A;
  813. blueSum += srcBL.B * (srcBottomWeight * srcLeftWeight) * srcBLA;
  814. greenSum += srcBL.G * (srcBottomWeight * srcLeftWeight) * srcBLA;
  815. redSum += srcBL.R * (srcBottomWeight * srcLeftWeight) * srcBLA;
  816. alphaSum += srcBL.A * (srcBottomWeight * srcLeftWeight);
  817. ColorBgra srcBR = source.GetPoint(srcRightInt, srcBottomInt);
  818. double srcBRA = srcBR.A;
  819. blueSum += srcBR.B * (srcBottomWeight * srcRightWeight) * srcBRA;
  820. greenSum += srcBR.G * (srcBottomWeight * srcRightWeight) * srcBRA;
  821. redSum += srcBR.R * (srcBottomWeight * srcRightWeight) * srcBRA;
  822. alphaSum += srcBR.A * (srcBottomWeight * srcRightWeight);
  823. double area = (srcRight - srcLeft) * (srcBottom - srcTop);
  824. double alpha = alphaSum / area;
  825. double blue;
  826. double green;
  827. double red;
  828. if (alpha == 0)
  829. {
  830. blue = 0;
  831. green = 0;
  832. red = 0;
  833. }
  834. else
  835. {
  836. blue = blueSum / alphaSum;
  837. green = greenSum / alphaSum;
  838. red = redSum / alphaSum;
  839. }
  840. // add 0.5 so that rounding goes in the direction we want it to
  841. blue += 0.5;
  842. green += 0.5;
  843. red += 0.5;
  844. alpha += 0.5;
  845. dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24);
  846. ++dstPtr;
  847. }
  848. }
  849. }
  850. }
  851. /// <summary>
  852. /// Fits the source surface to this surface using nearest neighbor resampling.
  853. /// </summary>
  854. /// <param name="source">The surface to read pixels from.</param>
  855. /// <param name="dstRoi">The rectangle to clip rendering to.</param>
  856. public void NearestNeighborFitSurface(Surface source, Rectangle dstRoi)
  857. {
  858. Rectangle roi = Rectangle.Intersect(dstRoi, this.Bounds);
  859. unsafe
  860. {
  861. for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY)
  862. {
  863. int srcY = (dstY * source.height) / height;
  864. ColorBgra* srcRow = source.GetRowAddressUnchecked(srcY);
  865. ColorBgra* dstPtr = this.GetPointAddressUnchecked(roi.Left, dstY);
  866. for (int dstX = roi.Left; dstX < roi.Right; ++dstX)
  867. {
  868. int srcX = (dstX * source.width) / width;
  869. *dstPtr = *(srcRow + srcX);
  870. ++dstPtr;
  871. }
  872. }
  873. }
  874. }
  875. private double CubeClamped(double x)
  876. {
  877. if (x >= 0)
  878. {
  879. return x * x * x;
  880. }
  881. else
  882. {
  883. return 0;
  884. }
  885. }
  886. /// <summary>
  887. /// Implements R() as defined at http://astronomy.swin.edu.au/%7Epbourke/colour/bicubic/
  888. /// </summary>
  889. private double R(double x)
  890. {
  891. return (CubeClamped(x + 2) - (4 * CubeClamped(x + 1)) + (6 * CubeClamped(x)) - (4 * CubeClamped(x - 1))) / 6;
  892. }
  893. /// <summary>
  894. /// Fits the source surface to this surface using bicubic interpolation.
  895. /// </summary>
  896. /// <param name="source">The Surface to read pixels from.</param>
  897. /// <param name="dstRoi">The rectangle to clip rendering to.</param>
  898. /// <remarks>
  899. /// This method was implemented with correctness, not performance, in mind.
  900. /// Based on: "Bicubic Interpolation for Image Scaling" by Paul Bourke,
  901. /// http://astronomy.swin.edu.au/%7Epbourke/colour/bicubic/
  902. /// </remarks>
  903. public void BicubicFitSurface(Surface source, Rectangle dstRoi)
  904. {
  905. float leftF = (1 * (float)(width - 1)) / (float)(source.width - 1);
  906. float topF = (1 * (height - 1)) / (float)(source.height - 1);
  907. float rightF = ((float)(source.width - 3) * (float)(width - 1)) / (float)(source.width - 1);
  908. float bottomF = ((float)(source.Height - 3) * (float)(height - 1)) / (float)(source.height - 1);
  909. int left = (int)Math.Ceiling((double)leftF);
  910. int top = (int)Math.Ceiling((double)topF);
  911. int right = (int)Math.Floor((double)rightF);
  912. int bottom = (int)Math.Floor((double)bottomF);
  913. Rectangle[] rois = new Rectangle[] {
  914. Rectangle.FromLTRB(left, top, right, bottom),
  915. new Rectangle(0, 0, width, top),
  916. new Rectangle(0, top, left, height - top),
  917. new Rectangle(right, top, width - right, height - top),
  918. new Rectangle(left, bottom, right - left, height - bottom)
  919. };
  920. for (int i = 0; i < rois.Length; ++i)
  921. {
  922. rois[i].Intersect(dstRoi);
  923. if (rois[i].Width > 0 && rois[i].Height > 0)
  924. {
  925. if (i == 0)
  926. {
  927. BicubicFitSurfaceUnchecked(source, rois[i]);
  928. }
  929. else
  930. {
  931. BicubicFitSurfaceChecked(source, rois[i]);
  932. }
  933. }
  934. }
  935. }
  936. /// <summary>
  937. /// Implements bicubic filtering with bounds checking at every pixel.
  938. /// </summary>
  939. private void BicubicFitSurfaceChecked(Surface source, Rectangle dstRoi)
  940. {
  941. if (this.width < 2 || this.height < 2 || source.width < 2 || source.height < 2)
  942. {
  943. SuperSamplingFitSurface(source, dstRoi);
  944. }
  945. else
  946. {
  947. unsafe
  948. {
  949. Rectangle roi = Rectangle.Intersect(dstRoi, this.Bounds);
  950. Rectangle roiIn = Rectangle.Intersect(dstRoi, new Rectangle(1, 1, width - 1, height - 1));
  951. IntPtr rColCacheIP = Memory.Allocate(4 * (ulong)roi.Width * (ulong)sizeof(double));
  952. double* rColCache = (double*)rColCacheIP.ToPointer();
  953. // Precompute and then cache the value of R() for each column
  954. for (int dstX = roi.Left; dstX < roi.Right; ++dstX)
  955. {
  956. double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1);
  957. double srcColumnFloor = Math.Floor(srcColumn);
  958. double srcColumnFrac = srcColumn - srcColumnFloor;
  959. int srcColumnInt = (int)srcColumn;
  960. for (int m = -1; m <= 2; ++m)
  961. {
  962. int index = (m + 1) + ((dstX - roi.Left) * 4);
  963. double x = m - srcColumnFrac;
  964. rColCache[index] = R(x);
  965. }
  966. }
  967. // Set this up so we can cache the R()'s for every row
  968. double* rRowCache = stackalloc double[4];
  969. for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY)
  970. {
  971. double srcRow = (double)(dstY * (source.height - 1)) / (double)(height - 1);
  972. double srcRowFloor = (double)Math.Floor(srcRow);
  973. double srcRowFrac = srcRow - srcRowFloor;
  974. int srcRowInt = (int)srcRow;
  975. ColorBgra* dstPtr = this.GetPointAddressUnchecked(roi.Left, dstY);
  976. // Compute the R() values for this row
  977. for (int n = -1; n <= 2; ++n)
  978. {
  979. double x = srcRowFrac - n;
  980. rRowCache[n + 1] = R(x);
  981. }
  982. // See Perf Note below
  983. //int nFirst = Math.Max(-srcRowInt, -1);
  984. //int nLast = Math.Min(source.height - srcRowInt - 1, 2);
  985. for (int dstX = roi.Left; dstX < roi.Right; dstX++)
  986. {
  987. double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1);
  988. double srcColumnFloor = Math.Floor(srcColumn);
  989. double srcColumnFrac = srcColumn - srcColumnFloor;
  990. int srcColumnInt = (int)srcColumn;
  991. double blueSum = 0;
  992. double greenSum = 0;
  993. double redSum = 0;
  994. double alphaSum = 0;
  995. double totalWeight = 0;
  996. // See Perf Note below
  997. //int mFirst = Math.Max(-srcColumnInt, -1);
  998. //int mLast = Math.Min(source.width - srcColumnInt - 1, 2);
  999. ColorBgra* srcPtr = source.GetPointAddressUnchecked(srcColumnInt - 1, srcRowInt - 1);
  1000. for (int n = -1; n <= 2; ++n)
  1001. {
  1002. int srcY = srcRowInt + n;
  1003. for (int m = -1; m <= 2; ++m)
  1004. {
  1005. // Perf Note: It actually benchmarks faster on my system to do
  1006. // a bounds check for every (m,n) than it is to limit the loop
  1007. // to nFirst-Last and mFirst-mLast.
  1008. // I'm leaving the code above, albeit commented out, so that
  1009. // benchmarking between these two can still be performed.
  1010. if (source.IsVisible(srcColumnInt + m, srcY))
  1011. {
  1012. double w0 = rColCache[(m + 1) + (4 * (dstX - roi.Left))];
  1013. double w1 = rRowCache[n + 1];
  1014. double w = w0 * w1;
  1015. blueSum += srcPtr->B * w * srcPtr->A;
  1016. greenSum += srcPtr->G * w * srcPtr->A;
  1017. redSum += srcPtr->R * w * srcPtr->A;
  1018. alphaSum += srcPtr->A * w;
  1019. totalWeight += w;
  1020. }
  1021. ++srcPtr;
  1022. }
  1023. srcPtr = (ColorBgra*)((byte*)(srcPtr - 4) + source.stride);
  1024. }
  1025. double alpha = alphaSum / totalWeight;
  1026. double blue;
  1027. double green;
  1028. double red;
  1029. if (alpha == 0)
  1030. {
  1031. blue = 0;
  1032. green = 0;
  1033. red = 0;
  1034. }
  1035. else
  1036. {
  1037. blue = blueSum / alphaSum;
  1038. green = greenSum / alphaSum;
  1039. red = redSum / alphaSum;
  1040. // add 0.5 to ensure truncation to uint results in rounding
  1041. alpha += 0.5;
  1042. blue += 0.5;
  1043. green += 0.5;
  1044. red += 0.5;
  1045. }
  1046. dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24);
  1047. ++dstPtr;
  1048. } // for (dstX...
  1049. } // for (dstY...
  1050. Memory.Free(rColCacheIP);
  1051. } // unsafe
  1052. }
  1053. }
  1054. /// <summary>
  1055. /// Implements bicubic filtering with NO bounds checking at any pixel.
  1056. /// </summary>
  1057. public void BicubicFitSurfaceUnchecked(Surface source, Rectangle dstRoi)
  1058. {
  1059. if (this.width < 2 || this.height < 2 || source.width < 2 || source.height < 2)
  1060. {
  1061. SuperSamplingFitSurface(source, dstRoi);
  1062. }
  1063. else
  1064. {
  1065. unsafe
  1066. {
  1067. Rectangle roi = Rectangle.Intersect(dstRoi, this.Bounds);
  1068. Rectangle roiIn = Rectangle.Intersect(dstRoi, new Rectangle(1, 1, width - 1, height - 1));
  1069. IntPtr rColCacheIP = Memory.Allocate(4 * (ulong)roi.Width * (ulong)sizeof(double));
  1070. double* rColCache = (double*)rColCacheIP.ToPointer();
  1071. // Precompute and then cache the value of R() for each column
  1072. for (int dstX = roi.Left; dstX < roi.Right; ++dstX)
  1073. {
  1074. double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1);
  1075. double srcColumnFloor = Math.Floor(srcColumn);
  1076. double srcColumnFrac = srcColumn - srcColumnFloor;
  1077. int srcColumnInt = (int)srcColumn;
  1078. for (int m = -1; m <= 2; ++m)
  1079. {
  1080. int index = (m + 1) + ((dstX - roi.Left) * 4);
  1081. double x = m - srcColumnFrac;
  1082. rColCache[index] = R(x);
  1083. }
  1084. }
  1085. // Set this up so we can cache the R()'s for every row
  1086. double* rRowCache = stackalloc double[4];
  1087. for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY)
  1088. {
  1089. double srcRow = (double)(dstY * (source.height - 1)) / (double)(height - 1);
  1090. double srcRowFloor = Math.Floor(srcRow);
  1091. double srcRowFrac = srcRow - srcRowFloor;
  1092. int srcRowInt = (int)srcRow;
  1093. ColorBgra* dstPtr = this.GetPointAddressUnchecked(roi.Left, dstY);
  1094. // Compute the R() values for this row
  1095. for (int n = -1; n <= 2; ++n)
  1096. {
  1097. double x = srcRowFrac - n;
  1098. rRowCache[n + 1] = R(x);
  1099. }
  1100. rColCache = (double*)rColCacheIP.ToPointer();
  1101. ColorBgra* srcRowPtr = source.GetRowAddressUnchecked(srcRowInt - 1);
  1102. for (int dstX = roi.Left; dstX < roi.Right; dstX++)
  1103. {
  1104. double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1);
  1105. double srcColumnFloor = Math.Floor(srcColumn);
  1106. double srcColumnFrac = srcColumn - srcColumnFloor;
  1107. int srcColumnInt = (int)srcColumn;
  1108. double blueSum = 0;
  1109. double greenSum = 0;
  1110. double redSum = 0;
  1111. double alphaSum = 0;
  1112. double totalWeight = 0;
  1113. ColorBgra* srcPtr = srcRowPtr + srcColumnInt - 1;
  1114. for (int n = 0; n <= 3; ++n)
  1115. {
  1116. double w0 = rColCache[0] * rRowCache[n];
  1117. double w1 = rColCache[1] * rRowCache[n];
  1118. double w2 = rColCache[2] * rRowCache[n];
  1119. double w3 = rColCache[3] * rRowCache[n];
  1120. double a0 = srcPtr[0].A;
  1121. double a1 = srcPtr[1].A;
  1122. double a2 = srcPtr[2].A;
  1123. double a3 = srcPtr[3].A;
  1124. alphaSum += (a0 * w0) + (a1 * w1) + (a2 * w2) + (a3 * w3);
  1125. totalWeight += w0 + w1 + w2 + w3;
  1126. blueSum += (a0 * srcPtr[0].B * w0) + (a1 * srcPtr[1].B * w1) + (a2 * srcPtr[2].B * w2) + (a3 * srcPtr[3].B * w3);
  1127. greenSum += (a0 * srcPtr[0].G * w0) + (a1 * srcPtr[1].G * w1) + (a2 * srcPtr[2].G * w2) + (a3 * srcPtr[3].G * w3);
  1128. redSum += (a0 * srcPtr[0].R * w0) + (a1 * srcPtr[1].R * w1) + (a2 * srcPtr[2].R * w2) + (a3 * srcPtr[3].R * w3);
  1129. srcPtr = (ColorBgra*)((byte*)srcPtr + source.stride);
  1130. }
  1131. double alpha = alphaSum / totalWeight;
  1132. double blue;
  1133. double green;
  1134. double red;
  1135. if (alpha == 0)
  1136. {
  1137. blue = 0;
  1138. green = 0;
  1139. red = 0;
  1140. }
  1141. else
  1142. {
  1143. blue = blueSum / alphaSum;
  1144. green = greenSum / alphaSum;
  1145. red = redSum / alphaSum;
  1146. // add 0.5 to ensure truncation to uint results in rounding
  1147. alpha += 0.5;
  1148. blue += 0.5;
  1149. green += 0.5;
  1150. red += 0.5;
  1151. }
  1152. dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24);
  1153. ++dstPtr;
  1154. rColCache += 4;
  1155. } // for (dstX...
  1156. } // for (dstY...
  1157. Memory.Free(rColCacheIP);
  1158. } // unsafe
  1159. }
  1160. }
  1161. /// <summary>
  1162. /// Releases all resources held by this Surface object.
  1163. /// </summary>
  1164. public void Dispose()
  1165. {
  1166. Dispose(true);
  1167. GC.SuppressFinalize(this);
  1168. }
  1169. private void Dispose(bool disposing)
  1170. {
  1171. if (!disposed)
  1172. {
  1173. disposed = true;
  1174. if (disposing)
  1175. {
  1176. scan0.Dispose();
  1177. scan0 = null;
  1178. //因内存拼图时此处会报错,遂临时注释之
  1179. if (mat != null && !mat.IsDisposed)
  1180. {
  1181. mat.Dispose();
  1182. mat = null;
  1183. }
  1184. }
  1185. }
  1186. }
  1187. ///<summary>
  1188. /// 生成缩略图
  1189. /// </summary>
  1190. /// <param name="mat">源图片</param>
  1191. /// <param name="width">缩略图宽度</param>
  1192. /// <param name="height">缩略图高度</param>
  1193. /// <param name="mode">生成缩略图的方式</param>
  1194. public static OpenCvSharp.Mat MakeThumbnailMat(OpenCvSharp.Mat mat, int width, int height, string mode)
  1195. {
  1196. int towidth = width;
  1197. int toheight = height;
  1198. switch (mode)
  1199. {
  1200. case "HW"://指定高宽缩放(可能变形)
  1201. break;
  1202. case "W"://指定宽,高按比例
  1203. toheight = mat.Height * width / mat.Width;
  1204. break;
  1205. case "H"://指定高,宽按比例
  1206. towidth = mat.Width * height / mat.Height;
  1207. break;
  1208. default:
  1209. break;
  1210. }
  1211. OpenCvSharp.Mat temp = new OpenCvSharp.Mat();
  1212. OpenCvSharp.Cv2.Resize(mat, temp, new OpenCvSharp.Size(towidth, toheight));
  1213. return temp;
  1214. }
  1215. ///<summary>
  1216. /// 生成缩略图
  1217. /// </summary>
  1218. /// <param name="originalImage">源图片</param>
  1219. /// <param name="width">缩略图宽度</param>
  1220. /// <param name="height">缩略图高度</param>
  1221. /// <param name="mode">生成缩略图的方式</param>
  1222. public static Bitmap MakeThumbnail(/*Image originalImage*/OpenCvSharp.Mat mat, int width, int height, string mode)
  1223. {
  1224. int towidth = width;
  1225. int toheight = height;
  1226. int x = 0;
  1227. int y = 0;
  1228. int ow = mat.Width;
  1229. int oh = mat.Height;
  1230. switch (mode)
  1231. {
  1232. case "HW"://指定高宽缩放(可能变形)
  1233. break;
  1234. case "W"://指定宽,高按比例
  1235. toheight = mat.Height * width / mat.Width;
  1236. break;
  1237. case "H"://指定高,宽按比例
  1238. towidth = mat.Width * height / mat.Height;
  1239. break;
  1240. case "Cut"://指定高宽裁减(不变形)
  1241. if ((double)mat.Width / (double)mat.Height > (double)towidth / (double)toheight)
  1242. {
  1243. oh = mat.Height;
  1244. ow = mat.Height * towidth / toheight;
  1245. y = 0;
  1246. x = (mat.Width - ow) / 2;
  1247. }
  1248. else
  1249. {
  1250. ow = mat.Width;
  1251. oh = mat.Width * height / towidth;
  1252. x = 0;
  1253. y = (mat.Height - oh) / 2;
  1254. }
  1255. break;
  1256. default:
  1257. break;
  1258. }
  1259. OpenCvSharp.Mat temp = new OpenCvSharp.Mat();
  1260. OpenCvSharp.Cv2.Resize(mat, temp, new OpenCvSharp.Size(towidth, toheight));
  1261. return OpenCvSharp.Extensions.BitmapConverter.ToBitmap(temp);
  1262. /*//新建一个bmp图片
  1263. Bitmap bitmap = new System.Drawing.Bitmap(towidth, toheight);
  1264. //新建一个画板
  1265. Graphics g = System.Drawing.Graphics.FromImage(bitmap);
  1266. //设置高质量插值法
  1267. g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
  1268. //设置高质量,低速度呈现平滑程度
  1269. g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
  1270. //清空画布并以透明背景色填充
  1271. g.Clear(Color.Transparent);
  1272. //在指定位置并且按指定大小绘制原图片的指定部分
  1273. g.DrawImage(originalImage, new Rectangle(0, 0, towidth, toheight),
  1274. new Rectangle(x, y, ow, oh),
  1275. GraphicsUnit.Pixel);
  1276. return bitmap;*/
  1277. }
  1278. public static Bitmap MakeThumbnail(Image originalImage, int width, int height, string mode)
  1279. {
  1280. int towidth = width;
  1281. int toheight = height;
  1282. int x = 0;
  1283. int y = 0;
  1284. int ow = originalImage.Width;
  1285. int oh = originalImage.Height;
  1286. switch (mode)
  1287. {
  1288. case "HW"://指定高宽缩放(可能变形)
  1289. break;
  1290. case "W"://指定宽,高按比例
  1291. toheight = originalImage.Height * width / originalImage.Width;
  1292. break;
  1293. case "H"://指定高,宽按比例
  1294. towidth = originalImage.Width * height / originalImage.Height;
  1295. break;
  1296. case "Cut"://指定高宽裁减(不变形)
  1297. if ((double)originalImage.Width / (double)originalImage.Height > (double)towidth / (double)toheight)
  1298. {
  1299. oh = originalImage.Height;
  1300. ow = originalImage.Height * towidth / toheight;
  1301. y = 0;
  1302. x = (originalImage.Width - ow) / 2;
  1303. }
  1304. else
  1305. {
  1306. ow = originalImage.Width;
  1307. oh = originalImage.Width * height / towidth;
  1308. x = 0;
  1309. y = (originalImage.Height - oh) / 2;
  1310. }
  1311. break;
  1312. default:
  1313. break;
  1314. }
  1315. //新建一个bmp图片
  1316. Bitmap bitmap = new System.Drawing.Bitmap(towidth, toheight);
  1317. //新建一个画板
  1318. Graphics g = System.Drawing.Graphics.FromImage(bitmap);
  1319. //设置高质量插值法
  1320. g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
  1321. //设置高质量,低速度呈现平滑程度
  1322. g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
  1323. //清空画布并以透明背景色填充
  1324. g.Clear(Color.Transparent);
  1325. //在指定位置并且按指定大小绘制原图片的指定部分
  1326. g.DrawImage(originalImage, new Rectangle(0, 0, towidth, toheight),
  1327. new Rectangle(x, y, ow, oh),
  1328. GraphicsUnit.Pixel);
  1329. return bitmap;
  1330. }
  1331. }
  1332. }