private ActiveEdge CreateActiveEdge(Edge edge, int offX, float startPoint) { var z = new ActiveEdge(); // TODO: make a pool of these!!! float dxdy = (edge.x1 - edge.x0) / (edge.y1 - edge.y0); //STBTT_assert(e->y0 <= start_point); // round dx down to avoid going too far if (dxdy < 0) { z.dx = -(int)Math.Floor(FIX * -dxdy); } else { z.dx = (int)Math.Floor(FIX * dxdy); } z.x = (int)Math.Floor(FIX * (edge.x0 + dxdy * (startPoint - edge.y0))); z.x -= offX * FIX; z.ey = edge.y1; z.next = null; if (edge.invert) { z.valid = 1; } else { z.valid = -1; } return(z); }
// note: this routine clips fills that extend off the edges... // ideally this wouldn't happen, but it could happen if the truetype glyph bounding boxes are wrong, or if the user supplies a too-small bitmap private void FillActiveEdges(byte[] scanline, int len, ActiveEdge e, int max_weight) { // non-zero winding fill int x0 = 0; int w = 0; int x1; while (e != null) { if (w == 0) { // if we're currently at zero, we need to record the edge start point x0 = e.x; w += e.valid; } else { x1 = e.x; w += e.valid; // if we went to zero, we need to draw if (w == 0) { int i = x0 >> FIXSHIFT; int j = x1 >> FIXSHIFT; if (i < len && j >= 0) { if (i == j) { // x0,x1 are the same pixel, so compute combined coverage scanline[i] = (byte)(scanline[i] + (byte)(((x1 - x0) * max_weight) >> FIXSHIFT)); } else { if (i >= 0)// add antialiasing for x0 { scanline[i] = (byte)(scanline[i] + (byte)(((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT)); } else { i = -1; // clip } if (j < len) // add antialiasing for x1 { scanline[j] = (byte)(scanline[j] + (byte)(((x1 & FIXMASK) * max_weight) >> FIXSHIFT)); } else { j = len; // clip } i++; while (i < j) // fill pixels between x0 and x1 { scanline[i] = (byte)(scanline[i] + (byte)max_weight); i++; } } } } } e = e.next; } }
private void RasterizeSortedEdges(GlyphBitmap bitmap, List <Edge> e, int vSubSamples, int offX, int off_y) { int eIndex = 0; ActiveEdge active = null; int max_weight = 255 / vSubSamples; // weight per vertical scanline int y = off_y * vSubSamples; int n = e.Count - 1; var tempEdge = e[n]; tempEdge.y0 = (off_y + bitmap.Height) * vSubSamples + 1; e[n] = tempEdge; var scanline = new byte[bitmap.Width]; float scanY = 0; int j = 0; while (j < bitmap.Height) { for (int iii = 0; iii < bitmap.Width; iii++) { scanline[iii] = 0; } for (int s = 0; s < vSubSamples; s++) { // find center of pixel for this scanline scanY = y + 0.5f; // update all active edges; // remove all active edges that terminate before the center of this scanline var curr = active; ActiveEdge prev = null; while (curr != null) { if (curr.ey <= scanY) { // delete from list if (prev != null) { prev.next = curr.next; } else { active = curr.next; } curr = curr.next; } else { curr.x += curr.dx; // advance to position for current scanline prev = curr; curr = curr.next; // advance through list } } // resort the list if needed bool changed; do { changed = false; curr = active; prev = null; while (curr != null && curr.next != null) { var prox = curr.next; if (curr.x > prox.x) { if (prev == null) { active = prox; } else { prev.next = prox; } curr.next = prox.next; prox.next = curr; Console.WriteLine("Sorted " + curr.ey + " with " + prox.ey); changed = true; } prev = curr; curr = curr.next; // advance through list } } while (changed); // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline while (e[eIndex].y0 <= scanY) { if (e[eIndex].y1 > scanY) { var z = CreateActiveEdge(e[eIndex], offX, scanY); // find insertion point if (active == null) { active = z; } else if (z.x < active.x) // insert at front { z.next = active; active = z; } else { // find thing to insert AFTER var p = active; while (p.next != null && p.next.x < z.x) { p = p.next; } // at this point, p->next->x is NOT < z->x z.next = p.next; p.next = z; } } eIndex++; } // now process all active edges in XOR fashion if (active != null) { FillActiveEdges(scanline, bitmap.Width, active, max_weight); } y++; } for (int iii = 0; iii < bitmap.Width; iii++) { if (scanline[iii] > 0) // OPTIMIZATION? { int ofs = iii + j * bitmap.Width; bitmap.Pixels[ofs] = scanline[iii]; } } j++; } }