private static void WalkContours(List <GlyphPoint[]> contours, EdgeWorkspace workspace) { if (contours == null) { return; } foreach (var contour in contours) { RenderContour(workspace, contour); } }
[NotNull] private static EdgeWorkspace PrepareEdgeWorkspace(int width, int height) { var requiredBufferSize = width * height; var workspace = new EdgeWorkspace { Height = height, Width = width, Data = new byte[requiredBufferSize] }; return(workspace); }
private static void FillScans(EdgeWorkspace workspace) { if (workspace == null) { return; } var ymax = workspace.Height; var xmax = workspace.Width - 1; // space to look ahead var data = workspace.Data; var w = workspace.Width; for (int y = 0; y < ymax; y++) { int inside = 0; var ypos = y * w; for (int x = 0; x < xmax; x++) { var v = data[ypos + x]; var up = (v & DirUp) > 0; var dn = (v & DirDown) > 0; if (up && dn) { data[ypos + x] |= Inside; continue; } if (up) { inside = 1; } if (dn) { inside = 0; } if (!up && !dn && inside > 0) { data[ypos + x] |= Inside; } } } }
public const byte Dropout = 0x40; // pixel *might* be a small feature drop-out /// <summary> /// Render a glyph at the given scale. Result is a grid of flag values. /// </summary> /// <param name="glyph">Glyph to render</param> /// <param name="xScale">Scale factor</param> /// <param name="yScale">Scale factor</param> [NotNull] public static EdgeWorkspace Rasterise(Glyph glyph, float xScale, float yScale) { if (glyph == null) { return(EdgeWorkspace.Empty()); } if (glyph.GlyphType != GlyphTypes.Simple) { return(EdgeWorkspace.Empty()); } // glyph sizes are not reliable for this. glyph.GetPointBounds(out var xmin, out var xmax, out var ymin, out var ymax); var baseline = (float)(glyph.yMin * yScale); var leftShift = (float)(-glyph.xMin * xScale * 0.5); // I guess the 0.5 fudge-factor is due to lack of kerning support var width = (int)((xmax - xmin) * xScale) + 8; var height = (int)((ymax - ymin) * yScale) + 8; var workspace = PrepareEdgeWorkspace(width, height); // 1. Grid fit / adjust the contours var contours = GridFitContours(glyph, xScale, yScale, out var yAdjust); // 2. Walk around all the contours, setting scan-line winding data. WalkContours(contours, workspace); // also adds extra headroom for super sampling // 3. Run each scan line, filling where sum of winding is != 0 FillScans(workspace); //DiagnosticFillScans(workspace); // adjust the baseline here, to control 'jitter' caused by pixel-fitting if (baseline < 0) { yAdjust += 0.4f; // adjust for rounding } baseline += yAdjust; workspace.Baseline = baseline; workspace.Shift = leftShift; return(workspace); }
private static void RenderContour(EdgeWorkspace workspace, GlyphPoint[] contour) { if (contour == null) { return; } if (workspace == null) { return; } var len = contour.Length; for (int i = 0; i < len; i++) { var ptThis = contour[i % len]; var ptNext = contour[(i + 1) % len]; DirectionalBresenham(workspace, ptThis, ptNext); } }
// ReSharper disable once UnusedMember.Local private static void DiagnosticFillScans(EdgeWorkspace workspace) { if (workspace == null) { return; } var ymax = workspace.Height; var xmax = workspace.Width - 1; // space to look ahead var data = workspace.Data; for (int y = 0; y < ymax; y++) { var ypos = y * workspace.Width; for (int x = 0; x < xmax; x++) { if (data[ypos + x] != 0) { data[ypos + x] |= Inside; } } } }
/// <summary> /// Write directions between two points into the workspace. /// </summary> private static void DirectionalBresenham(EdgeWorkspace workspace, GlyphPoint start, GlyphPoint end) { if (workspace == null) { return; } var fdx = end.X - start.X; var fdy = end.Y - start.Y; var x0 = (int)start.X; var x1 = (int)end.X; var y0 = (int)start.Y + 1; var y1 = (int)end.Y + 1; int dx = x1 - x0, sx = x0 < x1 ? 1 : -1; int dy = y1 - y0, sy = y0 < y1 ? 1 : -1; if (dx < 0) { dx = -dx; } if (dy < 0) { dy = -dy; } byte xWindFlag = fdx < 0 ? DirLeft : DirRight; byte yWindFlag = fdy < 0 ? DirDown : DirUp; if (dy == 0) { yWindFlag = 0; } if (dx == 0) { xWindFlag = 0; } int pxFlag = yWindFlag | xWindFlag | Touched; // assume first pixel makes a full movement if (dy == 0 && dx == 0) { pxFlag |= Dropout; // a single pixel. We mark for drop-out protection } int err = (dx > dy ? dx : -dy) / 2; int w = workspace.Width; var data = workspace.Data; for (;;) // for each point, bit-OR our decided direction onto the pixel // set pixel { data[(y0 * w) + x0] |= (byte)pxFlag; // end of line check if (x0 == x1 && y0 == y1) { break; } pxFlag = Touched; var e2 = err; if (e2 > -dx) { err -= dy; x0 += sx; pxFlag |= xWindFlag; } if (e2 < dy) { err += dx; y0 += sy; pxFlag |= yWindFlag; } } }