SurfaceBoxBaseRenderer.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. using System.Diagnostics;
  2. using System.Drawing;
  3. namespace PaintDotNet
  4. {
  5. public sealed class SurfaceBoxBaseRenderer
  6. : SurfaceBoxRenderer
  7. {
  8. private Surface source;
  9. private RenderDelegate renderDelegate;
  10. public Surface Source
  11. {
  12. get
  13. {
  14. return this.source;
  15. }
  16. set
  17. {
  18. this.source = value;
  19. Flush();
  20. }
  21. }
  22. private void Flush()
  23. {
  24. this.renderDelegate = null;
  25. }
  26. protected override void OnVisibleChanged()
  27. {
  28. Invalidate();
  29. }
  30. private void ChooseRenderDelegate()
  31. {
  32. if (SourceSize.Width > DestinationSize.Width)
  33. {
  34. // zoom out
  35. this.renderDelegate = new RenderDelegate(RenderZoomOutRotatedGridMultisampling);
  36. }
  37. else if (SourceSize == DestinationSize)
  38. {
  39. // zoom 100%
  40. this.renderDelegate = new RenderDelegate(RenderOneToOne);
  41. }
  42. else if (SourceSize.Width < DestinationSize.Width)
  43. {
  44. // zoom in
  45. this.renderDelegate = new RenderDelegate(RenderZoomInNearestNeighbor);
  46. }
  47. }
  48. public override void OnDestinationSizeChanged()
  49. {
  50. ChooseRenderDelegate();
  51. this.OwnerList.InvalidateLookups();
  52. base.OnDestinationSizeChanged();
  53. }
  54. public override void OnSourceSizeChanged()
  55. {
  56. ChooseRenderDelegate();
  57. this.OwnerList.InvalidateLookups();
  58. base.OnSourceSizeChanged();
  59. }
  60. public static void RenderOneToOne(Surface dst, Surface source, Point offset)
  61. {
  62. unsafe
  63. {
  64. Rectangle srcRect = new Rectangle(offset, dst.Size);
  65. srcRect.Intersect(source.Bounds);
  66. for (int dstRow = 0; dstRow < srcRect.Height; ++dstRow)
  67. {
  68. ColorBgra* dstRowPtr = dst.GetRowAddressUnchecked(dstRow);
  69. ColorBgra* srcRowPtr = source.GetPointAddressUnchecked(offset.X, dstRow + offset.Y);
  70. int dstCol = offset.X;
  71. int dstColEnd = offset.X + srcRect.Width;
  72. int checkerY = dstRow + offset.Y;
  73. while (dstCol < dstColEnd)
  74. {
  75. int b = srcRowPtr->B;
  76. int g = srcRowPtr->G;
  77. int r = srcRowPtr->R;
  78. int a = srcRowPtr->A;
  79. // Blend it over the checkerboard background
  80. int v = (((dstCol ^ checkerY) & 8) << 3) + 191;
  81. a = a + (a >> 7);
  82. int vmia = v * (256 - a);
  83. r = ((r * a) + vmia) >> 8;
  84. g = ((g * a) + vmia) >> 8;
  85. b = ((b * a) + vmia) >> 8;
  86. dstRowPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)255 << 24);
  87. ++dstRowPtr;
  88. ++srcRowPtr;
  89. ++dstCol;
  90. }
  91. }
  92. }
  93. }
  94. private void RenderOneToOne(Surface dst, Point offset)
  95. {
  96. RenderOneToOne(dst, this.source, offset);
  97. }
  98. private void RenderZoomInNearestNeighbor(Surface dst, Point offset)
  99. {
  100. unsafe
  101. {
  102. int[] d2SLookupY = OwnerList.Dst2SrcLookupY;
  103. int[] d2SLookupX = OwnerList.Dst2SrcLookupX;
  104. for (int dstRow = 0; dstRow < dst.Height; ++dstRow)
  105. {
  106. int nnY = dstRow + offset.Y;
  107. int srcY = d2SLookupY[nnY];
  108. ColorBgra* dstPtr = dst.GetRowAddressUnchecked(dstRow);
  109. ColorBgra* srcRow = this.source.GetRowAddressUnchecked(srcY);
  110. for (int dstCol = 0; dstCol < dst.Width; ++dstCol)
  111. {
  112. int nnX = dstCol + offset.X;
  113. int srcX = d2SLookupX[nnX];
  114. ColorBgra src = *(srcRow + srcX);
  115. int b = src.B;
  116. int g = src.G;
  117. int r = src.R;
  118. int a = src.A;
  119. // Blend it over the checkerboard background
  120. int v = (((dstCol + offset.X) ^ (dstRow + offset.Y)) & 8) * 8 + 191;
  121. a = a + (a >> 7);
  122. int vmia = v * (256 - a);
  123. r = ((r * a) + vmia) >> 8;
  124. g = ((g * a) + vmia) >> 8;
  125. b = ((b * a) + vmia) >> 8;
  126. dstPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)255 << 24);
  127. ++dstPtr;
  128. }
  129. }
  130. }
  131. }
  132. public static void RenderZoomOutRotatedGridMultisampling(Surface dst, Surface source, Point offset, Size destinationSize)
  133. {
  134. unsafe
  135. {
  136. const int fpShift = 12;
  137. const int fpFactor = (1 << fpShift);
  138. Size sourceSize = source.Size;
  139. long fDstLeftLong = ((long)offset.X * fpFactor * (long)sourceSize.Width) / (long)destinationSize.Width;
  140. long fDstTopLong = ((long)offset.Y * fpFactor * (long)sourceSize.Height) / (long)destinationSize.Height;
  141. long fDstRightLong = ((long)(offset.X + dst.Width) * fpFactor * (long)sourceSize.Width) / (long)destinationSize.Width;
  142. long fDstBottomLong = ((long)(offset.Y + dst.Height) * fpFactor * (long)sourceSize.Height) / (long)destinationSize.Height;
  143. int fDstLeft = (int)fDstLeftLong;
  144. int fDstTop = (int)fDstTopLong;
  145. int fDstRight = (int)fDstRightLong;
  146. int fDstBottom = (int)fDstBottomLong;
  147. int dx = (fDstRight - fDstLeft) / dst.Width;
  148. int dy = (fDstBottom - fDstTop) / dst.Height;
  149. for (int dstRow = 0, fDstY = fDstTop;
  150. dstRow < dst.Height && fDstY < fDstBottom;
  151. ++dstRow, fDstY += dy)
  152. {
  153. int srcY1 = fDstY >> fpShift; // y
  154. int srcY2 = (fDstY + (dy >> 2)) >> fpShift; // y + 0.25
  155. int srcY3 = (fDstY + (dy >> 1)) >> fpShift; // y + 0.50
  156. int srcY4 = (fDstY + (dy >> 1) + (dy >> 2)) >> fpShift; // y + 0.75
  157. #if DEBUG
  158. Debug.Assert(source.IsRowVisible(srcY1));
  159. Debug.Assert(source.IsRowVisible(srcY2));
  160. Debug.Assert(source.IsRowVisible(srcY3));
  161. Debug.Assert(source.IsRowVisible(srcY4));
  162. Debug.Assert(dst.IsRowVisible(dstRow));
  163. #endif
  164. ColorBgra* src1 = source.GetRowAddressUnchecked(srcY1);
  165. ColorBgra* src2 = source.GetRowAddressUnchecked(srcY2);
  166. ColorBgra* src3 = source.GetRowAddressUnchecked(srcY3);
  167. ColorBgra* src4 = source.GetRowAddressUnchecked(srcY4);
  168. ColorBgra* dstPtr = dst.GetRowAddressUnchecked(dstRow);
  169. int checkerY = dstRow + offset.Y;
  170. int checkerX = offset.X;
  171. int maxCheckerX = checkerX + dst.Width;
  172. for (int fDstX = fDstLeft;
  173. checkerX < maxCheckerX && fDstX < fDstRight;
  174. ++checkerX, fDstX += dx)
  175. {
  176. int srcX1 = (fDstX + (dx >> 2)) >> fpShift; // x + 0.25
  177. int srcX2 = (fDstX + (dx >> 1) + (dx >> 2)) >> fpShift; // x + 0.75
  178. int srcX3 = fDstX >> fpShift; // x
  179. int srcX4 = (fDstX + (dx >> 1)) >> fpShift; // x + 0.50
  180. #if DEBUG
  181. Debug.Assert(source.IsColumnVisible(srcX1));
  182. Debug.Assert(source.IsColumnVisible(srcX2));
  183. Debug.Assert(source.IsColumnVisible(srcX3));
  184. Debug.Assert(source.IsColumnVisible(srcX4));
  185. #endif
  186. ColorBgra* p1 = src1 + srcX1;
  187. ColorBgra* p2 = src2 + srcX2;
  188. ColorBgra* p3 = src3 + srcX3;
  189. ColorBgra* p4 = src4 + srcX4;
  190. int r = (2 + p1->R + p2->R + p3->R + p4->R) >> 2;
  191. int g = (2 + p1->G + p2->G + p3->G + p4->G) >> 2;
  192. int b = (2 + p1->B + p2->B + p3->B + p4->B) >> 2;
  193. int a = (2 + p1->A + p2->A + p3->A + p4->A) >> 2;
  194. // Blend it over the checkerboard background
  195. int v = ((checkerX ^ checkerY) & 8) * 8 + 191;
  196. a = a + (a >> 7);
  197. int vmia = v * (256 - a);
  198. r = ((r * a) + vmia) >> 8;
  199. g = ((g * a) + vmia) >> 8;
  200. b = ((b * a) + vmia) >> 8;
  201. dstPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + 0xff000000;
  202. ++dstPtr;
  203. }
  204. }
  205. }
  206. }
  207. private void RenderZoomOutRotatedGridMultisampling(Surface dst, Point offset)
  208. {
  209. RenderZoomOutRotatedGridMultisampling(dst, this.source, offset, this.DestinationSize);
  210. }
  211. public override void Render(Surface dst, Point offset)
  212. {
  213. if (this.renderDelegate == null)
  214. {
  215. ChooseRenderDelegate();
  216. }
  217. this.renderDelegate(dst, offset);
  218. }
  219. public SurfaceBoxBaseRenderer(SurfaceBoxRendererList ownerList, Surface source)
  220. : base(ownerList)
  221. {
  222. this.source = source;
  223. ChooseRenderDelegate();
  224. }
  225. }
  226. }