public void Scan(Scanner scanner, Rectangle clip) { scanner.yMin = int.MaxValue; scanner.yMax = int.MinValue; for(int i = 0; i < Vertices.Length; i++) { scanner.yMin = Math.Min(scanner.yMin, (int)Vertices[i].Y); scanner.yMax = Math.Max(scanner.yMax, (int)Vertices[i].Y); } //clip to clip, but with leeway if(scanner.yMin < clip.Min.Y - 1) scanner.yMin = clip.Min.Y - 1; if(scanner.yMax > clip.Max.Y + 1) scanner.yMax = clip.Max.Y + 1; for(int y = scanner.yMin; y <= scanner.yMax; y++) { scanner[y] = new Scanner.Scan{min = float.PositiveInfinity, max = float.NegativeInfinity}; } for(int i = 0; i < Vertices.Length; i++) { ScanLine(scanner, Vertices[i], Vertices[(i + 1) % Vertices.Length]); } //true clip if(scanner.yMin < clip.Min.Y) { scanner.yMin = clip.Min.Y; scanner.isYMinClipped = true; } if(scanner.yMax > clip.Max.Y) { scanner.yMax = clip.Max.Y; scanner.isYMaxClipped = true; } }
public void Scan(Scanner scanner, Rectangle clip) { scanner.yMin = Rect.Min.Y; scanner.yMax = Rect.Max.Y; //clip to clip, but with leeway if(scanner.yMin < clip.Min.Y - 1) scanner.yMin = clip.Min.Y - 1; if(scanner.yMax > clip.Max.Y + 1) scanner.yMax = clip.Max.Y + 1; for(int y = scanner.yMin; y <= scanner.yMax; y++) { if(y < Rect.Min.Y + Radii.Y) { double dy = (Rect.Min.Y + Radii.Y) - y; double dx = Math.Sqrt(1 - (dy * dy) / (Radii.Y * Radii.Y)) * Radii.X; dx -= Radii.X; scanner[y] = new Scanner.Scan{min = Rect.Min.X - (float)dx, max = Rect.Max.X + (float)dx}; }else if(y > Rect.Max.Y - Radii.Y) { double dy = y - (Rect.Max.Y - Radii.Y); double dx = Math.Sqrt(1 - (dy * dy) / (Radii.Y * Radii.Y)) * Radii.X; dx -= Radii.X; scanner[y] = new Scanner.Scan{min = Rect.Min.X - (float)dx, max = Rect.Max.X + (float)dx}; }else { scanner[y] = new Scanner.Scan{min = Rect.Min.X, max = Rect.Max.X}; } } //true clip if(scanner.yMin < clip.Min.Y) { scanner.yMin = clip.Min.Y; scanner.isYMinClipped = true; } if(scanner.yMax > clip.Max.Y) { scanner.yMax = clip.Max.Y; scanner.isYMaxClipped = true; } }
public void Scan(Scanner scanner, Rectangle clip) { ((Ellipse)this).Scan(scanner, clip); }
public void Scan(Scanner scanner, Rectangle clip) { new Rectangle{Min = Min.Round(), Max = Max.Round()}.Scan(scanner, clip); }
/// <summary> /// Basic implementation of a constant src solid filling renderer. /// </summary> /// <param name="clip">The clipping rectangle.</param> /// <param name="scanner">The scans to render.</param> /// <param name="dest">The map to render to.</param> /// <param name="value">The constant value.</param> /// <param name="mode">The color blending mode to use.</param> /// <param name="isAA">True if anti-aliasing is enabled.</param> public static void SolidConstRender(Rectangle clip, Scanner scanner, DataMap<ARGB> dest, ARGB value, ColorMode mode = ColorMode.NORMAL, bool isAA = true) { //for constants, we can early return on blend and mask when alpha is 0 if((mode == ColorMode.BLEND || mode == ColorMode.MASK) && value.A == 0) return; //mask at non-zero is equivalent to normal if(mode == ColorMode.MASK) mode = ColorMode.NORMAL; //blend at opaque is equivalent to normal if(mode == ColorMode.BLEND && value.A == 255) mode = ColorMode.NORMAL; IUnsafeMap uDest = dest as IUnsafeMap; if(uDest == null) { throw new InvalidOperationException("Destination image does not support advanced color rendering!"); } byte* addr = uDest.BeginUnsafeOperation(); int width = dest.Width; int stride = uDest.GetStride(); for(int y = scanner.yMin; y <= scanner.yMax; y++) { double x1 = Max(scanner[y].min, clip.Min.X); double x2 = Min(scanner[y].max, clip.Max.X); //Anti-Aliasing variables //x1 vars are x min side //x2 vars are x max side //xb vars are y min side //xa vars are y max side double x1b = 0, x1a = 0; double x2b = 0, x2a = 0; int left; int right; if(isAA) { bool hasMin = false, hasMax = false; //if not at ymin or ymin is a clipped min (aka we have another "ghost" scan) if(y > scanner.yMin || scanner.isYMinClipped) { x1b = Max(scanner[y - 1].min, clip.Min.X); x2b = Min(scanner[y - 1].max, clip.Max.X); x1b = (x1b + x1) / 2; x2b = (x2b + x2) / 2; hasMin = true; } //if not at ymax or ymax is a clipped max (aka we have another "ghost" scan) if(y < scanner.yMax || scanner.isYMaxClipped) { x1a = Max(scanner[y + 1].min, clip.Min.X); x2a = Min(scanner[y + 1].max, clip.Max.X); x1a = (x1a + x1) / 2; x2a = (x2a + x2) / 2; hasMax = true; } if(!hasMin) { //if single line shape, just make everything literal if(!hasMax) { x1b = x1a = x1; x2b = x2a = x2; }else//if at ymin, extrapolate max->current into current->min { x1b = x1 - (x1a - x1); x2b = x2 - (x2a - x2); } }else//if at ymax, extrapolate min->current into current->max if(!hasMax) { x1a = x1 + (x1 - x1b); x2a = x2 + (x2 - x2b); } //if aa, round toward center left = (int)Math.Ceiling(Max(x1b, x1a)); right = (int)Math.Floor(Min(x2b, x2a)); }else { //if not aa, round away from center to match default renderer left = (int)Math.Floor(x1); right = (int)Math.Ceiling(x2); } //if valid center if(left <= right) { //solid fill with blend modes byte* ptr = addr + (left + (y * width)) * stride; byte* endPtr = ptr + (right - left + 1) * stride; switch(mode) { case ColorMode.BLEND: { //precalc value ARGB preValue = new ARGB{A = (byte)(255 - value.A), R = (byte)((value.R * value.A) >> 8), G = (byte)((value.G * value.A) >> 8), B = (byte)((value.B * value.A) >> 8)}; while(ptr != endPtr) { //*((ARGB*)ptr) &= value; //manually inline blending ARGB color = *(ARGB*)ptr; color = new ARGB{A = (byte)(value.A + color.A), R = (byte)(preValue.R + ((color.R * preValue.A) >> 8)), G = (byte)(preValue.G + ((color.G * preValue.A) >> 8)), B = (byte)(preValue.B + ((color.B * preValue.A) >> 8))}; *(ARGB*)ptr = color; ptr += stride; } break; } case ColorMode.NORMAL: { while(ptr != endPtr) { *((ARGB*)ptr) = value; ptr += stride; } break; } } } //if aa, add smoothing to edges if(isAA) { int center = (left + right) / 2; if(Min(x1b, x1a) != left) AAEdgeConst(addr, width, stride, x1b, x1a, y, value, true, clipMax: Min(center, clip.Max.X)); if(Max(x2b, x2a) != right) AAEdgeConst(addr, width, stride, x2b + 1, x2a + 1, y, value, false, clipMin: Max(center + 1, clip.Min.X)); } } uDest.EndUnsafeOperation(); }
/// <summary> /// Basic implementation of a map src solid filling renderer. /// </summary> /// <param name="clip">The clipping rectangle.</param> /// <param name="scanner">The scans to render.</param> /// <param name="dest">The map to render to.</param> /// <param name="src">The source map.</param> /// <param name="offset">The offset of the source map.</param> /// <param name="mode">The color blending mode to use.</param> /// <param name="isAA">True if anti-aliasing is enabled.</param> public static void SolidCopyRender(Rectangle clip, Scanner scanner, DataMap<ARGB> dest, DataMap<ARGB> src, Point2D offset, ColorMode mode = ColorMode.NORMAL, bool isAA = true) { IUnsafeMap uDest = dest as IUnsafeMap; IUnsafeMap uSrc = src as IUnsafeMap; if(uDest == null) { throw new InvalidOperationException("Destination image does not support advanced color rendering!"); } if(uSrc == null) { throw new InvalidOperationException("Source image does not support advanced color rendering!"); } byte* addr = uDest.BeginUnsafeOperation(); int width = dest.Width; int stride = uDest.GetStride(); byte* srcAddr = uSrc.BeginUnsafeOperation(); int srcWidth = src.Width; int srcStride = uSrc.GetStride(); for(int y = scanner.yMin; y <= scanner.yMax; y++) { double x1 = Max(scanner[y].min, clip.Min.X); double x2 = Min(scanner[y].max, clip.Max.X); //Anti-Aliasing variables //x1 vars are x min side //x2 vars are x max side //xb vars are y min side //xa vars are y max side double x1b = 0, x1a = 0; double x2b = 0, x2a = 0; int left; int right; if(isAA) { bool hasMin = false, hasMax = false; //if not at ymin or ymin is a clipped min (aka we have another "ghost" scan) if(y > scanner.yMin || scanner.isYMinClipped) { x1b = Max(scanner[y - 1].min, clip.Min.X); x2b = Min(scanner[y - 1].max, clip.Max.X); x1b = (x1b + x1) / 2; x2b = (x2b + x2) / 2; hasMin = true; } //if not at ymax or ymax is a clipped max (aka we have another "ghost" scan) if(y < scanner.yMax || scanner.isYMaxClipped) { x1a = Max(scanner[y + 1].min, clip.Min.X); x2a = Min(scanner[y + 1].max, clip.Max.X); x1a = (x1a + x1) / 2; x2a = (x2a + x2) / 2; hasMax = true; } if(!hasMin) { //if single line shape, just make everything literal if(!hasMax) { x1b = x1a = x1; x2b = x2a = x2; }else//if at ymin, extrapolate max->current into current->min { x1b = x1 - (x1a - x1); x2b = x2 - (x2a - x2); } }else//if at ymax, extrapolate min->current into current->max if(!hasMax) { x1a = x1 + (x1 - x1b); x2a = x2 + (x2 - x2b); } //if aa, round toward center, otherwise round away to match default renderer left = (int)Math.Ceiling(Max(x1b, x1a)); right = (int)Math.Floor(Min(x2b, x2a)); }else { //if not aa, round away to match default renderer left = (int)Math.Floor(x1); right = (int)Math.Ceiling(x2); } //if valid center if(left <= right) { //solid fill with blend modes byte* ptr = addr + (left + (y * width)) * stride; byte* srcPtr = srcAddr + ((left + offset.X) + ((y + offset.Y) * srcWidth)) * srcStride; byte* endPtr = ptr + (right - left + 1) * stride; switch(mode) { case ColorMode.BLEND: { while(ptr != endPtr) { //*((ARGB*)ptr) &= *((ARGB*)srcPtr); //manually inline blending ARGB srcColor = *(ARGB*)srcPtr; ARGB color = *(ARGB*)ptr; int aComp = 255 - srcColor.A; color = new ARGB{A = (byte)(srcColor.A + color.A), R = (byte)(((srcColor.R * srcColor.A) + (color.R * aComp)) >> 8), G = (byte)(((srcColor.G * srcColor.A) + (color.G * aComp)) >> 8), B = (byte)(((srcColor.B * srcColor.A) + (color.B * aComp)) >> 8)}; *(ARGB*)ptr = color; ptr += stride; srcPtr += srcStride; } break; } case ColorMode.NORMAL: { while(ptr != endPtr) { *((ARGB*)ptr) = *((ARGB*)srcPtr); ptr += stride; srcPtr += srcStride; } break; } case ColorMode.MASK: { while(ptr != endPtr) { if((*((ARGB*)srcPtr)).A != 0) *((ARGB*)ptr) = *((ARGB*)srcPtr); ptr += stride; srcPtr += srcStride; } break; } } } //if aa, add smoothing to edges if(isAA) { int center = (left + right) / 2; if(Min(x1b, x1a) != left) AAEdgeCopy(addr, width, stride, x1b, x1a, y, srcAddr, srcWidth, srcStride, offset, true, clipMax: Min(center, clip.Max.X)); if(Max(x2b, x2a) != right) AAEdgeCopy(addr, width, stride, x2b + 1, x2a + 1, y, srcAddr, srcWidth, srcStride, offset, false, clipMin: Max(center + 1, clip.Min.X)); } } uDest.EndUnsafeOperation(); uSrc.EndUnsafeOperation(); }
public void Scan(Scanner scanner, Rectangle clip) { Point2D center = (Point2D)Position.Round(); scanner.yMin = center.Y - (int)Radii.Y; scanner.yMax = center.Y + (int)Radii.Y; //clip to clip, but with leeway if(scanner.yMin < clip.Min.Y - 1) scanner.yMin = clip.Min.Y - 1; if(scanner.yMax > clip.Max.Y + 1) scanner.yMax = clip.Max.Y + 1; for(int y = scanner.yMin; y <= scanner.yMax; y++) { double dy = center.Y - y; double dx = Math.Sqrt(1 - (dy * dy) / (Radii.Y * Radii.Y)) * Radii.X; scanner[y] = new Scanner.Scan{min = center.X - (float)dx, max = center.X + (float)dx}; } //true clip if(scanner.yMin < clip.Min.Y) { scanner.yMin = clip.Min.Y; scanner.isYMinClipped = true; } if(scanner.yMax > clip.Max.Y) { scanner.yMax = clip.Max.Y; scanner.isYMaxClipped = true; } }
/// <summary> /// Returns a scanner for the given height. /// </summary> /// <param name="height">The height of the target.</param> /// <returns>A scanner obj.</returns> public static Scanner Get(int height) { lock(scanners) { foreach(var node in scanners.GetNodes()) { if(node.Value.Length >= height) { scanners.Remove(node); return node.Value; } } } Scanner result = scanners.Pop(); if(result == null) result = new Scanner(); result.EnsureSize(height); return result; }
/// <summary> /// Returns a scanner to the pool after it's done being used. /// </summary> /// <param name="scanner">The scanner to return.</param> public static void Put(Scanner scanner) { scanners.AddSorted(scanner, (a, b) => a.Length - b.Length); }
private void ScanLine(Scanner scanner, Vec2D v1, Vec2D v2) { if((int)v1.Y == (int)v2.Y) { int ly = (int)v1.Y; if(ly > scanner.yMin && ly < scanner.yMax) { double minX = Math.Min(v1.X, v2.X); double maxX = Math.Max(v1.X, v2.X); Scanner.Scan scan = scanner[ly]; scan.min = Math.Min(scan.min, (float)minX); scan.max = Math.Max(scan.max, (float)maxX); scanner[ly] = scan; } return; } //v1.Y always less than v2.Y if(v1.Y > v2.Y) { Util.Swap(ref v1, ref v2); } //skip if out of bounds if(v2.Y < scanner.yMin || v1.Y > scanner.yMax) return; double x = v1.X; double dx = (v2.X - v1.X) / ((int)v2.Y - (int)v1.Y); int y = (int)v1.Y; int maxY = Math.Min((int)v2.Y, scanner.yMax); //start at beginning if(y < scanner.yMin) { x += dx * (scanner.yMin - y); y = scanner.yMin; } for(; y <= maxY; y++, x += dx) { Scanner.Scan scan = scanner[y]; scan.min = Math.Min(scan.min, (float)x); scan.max = Math.Max(scan.max, (float)x); scanner[y] = scan; } }