/// <summary> /// Copies the contents of the given surface to the upper left corner of this surface. /// </summary> /// <param name="source">The surface to copy pixels from.</param> /// <remarks> /// The source surface does not need to have the same dimensions as this surface. Clipping /// will be handled automatically. No resizing will be done. /// </remarks> public void CopySurface(Surface source) { if (disposed) { throw new ObjectDisposedException("Surface"); } if (this.stride == source.stride && (this.width * ColorBgra.SizeOf) == this.stride && this.width == source.width && this.height == source.height) { unsafe { Memory.Copy(this.scan0.VoidStar, source.scan0.VoidStar, ((ulong)(height - 1) * (ulong)stride) + ((ulong)width * (ulong)ColorBgra.SizeOf)); } } else { int copyWidth = Math.Min(width, source.width); int copyHeight = Math.Min(height, source.height); unsafe { for (int y = 0; y < copyHeight; ++y) { Memory.Copy(GetRowAddressUnchecked(y), source.GetRowAddressUnchecked(y), (ulong)copyWidth * (ulong)ColorBgra.SizeOf); } } } }
protected override void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler progressCallback) { GifSaveConfigToken gsct = (GifSaveConfigToken)token; // Flatten and pre-process the image scratchSurface.Clear(ColorBgra.FromBgra(255, 255, 255, 0)); using (RenderArgs ra = new RenderArgs(scratchSurface)) { input.Render(ra, true); } for (int y = 0; y < scratchSurface.Height; ++y) { unsafe { ColorBgra* ptr = scratchSurface.GetRowAddressUnchecked(y); for (int x = 0; x < scratchSurface.Width; ++x) { if (ptr->A < gsct.Threshold) { ptr->Bgra = 0; } else { if (gsct.PreMultiplyAlpha) { int r = ((ptr->R * ptr->A) + (255 * (255 - ptr->A))) / 255; int g = ((ptr->G * ptr->A) + (255 * (255 - ptr->A))) / 255; int b = ((ptr->B * ptr->A) + (255 * (255 - ptr->A))) / 255; int a = 255; *ptr = ColorBgra.FromBgra((byte)b, (byte)g, (byte)r, (byte)a); } else { ptr->Bgra |= 0xff000000; } } ++ptr; } } } using (Bitmap quantized = Quantize(scratchSurface, gsct.DitherLevel, 255, progressCallback)) { quantized.Save(output, ImageFormat.Gif); } }
protected override void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler progressCallback) { GifSaveConfigToken gsct = (GifSaveConfigToken)token; // Flatten and pre-process the image scratchSurface.Clear(ColorBgra.FromBgra(255, 255, 255, 0)); using (RenderArgs ra = new RenderArgs(scratchSurface)) { input.Render(ra, true); } for (int y = 0; y < scratchSurface.Height; ++y) { unsafe { ColorBgra *ptr = scratchSurface.GetRowAddressUnchecked(y); for (int x = 0; x < scratchSurface.Width; ++x) { if (ptr->A < gsct.Threshold) { ptr->Bgra = 0; } else { if (gsct.PreMultiplyAlpha) { int r = ((ptr->R * ptr->A) + (255 * (255 - ptr->A))) / 255; int g = ((ptr->G * ptr->A) + (255 * (255 - ptr->A))) / 255; int b = ((ptr->B * ptr->A) + (255 * (255 - ptr->A))) / 255; int a = 255; *ptr = ColorBgra.FromBgra((byte)b, (byte)g, (byte)r, (byte)a); } else { ptr->Bgra |= 0xff000000; } } ++ptr; } } } using (Bitmap quantized = Quantize(scratchSurface, gsct.DitherLevel, 255, progressCallback)) { quantized.Save(output, ImageFormat.Gif); } }
public void Apply(Surface dst, Surface lhs, Surface rhs) { if (dst.Size != lhs.Size) { throw new ArgumentException("dst.Size != lhs.Size"); } if (lhs.Size != rhs.Size) { throw new ArgumentException("lhs.Size != rhs.Size"); } unsafe { for (int y = 0; y < dst.Height; ++y) { ColorBgra *dstPtr = dst.GetRowAddressUnchecked(y); ColorBgra *lhsPtr = lhs.GetRowAddressUnchecked(y); ColorBgra *rhsPtr = lhs.GetRowAddressUnchecked(y); Apply(dstPtr, lhsPtr, rhsPtr, dst.Width); } } }
public void Apply(Surface dst, Surface src) { if (dst.Size != src.Size) { throw new ArgumentException("dst.Size != src.Size"); } unsafe { for (int y = 0; y < dst.Height; ++y) { ColorBgra *dstPtr = dst.GetRowAddressUnchecked(y); ColorBgra *srcPtr = src.GetRowAddressUnchecked(y); Apply(dstPtr, srcPtr, dst.Width); } } }
/// <summary> /// Creates a new Surface and copies the pixels from a Bitmap to it. /// </summary> /// <param name="bitmap">The Bitmap to duplicate.</param> /// <returns>A new Surface that is the same size as the given Bitmap and that has the same pixel values.</returns> public static Surface CopyFromBitmap(Bitmap bitmap) { Surface surface = new Surface(bitmap.Width, bitmap.Height); BitmapData bd = bitmap.LockBits(surface.Bounds, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); unsafe { for (int y = 0; y < bd.Height; ++y) { Memory.Copy((void *)surface.GetRowAddressUnchecked(y), (byte *)bd.Scan0.ToPointer() + (y * bd.Stride), (ulong)bd.Width * ColorBgra.SizeOf); } } bitmap.UnlockBits(bd); return(surface); }
private void RenderZoomInNearestNeighbor(Surface dst, Point offset) { unsafe { int[] d2SLookupY = OwnerList.Dst2SrcLookupY; int[] d2SLookupX = OwnerList.Dst2SrcLookupX; for (int dstRow = 0; dstRow < dst.Height; ++dstRow) { int nnY = dstRow + offset.Y; int srcY = d2SLookupY[nnY]; ColorBgra *dstPtr = dst.GetRowAddressUnchecked(dstRow); ColorBgra *srcRow = this.source.GetRowAddressUnchecked(srcY); for (int dstCol = 0; dstCol < dst.Width; ++dstCol) { int nnX = dstCol + offset.X; int srcX = d2SLookupX[nnX]; ColorBgra src = *(srcRow + srcX); int b = src.B; int g = src.G; int r = src.R; int a = src.A; // Blend it over the checkerboard background int v = (((dstCol + offset.X) ^ (dstRow + offset.Y)) & 8) * 8 + 191; a = a + (a >> 7); int vmia = v * (256 - a); r = ((r * a) + vmia) >> 8; g = ((g * a) + vmia) >> 8; b = ((b * a) + vmia) >> 8; dstPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)255 << 24); ++dstPtr; } } } }
public static void RenderOneToOne(Surface dst, Surface source, Point offset) { unsafe { Rectangle srcRect = new Rectangle(offset, dst.Size); srcRect.Intersect(source.Bounds); for (int dstRow = 0; dstRow < srcRect.Height; ++dstRow) { ColorBgra *dstRowPtr = dst.GetRowAddressUnchecked(dstRow); ColorBgra *srcRowPtr = source.GetPointAddressUnchecked(offset.X, dstRow + offset.Y); int dstCol = offset.X; int dstColEnd = offset.X + srcRect.Width; int checkerY = dstRow + offset.Y; while (dstCol < dstColEnd) { int b = srcRowPtr->B; int g = srcRowPtr->G; int r = srcRowPtr->R; int a = srcRowPtr->A; // Blend it over the checkerboard background int v = (((dstCol ^ checkerY) & 8) << 3) + 191; a = a + (a >> 7); int vmia = v * (256 - a); r = ((r * a) + vmia) >> 8; g = ((g * a) + vmia) >> 8; b = ((b * a) + vmia) >> 8; dstRowPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)255 << 24); ++dstRowPtr; ++srcRowPtr; ++dstCol; } } } }
public static void RenderOneToOne(Surface dst, Surface source, Point offset) { unsafe { Rectangle srcRect = new Rectangle(offset, dst.Size); srcRect.Intersect(source.Bounds); for (int dstRow = 0; dstRow < srcRect.Height; ++dstRow) { ColorBgra* dstRowPtr = dst.GetRowAddressUnchecked(dstRow); ColorBgra* srcRowPtr = source.GetPointAddressUnchecked(offset.X, dstRow + offset.Y); int dstCol = offset.X; int dstColEnd = offset.X + srcRect.Width; int checkerY = dstRow + offset.Y; while (dstCol < dstColEnd) { int b = srcRowPtr->B; int g = srcRowPtr->G; int r = srcRowPtr->R; int a = srcRowPtr->A; // Blend it over the checkerboard background int v = (((dstCol ^ checkerY) & 8) << 3) + 191; a = a + (a >> 7); int vmia = v * (256 - a); r = ((r * a) + vmia) >> 8; g = ((g * a) + vmia) >> 8; b = ((b * a) + vmia) >> 8; dstRowPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)255 << 24); ++dstRowPtr; ++srcRowPtr; ++dstCol; } } } }
/// <summary> /// Implements bicubic filtering with NO bounds checking at any pixel. /// </summary> private unsafe void BicubicFitSurfaceUnchecked(Surface source, Rectangle dstRoi) { Rectangle roi = Rectangle.Intersect(dstRoi, this.Bounds); Rectangle roiIn = Rectangle.Intersect(dstRoi, new Rectangle(1, 1, width - 1, height - 1)); IntPtr rColCacheIP = Memory.Allocate(4 * (ulong)roi.Width * (ulong)sizeof(double)); double *rColCache = (double *)rColCacheIP.ToPointer(); // Precompute and then cache the value of R() for each column for (int dstX = roi.Left; dstX < roi.Right; ++dstX) { double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1); double srcColumnFloor = Math.Floor(srcColumn); double srcColumnFrac = srcColumn - srcColumnFloor; for (int m = -1; m <= 2; ++m) { int index = (m + 1) + ((dstX - roi.Left) * 4); double x = m - srcColumnFrac; rColCache[index] = R(x); } } // Set this up so we can cache the R()'s for every row double *rRowCache = stackalloc double[4]; for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY) { double srcRow = (double)(dstY * (source.height - 1)) / (double)(height - 1); double srcRowFloor = Math.Floor(srcRow); double srcRowFrac = srcRow - srcRowFloor; int srcRowInt = (int)srcRow; ColorBgra *dstPtr = this.GetPointAddressUnchecked(roi.Left, dstY); // Compute the R() values for this row for (int n = -1; n <= 2; ++n) { double x = srcRowFrac - n; rRowCache[n + 1] = R(x); } rColCache = (double *)rColCacheIP.ToPointer(); ColorBgra *srcRowPtr = source.GetRowAddressUnchecked(srcRowInt - 1); for (int dstX = roi.Left; dstX < roi.Right; dstX++) { double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1); double srcColumnFloor = Math.Floor(srcColumn); double srcColumnFrac = srcColumn - srcColumnFloor; int srcColumnInt = (int)srcColumn; double blueSum = 0; double greenSum = 0; double redSum = 0; double alphaSum = 0; double totalWeight = 0; ColorBgra *srcPtr = srcRowPtr + srcColumnInt - 1; for (int n = 0; n <= 3; ++n) { double w0 = rColCache[0] * rRowCache[n]; double w1 = rColCache[1] * rRowCache[n]; double w2 = rColCache[2] * rRowCache[n]; double w3 = rColCache[3] * rRowCache[n]; double a0 = srcPtr[0].A; double a1 = srcPtr[1].A; double a2 = srcPtr[2].A; double a3 = srcPtr[3].A; alphaSum += (a0 * w0) + (a1 * w1) + (a2 * w2) + (a3 * w3); totalWeight += w0 + w1 + w2 + w3; blueSum += (a0 * srcPtr[0].B * w0) + (a1 * srcPtr[1].B * w1) + (a2 * srcPtr[2].B * w2) + (a3 * srcPtr[3].B * w3); greenSum += (a0 * srcPtr[0].G * w0) + (a1 * srcPtr[1].G * w1) + (a2 * srcPtr[2].G * w2) + (a3 * srcPtr[3].G * w3); redSum += (a0 * srcPtr[0].R * w0) + (a1 * srcPtr[1].R * w1) + (a2 * srcPtr[2].R * w2) + (a3 * srcPtr[3].R * w3); srcPtr = (ColorBgra *)((byte *)srcPtr + source.stride); } double alpha = alphaSum / totalWeight; double blue; double green; double red; if (alpha == 0) { blue = 0; green = 0; red = 0; } else { blue = blueSum / alphaSum; green = greenSum / alphaSum; red = redSum / alphaSum; // add 0.5 to ensure truncation to uint results in rounding alpha += 0.5; blue += 0.5; green += 0.5; red += 0.5; } dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24); ++dstPtr; rColCache += 4; } // for (dstX... } // for (dstY... Memory.Free(rColCacheIP); }
private unsafe void DrawACircle(PointF pt, Surface srfSrc, Surface srfDst, Point difference, Rectangle rect) { float bw = AppEnvironment.PenInfo.Width / 2; float envAlpha = AppEnvironment.PrimaryColor.A / 255.0f; rect.Intersect(new Rectangle(difference, srfSrc.Size)); rect.Intersect(srfDst.Bounds); if (rect.Width == 0 || rect.Height == 0) { return; } // envAlpha = envAlpha^4 envAlpha *= envAlpha; envAlpha *= envAlpha; for (int y = rect.Top; y < rect.Bottom; y++) { ColorBgra *srcRow = srfSrc.GetRowAddressUnchecked(y - difference.Y); ColorBgra *dstRow = srfDst.GetRowAddressUnchecked(y); for (int x = rect.Left; x < rect.Right; x++) { ColorBgra *srcPtr = unchecked(srcRow + x - difference.X); ColorBgra *dstPtr = unchecked(dstRow + x); float distFromRing = 0.5f + bw - Utility.Distance(pt, new PointF(x, y)); if (distFromRing > 0) { float alpha = antialiasing ? Utility.Clamp(distFromRing * envAlpha, 0, 1) : 1; alpha *= srcPtr->A / 255.0f; dstPtr->A = (byte)(255 - (255 - dstPtr->A) * (1 - alpha)); if (0 == (alpha + (1 - alpha) * dstPtr->A / 255)) { dstPtr->Bgra = 0; } else { dstPtr->R = (byte)((srcPtr->R * alpha + dstPtr->R * (1 - alpha) * dstPtr->A / 255) / (alpha + (1 - alpha) * dstPtr->A / 255)); dstPtr->G = (byte)((srcPtr->G * alpha + dstPtr->G * (1 - alpha) * dstPtr->A / 255) / (alpha + (1 - alpha) * dstPtr->A / 255)); dstPtr->B = (byte)((srcPtr->B * alpha + dstPtr->B * (1 - alpha) * dstPtr->A / 255) / (alpha + (1 - alpha) * dstPtr->A / 255)); } } } } rect.Inflate(1, 1); Document.Invalidate(rect); }
public static void RenderZoomOutRotatedGridMultisampling(Surface dst, Surface source, Point offset, Size destinationSize) { unsafe { const int fpShift = 12; const int fpFactor = (1 << fpShift); Size sourceSize = source.Size; long fDstLeftLong = ((long)offset.X * fpFactor * (long)sourceSize.Width) / (long)destinationSize.Width; long fDstTopLong = ((long)offset.Y * fpFactor * (long)sourceSize.Height) / (long)destinationSize.Height; long fDstRightLong = ((long)(offset.X + dst.Width) * fpFactor * (long)sourceSize.Width) / (long)destinationSize.Width; long fDstBottomLong = ((long)(offset.Y + dst.Height) * fpFactor * (long)sourceSize.Height) / (long)destinationSize.Height; int fDstLeft = (int)fDstLeftLong; int fDstTop = (int)fDstTopLong; int fDstRight = (int)fDstRightLong; int fDstBottom = (int)fDstBottomLong; int dx = (fDstRight - fDstLeft) / dst.Width; int dy = (fDstBottom - fDstTop) / dst.Height; for (int dstRow = 0, fDstY = fDstTop; dstRow < dst.Height && fDstY < fDstBottom; ++dstRow, fDstY += dy) { int srcY1 = fDstY >> fpShift; // y int srcY2 = (fDstY + (dy >> 2)) >> fpShift; // y + 0.25 int srcY3 = (fDstY + (dy >> 1)) >> fpShift; // y + 0.50 int srcY4 = (fDstY + (dy >> 1) + (dy >> 2)) >> fpShift; // y + 0.75 #if DEBUG Debug.Assert(source.IsRowVisible(srcY1)); Debug.Assert(source.IsRowVisible(srcY2)); Debug.Assert(source.IsRowVisible(srcY3)); Debug.Assert(source.IsRowVisible(srcY4)); Debug.Assert(dst.IsRowVisible(dstRow)); #endif ColorBgra* src1 = source.GetRowAddressUnchecked(srcY1); ColorBgra* src2 = source.GetRowAddressUnchecked(srcY2); ColorBgra* src3 = source.GetRowAddressUnchecked(srcY3); ColorBgra* src4 = source.GetRowAddressUnchecked(srcY4); ColorBgra* dstPtr = dst.GetRowAddressUnchecked(dstRow); int checkerY = dstRow + offset.Y; int checkerX = offset.X; int maxCheckerX = checkerX + dst.Width; for (int fDstX = fDstLeft; checkerX < maxCheckerX && fDstX < fDstRight; ++checkerX, fDstX += dx) { int srcX1 = (fDstX + (dx >> 2)) >> fpShift; // x + 0.25 int srcX2 = (fDstX + (dx >> 1) + (dx >> 2)) >> fpShift; // x + 0.75 int srcX3 = fDstX >> fpShift; // x int srcX4 = (fDstX + (dx >> 1)) >> fpShift; // x + 0.50 #if DEBUG Debug.Assert(source.IsColumnVisible(srcX1)); Debug.Assert(source.IsColumnVisible(srcX2)); Debug.Assert(source.IsColumnVisible(srcX3)); Debug.Assert(source.IsColumnVisible(srcX4)); #endif ColorBgra* p1 = src1 + srcX1; ColorBgra* p2 = src2 + srcX2; ColorBgra* p3 = src3 + srcX3; ColorBgra* p4 = src4 + srcX4; int r = (2 + p1->R + p2->R + p3->R + p4->R) >> 2; int g = (2 + p1->G + p2->G + p3->G + p4->G) >> 2; int b = (2 + p1->B + p2->B + p3->B + p4->B) >> 2; int a = (2 + p1->A + p2->A + p3->A + p4->A) >> 2; // Blend it over the checkerboard background int v = ((checkerX ^ checkerY) & 8) * 8 + 191; a = a + (a >> 7); int vmia = v * (256 - a); r = ((r * a) + vmia) >> 8; g = ((g * a) + vmia) >> 8; b = ((b * a) + vmia) >> 8; dstPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + 0xff000000; ++dstPtr; } } } }
public static void RenderZoomOutRotatedGridMultisampling(Surface dst, Surface source, Point offset, Size destinationSize) { unsafe { const int fpShift = 12; const int fpFactor = (1 << fpShift); Size sourceSize = source.Size; long fDstLeftLong = ((long)offset.X * fpFactor * (long)sourceSize.Width) / (long)destinationSize.Width; long fDstTopLong = ((long)offset.Y * fpFactor * (long)sourceSize.Height) / (long)destinationSize.Height; long fDstRightLong = ((long)(offset.X + dst.Width) * fpFactor * (long)sourceSize.Width) / (long)destinationSize.Width; long fDstBottomLong = ((long)(offset.Y + dst.Height) * fpFactor * (long)sourceSize.Height) / (long)destinationSize.Height; int fDstLeft = (int)fDstLeftLong; int fDstTop = (int)fDstTopLong; int fDstRight = (int)fDstRightLong; int fDstBottom = (int)fDstBottomLong; int dx = (fDstRight - fDstLeft) / dst.Width; int dy = (fDstBottom - fDstTop) / dst.Height; for (int dstRow = 0, fDstY = fDstTop; dstRow < dst.Height && fDstY < fDstBottom; ++dstRow, fDstY += dy) { int srcY1 = fDstY >> fpShift; // y int srcY2 = (fDstY + (dy >> 2)) >> fpShift; // y + 0.25 int srcY3 = (fDstY + (dy >> 1)) >> fpShift; // y + 0.50 int srcY4 = (fDstY + (dy >> 1) + (dy >> 2)) >> fpShift; // y + 0.75 #if DEBUG Debug.Assert(source.IsRowVisible(srcY1)); Debug.Assert(source.IsRowVisible(srcY2)); Debug.Assert(source.IsRowVisible(srcY3)); Debug.Assert(source.IsRowVisible(srcY4)); Debug.Assert(dst.IsRowVisible(dstRow)); #endif ColorBgra *src1 = source.GetRowAddressUnchecked(srcY1); ColorBgra *src2 = source.GetRowAddressUnchecked(srcY2); ColorBgra *src3 = source.GetRowAddressUnchecked(srcY3); ColorBgra *src4 = source.GetRowAddressUnchecked(srcY4); ColorBgra *dstPtr = dst.GetRowAddressUnchecked(dstRow); int checkerY = dstRow + offset.Y; int checkerX = offset.X; int maxCheckerX = checkerX + dst.Width; for (int fDstX = fDstLeft; checkerX < maxCheckerX && fDstX < fDstRight; ++checkerX, fDstX += dx) { int srcX1 = (fDstX + (dx >> 2)) >> fpShift; // x + 0.25 int srcX2 = (fDstX + (dx >> 1) + (dx >> 2)) >> fpShift; // x + 0.75 int srcX3 = fDstX >> fpShift; // x int srcX4 = (fDstX + (dx >> 1)) >> fpShift; // x + 0.50 #if DEBUG Debug.Assert(source.IsColumnVisible(srcX1)); Debug.Assert(source.IsColumnVisible(srcX2)); Debug.Assert(source.IsColumnVisible(srcX3)); Debug.Assert(source.IsColumnVisible(srcX4)); #endif ColorBgra *p1 = src1 + srcX1; ColorBgra *p2 = src2 + srcX2; ColorBgra *p3 = src3 + srcX3; ColorBgra *p4 = src4 + srcX4; int r = (2 + p1->R + p2->R + p3->R + p4->R) >> 2; int g = (2 + p1->G + p2->G + p3->G + p4->G) >> 2; int b = (2 + p1->B + p2->B + p3->B + p4->B) >> 2; int a = (2 + p1->A + p2->A + p3->A + p4->A) >> 2; // Blend it over the checkerboard background int v = ((checkerX ^ checkerY) & 8) * 8 + 191; a = a + (a >> 7); int vmia = v * (256 - a); r = ((r * a) + vmia) >> 8; g = ((g * a) + vmia) >> 8; b = ((b * a) + vmia) >> 8; dstPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + 0xff000000; ++dstPtr; } } } }
private unsafe void DrawText(Surface dst, Font textFont, string text, Point pt, Size measuredSize, bool antiAliasing, Surface brush8x8) { Point pt2 = pt; Size measuredSize2 = measuredSize; int offset = (int)textFont.Height; pt.X -= offset; measuredSize.Width += 2 * offset; Rectangle dstRect = new Rectangle(pt, measuredSize); Rectangle dstRectClipped = Rectangle.Intersect(dstRect, ScratchSurface.Bounds); if (dstRectClipped.Width == 0 || dstRectClipped.Height == 0) { return; } // We only use the first 8,8 of brush using (RenderArgs renderArgs = new RenderArgs(this.ScratchSurface)) { renderArgs.Graphics.FillRectangle(Brushes.White, pt.X, pt.Y, measuredSize.Width, measuredSize.Height); if (measuredSize.Width > 0 && measuredSize.Height > 0) { using (Surface s2 = renderArgs.Surface.CreateWindow(dstRectClipped)) { using (RenderArgs renderArgs2 = new RenderArgs(s2)) { SystemLayer.Fonts.DrawText( renderArgs2.Graphics, this.font, text, new Point(dstRect.X - dstRectClipped.X + offset, dstRect.Y - dstRectClipped.Y), AppEnvironment.AntiAliasing, AppEnvironment.FontSmoothing); } } } // Mask out anything that isn't within the user's clip region (selected region) using (PdnRegion clip = Selection.CreateRegion()) { clip.Xor(renderArgs.Surface.Bounds); // invert clip.Intersect(new Rectangle(pt, measuredSize)); renderArgs.Graphics.FillRegion(Brushes.White, clip.GetRegionReadOnly()); } int skipX; if (pt.X < 0) { skipX = -pt.X; } else { skipX = 0; } int xEnd = Math.Min(dst.Width, pt.X + measuredSize.Width); bool blending = AppEnvironment.AlphaBlending; if (dst.IsColumnVisible(pt.X + skipX)) { for (int y = pt.Y; y < pt.Y + measuredSize.Height; ++y) { if (!dst.IsRowVisible(y)) { continue; } ColorBgra *dstPtr = dst.GetPointAddressUnchecked(pt.X + skipX, y); ColorBgra *srcPtr = ScratchSurface.GetPointAddress(pt.X + skipX, y); ColorBgra *brushPtr = brush8x8.GetRowAddressUnchecked(y & 7); for (int x = pt.X + skipX; x < xEnd; ++x) { ColorBgra srcPixel = *srcPtr; ColorBgra dstPixel = *dstPtr; ColorBgra brushPixel = brushPtr[x & 7]; int alpha = ((255 - srcPixel.R) * brushPixel.A) / 255; // we could use srcPixel.R, .G, or .B -- the choice here is arbitrary brushPixel.A = (byte)alpha; if (srcPtr->R == 255) // could use R, G, or B -- arbitrary choice { // do nothing -- leave dst alone } else if (alpha == 255 || !blending) { // copy it straight over *dstPtr = brushPixel; } else { // do expensive blending *dstPtr = UserBlendOps.NormalBlendOp.ApplyStatic(dstPixel, brushPixel); } ++dstPtr; ++srcPtr; } } } } }
/// <summary> /// Fits the source surface to this surface using nearest neighbor resampling. /// </summary> /// <param name="source">The surface to read pixels from.</param> /// <param name="dstRoi">The rectangle to clip rendering to.</param> public void NearestNeighborFitSurface(Surface source, Rectangle dstRoi) { Rectangle roi = Rectangle.Intersect(dstRoi, this.Bounds); unsafe { for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY) { int srcY = (dstY * source.height) / height; ColorBgra *srcRow = source.GetRowAddressUnchecked(srcY); ColorBgra *dstPtr = this.GetPointAddressUnchecked(roi.Left, dstY); for (int dstX = roi.Left; dstX < roi.Right; ++dstX) { int srcX = (dstX * source.width) / width; *dstPtr = *(srcRow + srcX); ++dstPtr; } } } }
/// <summary> /// Implements bicubic filtering with NO bounds checking at any pixel. /// </summary> public void BicubicFitSurfaceUnchecked(Surface source, Rectangle dstRoi) { if (this.width < 2 || this.height < 2 || source.width < 2 || source.height < 2) { SuperSamplingFitSurface(source, dstRoi); } else { unsafe { Rectangle roi = Rectangle.Intersect(dstRoi, this.Bounds); Rectangle roiIn = Rectangle.Intersect(dstRoi, new Rectangle(1, 1, width - 1, height - 1)); IntPtr rColCacheIP = Memory.Allocate(4 * (ulong)roi.Width * (ulong)sizeof(double)); double* rColCache = (double*)rColCacheIP.ToPointer(); // Precompute and then cache the value of R() for each column for (int dstX = roi.Left; dstX < roi.Right; ++dstX) { double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1); double srcColumnFloor = Math.Floor(srcColumn); double srcColumnFrac = srcColumn - srcColumnFloor; int srcColumnInt = (int)srcColumn; for (int m = -1; m <= 2; ++m) { int index = (m + 1) + ((dstX - roi.Left) * 4); double x = m - srcColumnFrac; rColCache[index] = R(x); } } // Set this up so we can cache the R()'s for every row double* rRowCache = stackalloc double[4]; for (int dstY = roi.Top; dstY < roi.Bottom; ++dstY) { double srcRow = (double)(dstY * (source.height - 1)) / (double)(height - 1); double srcRowFloor = Math.Floor(srcRow); double srcRowFrac = srcRow - srcRowFloor; int srcRowInt = (int)srcRow; ColorBgra *dstPtr = this.GetPointAddressUnchecked(roi.Left, dstY); // Compute the R() values for this row for (int n = -1; n <= 2; ++n) { double x = srcRowFrac - n; rRowCache[n + 1] = R(x); } rColCache = (double*)rColCacheIP.ToPointer(); ColorBgra *srcRowPtr = source.GetRowAddressUnchecked(srcRowInt - 1); for (int dstX = roi.Left; dstX < roi.Right; dstX++) { double srcColumn = (double)(dstX * (source.width - 1)) / (double)(width - 1); double srcColumnFloor = Math.Floor(srcColumn); double srcColumnFrac = srcColumn - srcColumnFloor; int srcColumnInt = (int)srcColumn; double blueSum = 0; double greenSum = 0; double redSum = 0; double alphaSum = 0; double totalWeight = 0; ColorBgra *srcPtr = srcRowPtr + srcColumnInt - 1; for (int n = 0; n <= 3; ++n) { double w0 = rColCache[0] * rRowCache[n]; double w1 = rColCache[1] * rRowCache[n]; double w2 = rColCache[2] * rRowCache[n]; double w3 = rColCache[3] * rRowCache[n]; double a0 = srcPtr[0].A; double a1 = srcPtr[1].A; double a2 = srcPtr[2].A; double a3 = srcPtr[3].A; alphaSum += (a0 * w0) + (a1 * w1) + (a2 * w2) + (a3 * w3); totalWeight += w0 + w1 + w2 + w3; blueSum += (a0 * srcPtr[0].B * w0) + (a1 * srcPtr[1].B * w1) + (a2 * srcPtr[2].B * w2) + (a3 * srcPtr[3].B * w3); greenSum += (a0 * srcPtr[0].G * w0) + (a1 * srcPtr[1].G * w1) + (a2 * srcPtr[2].G * w2) + (a3 * srcPtr[3].G * w3); redSum += (a0 * srcPtr[0].R * w0) + (a1 * srcPtr[1].R * w1) + (a2 * srcPtr[2].R * w2) + (a3 * srcPtr[3].R * w3); srcPtr = (ColorBgra *)((byte *)srcPtr + source.stride); } double alpha = alphaSum / totalWeight; double blue; double green; double red; if (alpha == 0) { blue = 0; green = 0; red = 0; } else { blue = blueSum / alphaSum; green = greenSum / alphaSum; red = redSum / alphaSum; // add 0.5 to ensure truncation to uint results in rounding alpha += 0.5; blue += 0.5; green += 0.5; red += 0.5; } dstPtr->Bgra = (uint)blue + ((uint)green << 8) + ((uint)red << 16) + ((uint)alpha << 24); ++dstPtr; rColCache += 4; } // for (dstX... } // for (dstY... Memory.Free(rColCacheIP); } // unsafe } }