private unsafe void RenderZoomIn(Cairo.ImageSurface src, Cairo.ImageSurface dst, Gdk.Point offset, bool checker) { ColorBgra *src_ptr = (ColorBgra *)src.DataPtr; ColorBgra *dst_ptr = (ColorBgra *)dst.DataPtr; int src_width = src.Width; int dst_width = dst.Width; int dst_height = dst.Height; if (!generated) { d2sLookupX = CreateLookupX(src_width, destination_size.Width, scale_factor); d2sLookupY = CreateLookupY(src.Height, destination_size.Height, scale_factor); s2dLookupX = CreateS2DLookupX(src_width, destination_size.Width, scale_factor); s2dLookupY = CreateS2DLookupY(src.Height, destination_size.Height, scale_factor); generated = true; } for (int dstRow = 0; dstRow < dst_height; ++dstRow) { int nnY = dstRow + offset.Y; int srcY = d2sLookupY[nnY]; ColorBgra *dstPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dstRow); ColorBgra *srcRow = src.GetRowAddressUnchecked(src_ptr, src_width, srcY); for (int dstCol = 0; dstCol < dst_width; ++dstCol) { int nnX = dstCol + offset.X; int srcX = d2sLookupX[nnX]; if (checker) { ColorBgra src2 = *(srcRow + srcX); int b = src2.B; int g = src2.G; int r = src2.R; int a = src2.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); } else { *dstPtr = *(srcRow + srcX); } ++dstPtr; } } }
private unsafe void RenderPixelGrid(Cairo.ImageSurface dst, Gdk.Point offset) { EnsureLookupTablesCreated(); // Draw horizontal lines var dst_ptr = (ColorBgra *)dst.DataPtr; int dstHeight = dst.Height; int dstWidth = dst.Width; int dstStride = dst.Stride; int sTop = d2sLookupY[offset.Y]; int sBottom = d2sLookupY[offset.Y + dstHeight]; for (int srcY = sTop; srcY <= sBottom; ++srcY) { int dstY = s2dLookupY[srcY]; int dstRow = dstY - offset.Y; if (dstRow >= 0 && dstRow < dstHeight) { ColorBgra *dstRowPtr = dst.GetRowAddressUnchecked(dst_ptr, dstWidth, dstRow); ColorBgra *dstRowEndPtr = dstRowPtr + dstWidth; dstRowPtr += offset.X & 1; while (dstRowPtr < dstRowEndPtr) { *dstRowPtr = ColorBgra.Black; dstRowPtr += 2; } } } // Draw vertical lines int sLeft = d2sLookupX[offset.X]; int sRight = d2sLookupX[offset.X + dstWidth]; for (int srcX = sLeft; srcX <= sRight; ++srcX) { int dstX = s2dLookupX[srcX]; int dstCol = dstX - offset.X; if (dstCol >= 0 && dstCol < dstWidth) { byte *dstColPtr = (byte *)dst.GetPointAddress(dstCol, 0); byte *dstColEndPtr = dstColPtr + dstStride * dstHeight; dstColPtr += (offset.Y & 1) * dstStride; while (dstColPtr < dstColEndPtr) { *((ColorBgra *)dstColPtr) = ColorBgra.Black; dstColPtr += 2 * dstStride; } } } }
private unsafe void RenderPixelGrid(Cairo.ImageSurface dst, Point offset) { // Draw horizontal lines var dst_ptr = (ColorBgra *)dst.DataPtr; var dstHeight = dst.Height; var dstWidth = dst.Width; var dstStride = dst.Stride; var sTop = D2SLookupY[offset.Y]; var sBottom = D2SLookupY[offset.Y + dstHeight]; for (var srcY = sTop; srcY <= sBottom; ++srcY) { var dstY = S2DLookupY[srcY]; var dstRow = dstY - offset.Y; if (dstRow >= 0 && dstRow < dstHeight) { var dstRowPtr = dst.GetRowAddressUnchecked(dst_ptr, dstWidth, dstRow); var dstRowEndPtr = dstRowPtr + dstWidth; dstRowPtr += offset.X & 1; while (dstRowPtr < dstRowEndPtr) { *dstRowPtr = ColorBgra.Black; dstRowPtr += 2; } } } // Draw vertical lines var sLeft = D2SLookupX[offset.X]; var sRight = D2SLookupX[offset.X + dstWidth]; for (var srcX = sLeft; srcX <= sRight; ++srcX) { var dstX = S2DLookupX[srcX]; var dstCol = dstX - offset.X; if (dstCol >= 0 && dstCol < dstWidth) { var dstColPtr = (byte *)dst.GetPointAddress(dstCol, 0); var dstColEndPtr = dstColPtr + dstStride * dstHeight; dstColPtr += (offset.Y & 1) * dstStride; while (dstColPtr < dstColEndPtr) { *((ColorBgra *)dstColPtr) = ColorBgra.Black; dstColPtr += 2 * dstStride; } } } }
private unsafe void RenderOneToOne(Cairo.ImageSurface src, Cairo.ImageSurface dst, Gdk.Point offset, bool checker) { Gdk.Rectangle srcRect = new Gdk.Rectangle(offset, dst.GetBounds().Size); srcRect.Intersect(src.GetBounds()); ColorBgra *src_ptr = (ColorBgra *)src.DataPtr; ColorBgra *dst_ptr = (ColorBgra *)dst.DataPtr; int src_width = src.Width; int dst_width = dst.Width; int dst_height = dst.Height; for (int dstRow = 0; dstRow < srcRect.Height; ++dstRow) { ColorBgra *dstRowPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dstRow); ColorBgra *srcRowPtr = src.GetPointAddressUnchecked(src_ptr, src_width, offset.X, dstRow + offset.Y); int dstCol = offset.X; int dstColEnd = offset.X + srcRect.Width; int checkerY = dstRow + offset.Y; while (dstCol < dstColEnd) { // Blend it over the checkerboard background if (checker) { int b = srcRowPtr->B; int g = srcRowPtr->G; int r = srcRowPtr->R; int a = srcRowPtr->A; 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); } else { *dstRowPtr = *srcRowPtr; } ++dstRowPtr; ++srcRowPtr; ++dstCol; } } }
public void Apply(Cairo.ImageSurface dst, Cairo.ImageSurface src) { if (dst.GetSize() != src.GetSize()) { 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); } } }
private unsafe void CopyScaledZoomIn(Cairo.ImageSurface src, Cairo.ImageSurface dst, Rectangle roi) { // Tell Cairo we need the latest raw data dst.Flush(); EnsureLookupTablesCreated(); // Cache pointers to surface raw data var src_ptr = (ColorBgra *)src.DataPtr; var dst_ptr = (ColorBgra *)dst.DataPtr; // Cache surface sizes var src_width = src.Width; var dst_width = dst.Width; var dst_height = dst.Height; for (var dst_row = 0; dst_row < dst_height; ++dst_row) { // For each dest row, look up the src row to copy from var nnY = dst_row + roi.Y; var srcY = d2sLookupY[nnY]; // Get pointers to src and dest rows var dst_row_ptr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dst_row); var src_row_ptr = src.GetRowAddressUnchecked(src_ptr, src_width, srcY); for (var dstCol = 0; dstCol < dst_width; ++dstCol) { // Look up the src column to copy from var nnX = dstCol + roi.X; var srcX = d2sLookupX[nnX]; // Copy source to destination *dst_row_ptr++ = *(src_row_ptr + srcX); } } // Tell Cairo we changed the raw data dst.MarkDirty(); }
public void Apply(Cairo.ImageSurface dst, Cairo.ImageSurface lhs, Cairo.ImageSurface rhs) { if (dst.GetSize() != lhs.GetSize()) { throw new ArgumentException("dst.Size != lhs.Size"); } if (lhs.GetSize() != rhs.GetSize()) { 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 = rhs.GetRowAddressUnchecked(y); Apply(dstPtr, lhsPtr, rhsPtr, dst.Width); } } }
private unsafe void CopyScaledZoomOut(Cairo.ImageSurface src, Cairo.ImageSurface dst, Rectangle roi) { // Tell Cairo we need the latest raw data dst.Flush(); const int fpShift = 12; const int fpFactor = (1 << fpShift); var source_size = src.GetBounds().Size; // Find destination bounds var dst_left = (int)(((long)roi.X * fpFactor * (long)source_size.Width) / (long)destination_size.Width); var dst_top = (int)(((long)roi.Y * fpFactor * (long)source_size.Height) / (long)destination_size.Height); var dst_right = (int)(((long)(roi.X + dst.Width) * fpFactor * (long)source_size.Width) / (long)destination_size.Width); var dst_bottom = (int)(((long)(roi.Y + dst.Height) * fpFactor * (long)source_size.Height) / (long)destination_size.Height); var dx = (dst_right - dst_left) / dst.Width; var dy = (dst_bottom - dst_top) / dst.Height; // Cache pointers to surface raw data and sizes var src_ptr = (ColorBgra *)src.DataPtr; var dst_ptr = (ColorBgra *)dst.DataPtr; var src_width = src.Width; var dst_width = dst.Width; var dst_height = dst.Height; for (int dstRow = 0, fDstY = dst_top; dstRow < dst_height && fDstY < dst_bottom; ++dstRow, fDstY += dy) { var srcY1 = fDstY >> fpShift; // y var srcY2 = (fDstY + (dy >> 2)) >> fpShift; // y + 0.25 var srcY3 = (fDstY + (dy >> 1)) >> fpShift; // y + 0.50 var srcY4 = (fDstY + (dy >> 1) + (dy >> 2)) >> fpShift; // y + 0.75 var src1 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY1); var src2 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY2); var src3 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY3); var src4 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY4); var dstPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dstRow); var checkerY = dstRow + roi.Y; var checkerX = roi.X; var maxCheckerX = checkerX + dst.Width; for (var fDstX = dst_left; checkerX < maxCheckerX && fDstX < dst_right; ++checkerX, fDstX += dx) { var srcX1 = (fDstX + (dx >> 2)) >> fpShift; // x + 0.25 var srcX2 = (fDstX + (dx >> 1) + (dx >> 2)) >> fpShift; // x + 0.75 var srcX3 = fDstX >> fpShift; // x var srcX4 = (fDstX + (dx >> 1)) >> fpShift; // x + 0.50 var p1 = src1 + srcX1; var p2 = src2 + srcX2; var p3 = src3 + srcX3; var p4 = src4 + srcX4; var r = (2 + p1->R + p2->R + p3->R + p4->R) >> 2; var g = (2 + p1->G + p2->G + p3->G + p4->G) >> 2; var b = (2 + p1->B + p2->B + p3->B + p4->B) >> 2; var a = (2 + p1->A + p2->A + p3->A + p4->A) >> 2; // Copy color to destination *dstPtr++ = ColorBgra.FromUInt32((uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)a << 24)); } } // Tell Cairo we changed the raw data dst.MarkDirty(); }
public unsafe void Render(Cairo.ImageSurface dst, Gdk.Point offset) { if (cr.ScaleFactor > new ScaleFactor(1, 2)) { return; } int[] d2SLookupX = cr.Dst2SrcLookupX; int[] d2SLookupY = cr.Dst2SrcLookupY; int[] s2DLookupX = cr.Src2DstLookupX; int[] s2DLookupY = cr.Src2DstLookupY; ColorBgra[] blackAndWhite = new ColorBgra[2] { ColorBgra.White, ColorBgra.Black }; // draw horizontal lines int dstHeight = dst.Height; int dstWidth = dst.Width; int dstStride = dst.Stride; int sTop = d2SLookupY[offset.Y]; int sBottom = d2SLookupY[offset.Y + dstHeight]; dst.Flush(); for (int srcY = sTop; srcY <= sBottom; ++srcY) { int dstY = s2DLookupY[srcY]; int dstRow = dstY - offset.Y; if (dstRow >= 0 && dstRow < dstHeight) { ColorBgra *dstRowPtr = dst.GetRowAddressUnchecked(dstRow); ColorBgra *dstRowEndPtr = dstRowPtr + dstWidth; dstRowPtr += offset.X & 1; while (dstRowPtr < dstRowEndPtr) { *dstRowPtr = ColorBgra.Black; dstRowPtr += 2; } } } // draw vertical lines int sLeft = d2SLookupX[offset.X]; int sRight = d2SLookupX[offset.X + dstWidth]; for (int srcX = sLeft; srcX <= sRight; ++srcX) { int dstX = s2DLookupX[srcX]; int dstCol = dstX - offset.X; if (dstCol >= 0 && dstCol < dstWidth) { byte *dstColPtr = (byte *)dst.GetPointAddress(dstCol, 0); byte *dstColEndPtr = dstColPtr + dstStride * dstHeight; dstColPtr += (offset.Y & 1) * dstStride; while (dstColPtr < dstColEndPtr) { *((ColorBgra *)dstColPtr) = ColorBgra.Black; dstColPtr += 2 * dstStride; } } } dst.MarkDirty(); }
public unsafe void Export(Document document, string fileName, Window parent) { int w = 64, h = 64; //Default value to configure, pixels per block string chars = DefaultCharacters.AsciiChars; //Get the selection from config ASCIIOptionsDialog dialog = new ASCIIOptionsDialog(w, h); try { dialog.WindowPosition = Gtk.WindowPosition.CenterOnParent; int response = dialog.Run(); if (response == (int)Gtk.ResponseType.Ok) { w = dialog.ExportImageWidth; h = dialog.ExportImageHeight; chars = dialog.ExportImageChars; } else { return; } } finally { dialog.Destroy(); } Cairo.ImageSurface surface = document.GetFlattenedImage(); int W = surface.Width, H = surface.Height; // Special case where 1 cell corresponds directly to 1 pixel // Written separately for performance if (h == 1 && w == 1) { StreamWriter writer = new StreamWriter(fileName); for (int y = 0; y < H; y++) { ColorBgra *current = surface.GetRowAddressUnchecked(y); for (int x = 0; x < W; x++) { int pos = (int)((1 - current->GetIntensity()) * chars.Length); char c = chars[pos == chars.Length ? pos - 1 : pos]; writer.Write(c); current++; } if (y != H - 1) { writer.WriteLine(); } } writer.Flush(); } else { double[,] totals = new double[W / w, H / h]; for (int y = 0; y < H / h * h; y++) { ColorBgra *current = surface.GetRowAddressUnchecked(y); for (int x = 0; x < W / w * w; x++) { totals[x / w, y / h] += 1 - current->GetIntensity(); current++; } } int ppc = w * h; StreamWriter writer = new StreamWriter(fileName); for (int y = 0; y < H / h; y++) { for (int x = 0; x < W / w; x++) { int pos = (int)(totals[x, y] / ppc * chars.Length); char c = chars[pos == chars.Length ? pos - 1 : pos]; writer.Write(c); } if (y != H / h - 1) { writer.WriteLine(); } } writer.Flush(); } }
private unsafe void RenderZoomOut(List <Layer> layers, Cairo.ImageSurface dst, Gdk.Point offset, Gdk.Size destinationSize) { // The first layer should be blended with the transparent checkerboard var checker = true; CheckerBoardOperation checker_op = null; for (int i = 0; i < layers.Count; i++) { var layer = layers[i]; // If we're in LivePreview, substitute current layer with the preview layer if (layer == PintaCore.Layers.CurrentLayer && PintaCore.LivePreview.IsEnabled) { layer = CreateLivePreviewLayer(layer); } // If the layer is offset, handle it here if (!layer.Offset.IsEmpty()) { layer = CreateOffsetLayer(layer); } var src = layer.Surface; // Get the blend mode for this layer and opacity var blend_op = UserBlendOps.GetBlendOp(layer.BlendMode, layer.Opacity); if (checker) { checker_op = new CheckerBoardOperation(layer.Opacity); } const int fpShift = 12; const int fpFactor = (1 << fpShift); Gdk.Size sourceSize = src.GetBounds().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; ColorBgra *src_ptr = (ColorBgra *)src.DataPtr; ColorBgra *dst_ptr = (ColorBgra *)dst.DataPtr; int src_width = src.Width; int dst_width = dst.Width; 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 ColorBgra *src1 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY1); ColorBgra *src2 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY2); ColorBgra *src3 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY3); ColorBgra *src4 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY4); ColorBgra *dstPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, 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 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 if (checker) { *dstPtr = checker_op.Apply(ColorBgra.FromUInt32((uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)a << 24)), checkerX, checkerY); } else { *dstPtr = blend_op.Apply(*dstPtr, ColorBgra.FromUInt32((uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)a << 24))); } ++dstPtr; } } // Only checker the first layer checker = false; } }
private unsafe void RenderZoomIn(List <Layer> layers, Cairo.ImageSurface dst, Gdk.Point offset) { // The first layer should be blended with the transparent checkerboard var checker = true; CheckerBoardOperation checker_op = null; for (int i = 0; i < layers.Count; i++) { var layer = layers[i]; // If we're in LivePreview, substitute current layer with the preview layer if (layer == PintaCore.Layers.CurrentLayer && PintaCore.LivePreview.IsEnabled) { layer = CreateLivePreviewLayer(layer); } // If the layer is offset, handle it here if (!layer.Offset.IsEmpty()) { layer = CreateOffsetLayer(layer); } var src = layer.Surface; // Get the blend mode for this layer and opacity var blend_op = UserBlendOps.GetBlendOp(layer.BlendMode, layer.Opacity); if (checker) { checker_op = new CheckerBoardOperation(layer.Opacity); } ColorBgra *src_ptr = (ColorBgra *)src.DataPtr; ColorBgra *dst_ptr = (ColorBgra *)dst.DataPtr; int src_width = src.Width; int dst_width = dst.Width; int dst_height = dst.Height; if (!generated) { d2sLookupX = CreateLookupX(src_width, destination_size.Width, scale_factor); d2sLookupY = CreateLookupY(src.Height, destination_size.Height, scale_factor); s2dLookupX = CreateS2DLookupX(src_width, destination_size.Width, scale_factor); s2dLookupY = CreateS2DLookupY(src.Height, destination_size.Height, scale_factor); generated = true; } for (int dstRow = 0; dstRow < dst_height; ++dstRow) { int nnY = dstRow + offset.Y; int srcY = d2sLookupY[nnY]; ColorBgra *dstPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dstRow); ColorBgra *srcRow = src.GetRowAddressUnchecked(src_ptr, src_width, srcY); for (int dstCol = 0; dstCol < dst_width; ++dstCol) { int nnX = dstCol + offset.X; int srcX = d2sLookupX[nnX]; // Blend it over the checkerboard background if (checker) { *dstPtr = checker_op.Apply(*(srcRow + srcX), dstCol + offset.X, dstRow + offset.Y); } else { *dstPtr = blend_op.Apply(*dstPtr, *(srcRow + srcX)); } ++dstPtr; } } // Only checker the first layer checker = false; } // If we are at least 200% and grid is requested, draw it if (PintaCore.Actions.View.PixelGrid.Active && scale_factor.Ratio <= 0.5d) { RenderPixelGrid(dst, offset); } }
private unsafe void RenderOneToOne(List <Layer> layers, Cairo.ImageSurface dst, Gdk.Point offset) { // The first layer should be blended with the transparent checkerboard var checker = true; CheckerBoardOperation checker_op = null; for (int i = 0; i < layers.Count; i++) { var layer = layers[i]; // If we're in LivePreview, substitute current layer with the preview layer if (layer == PintaCore.Layers.CurrentLayer && PintaCore.LivePreview.IsEnabled) { layer = CreateLivePreviewLayer(layer); } // If the layer is offset, handle it here if (!layer.Offset.IsEmpty()) { layer = CreateOffsetLayer(layer); } var src = layer.Surface; // Get the blend mode for this layer and opacity var blend_op = UserBlendOps.GetBlendOp(layer.BlendMode, layer.Opacity); if (checker) { checker_op = new CheckerBoardOperation(layer.Opacity); } // Figure out where our source and destination intersect var srcRect = new Gdk.Rectangle(offset, dst.GetBounds().Size); srcRect.Intersect(src.GetBounds()); // Get pointers to our surfaces var src_ptr = (ColorBgra *)src.DataPtr; var dst_ptr = (ColorBgra *)dst.DataPtr; // Cache widths int src_width = src.Width; int dst_width = dst.Width; for (int dstRow = 0; dstRow < srcRect.Height; ++dstRow) { ColorBgra *dstRowPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, dstRow); ColorBgra *srcRowPtr = src.GetPointAddressUnchecked(src_ptr, src_width, offset.X, dstRow + offset.Y); int dstCol = offset.X; int dstColEnd = offset.X + srcRect.Width; int checkerY = dstRow + offset.Y; while (dstCol < dstColEnd) { // Blend it over the checkerboard background if (checker) { *dstRowPtr = checker_op.Apply(*srcRowPtr, dstCol, checkerY); } else { *dstRowPtr = blend_op.Apply(*dstRowPtr, *srcRowPtr); } ++dstRowPtr; ++srcRowPtr; ++dstCol; } } // Only checker the first layer checker = false; } }
private unsafe void RenderZoomOut(Cairo.ImageSurface src, Cairo.ImageSurface dst, Gdk.Point offset, Gdk.Size destinationSize, bool checker) { const int fpShift = 12; const int fpFactor = (1 << fpShift); Gdk.Size sourceSize = src.GetBounds().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; ColorBgra *src_ptr = (ColorBgra *)src.DataPtr; ColorBgra *dst_ptr = (ColorBgra *)dst.DataPtr; int src_width = src.Width; int dst_width = dst.Width; 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 ColorBgra *src1 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY1); ColorBgra *src2 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY2); ColorBgra *src3 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY3); ColorBgra *src4 = src.GetRowAddressUnchecked(src_ptr, src_width, srcY4); ColorBgra *dstPtr = dst.GetRowAddressUnchecked(dst_ptr, dst_width, 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 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; if (checker) { // 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; } else { dstPtr->Bgra = (uint)b + ((uint)g << 8) + ((uint)r << 16) + ((uint)a << 24); } ++dstPtr; } } }