func drawGlyphOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform, mask *image.Alpha, mp image.Point) { i0 := dst.PixOffset(r.Min.X, r.Min.Y) i1 := i0 + r.Dx()*4 mi0 := mask.PixOffset(mp.X, mp.Y) sr, sg, sb, sa := src.RGBA() for y, my := r.Min.Y, mp.Y; y != r.Max.Y; y, my = y+1, my+1 { for i, mi := i0, mi0; i < i1; i, mi = i+4, mi+1 { ma := uint32(mask.Pix[mi]) if ma == 0 { continue } ma |= ma << 8 dr := uint32(dst.Pix[i+0]) dg := uint32(dst.Pix[i+1]) db := uint32(dst.Pix[i+2]) da := uint32(dst.Pix[i+3]) // The 0x101 is here for the same reason as in drawRGBA. a := (m - (sa * ma / m)) * 0x101 dst.Pix[i+0] = uint8((dr*a + sr*ma) / m >> 8) dst.Pix[i+1] = uint8((dg*a + sg*ma) / m >> 8) dst.Pix[i+2] = uint8((db*a + sb*ma) / m >> 8) dst.Pix[i+3] = uint8((da*a + sa*ma) / m >> 8) } i0 += dst.Stride i1 += dst.Stride mi0 += mask.Stride } }
func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpSrc(dst *image.Alpha, r image.Rectangle) { // TODO: non-zero vs even-odd winding? if r == dst.Bounds() && r == z.Bounds() { // We bypass the z.accumulateMask step and convert straight from // z.bufF32 or z.bufU32 to dst.Pix. if z.useFloatingPointMath { if haveFloatingAccumulateSIMD { floatingAccumulateOpSrcSIMD(dst.Pix, z.bufF32) } else { floatingAccumulateOpSrc(dst.Pix, z.bufF32) } } else { if haveFixedAccumulateSIMD { fixedAccumulateOpSrcSIMD(dst.Pix, z.bufU32) } else { fixedAccumulateOpSrc(dst.Pix, z.bufU32) } } return } z.accumulateMask() pix := dst.Pix[dst.PixOffset(r.Min.X, r.Min.Y):] for y, y1 := 0, r.Max.Y-r.Min.Y; y < y1; y++ { for x, x1 := 0, r.Max.X-r.Min.X; x < x1; x++ { ma := z.bufU32[y*z.size.X+x] // This formula is like rasterizeOpSrc's, simplified for the // concrete dst type and opaque src assumption. pix[y*dst.Stride+x] = uint8(ma >> 8) } } }
func (r *Rasterizer) Fill(img *image.Alpha, p Polygon, nonZeroWindingRule bool) { r.table = make([]*Intersection, img.Bounds().Dy()) var xmin, ymin, xmax, ymax float64 xmin = p[0] ymin = p[1] xmax = xmin ymax = ymin var x, y float64 for i := 2; i < len(p); i += 2 { x, y = p[i], p[i+1] if x > xmax { xmax = x } else if x < xmin { xmin = x } if y > ymax { ymax = y } else if y < ymin { ymin = y } } prevX, prevY := p[0], p[1] for i := 2; i < len(p); i += 2 { x, y = p[i], p[i+1] r.edge(prevX, prevY, x, y) prevX, prevY = x, y } r.edge(x, y, p[0], p[1]) if nonZeroWindingRule { r.scanNonZero(img, int(ymin), int(ymax+0.5)) } else { r.scanEvenOdd(img, int(ymin), int(ymax+0.5)) } }
func newAtFuncAlpha(p *image.Alpha) AtFunc { return func(x, y int) (r, g, b, a uint32) { i := p.PixOffset(x, y) a = uint32(p.Pix[i]) a |= a << 8 return a, a, a, a } }
func (z *Rasterizer) rasterizeDstAlphaSrcOpaqueOpSrc(dst *image.Alpha, r image.Rectangle) { // TODO: add SIMD implementations. // TODO: add a fixed point math implementation. // TODO: non-zero vs even-odd winding? if r == dst.Bounds() && r == z.Bounds() { floatingAccumulate(dst.Pix, z.area) return } println("TODO: the general case") }
// checkCornersCenter checks that the corners of the image are all 0x00 and the // center is 0xff. func checkCornersCenter(m *image.Alpha) error { if err := checkCorners(m); err != nil { return err } size := m.Bounds().Size() center := m.Pix[(size.Y/2)*m.Stride+(size.X/2)] if center != 0xff { return fmt.Errorf("center: got %#02x, want 0xff", center) } return nil }
func drawPixels(img *image.Alpha, px, py, pw, ph uint, fill bool) { var x, y uint for y = 0; y < ph; y++ { for x = 0; x < pw; x++ { if fill { img.Set(int(px*pw+x), int(py*ph+y), image.White) } else { img.Set(int(px*pw+x), int(py*ph+y), image.Transparent) } } } }
// checkCorners checks that the corners of the image are all 0x00. func checkCorners(m *image.Alpha) error { size := m.Bounds().Size() corners := [4]uint8{ m.Pix[(0*size.Y+0)*m.Stride+(0*size.X+0)], m.Pix[(0*size.Y+0)*m.Stride+(1*size.X-1)], m.Pix[(1*size.Y-1)*m.Stride+(0*size.X+0)], m.Pix[(1*size.Y-1)*m.Stride+(1*size.X-1)], } if corners != [4]uint8{} { return fmt.Errorf("corners were not all zero: %v", corners) } return nil }
func Context(dst *image.Alpha, bold bool) *freetype.Context { c := freetype.NewContext() c.SetDPI(dpi) if bold { c.SetFont(bld) } else { c.SetFont(reg) } c.SetFontSize(size) c.SetClip(dst.Bounds()) c.SetDst(dst) c.SetSrc(image.Opaque) return c }
// Renders the mask to the canvas func DrawSolidRGBA(dest *image.RGBA, mask *image.Alpha, rgba color.RGBA) { rect := dest.Bounds().Intersect(mask.Bounds()) minX := uint32(rect.Min.X) minY := uint32(rect.Min.Y) maxX := uint32(rect.Max.X) maxY := uint32(rect.Max.Y) pixColor := *(*uint32)(unsafe.Pointer(&rgba)) cs1 := pixColor & 0xff00ff cs2 := (pixColor >> 8) & 0xff00ff stride1 := uint32(dest.Stride) stride2 := uint32(mask.Stride) maxY *= stride1 var pix, pixm []uint8 var pixelx uint32 var x, y1, y2 uint32 for y1, y2 = minY*stride1, minY*stride2; y1 < maxY; y1, y2 = y1+stride1, y2+stride2 { pix = dest.Pix[y1:] pixm = mask.Pix[y2:] pixelx = minX * 4 for x = minX; x < maxX; x++ { alpha := uint32(pixm[x]) p := (*uint32)(unsafe.Pointer(&pix[pixelx])) if alpha != 0 { invAlpha := 0xff - alpha ct1 := (*p & 0xff00ff) * invAlpha ct2 := ((*p >> 8) & 0xff00ff) * invAlpha ct1 = ((ct1 + cs1*alpha) >> 8) & 0xff00ff ct2 = (ct2 + cs2*alpha) & 0xff00ff00 *p = ct1 + ct2 } pixelx += 4 } } }
func bilinearAlpha(src *image.Alpha, x, y float32) color.Alpha { p := findLinearSrc(src.Bounds(), x, y) // Slice offsets for the surrounding pixels. off00 := src.PixOffset(p.low.X, p.low.Y) off01 := src.PixOffset(p.high.X, p.low.Y) off10 := src.PixOffset(p.low.X, p.high.Y) off11 := src.PixOffset(p.high.X, p.high.Y) fa := float32(src.Pix[off00]) * p.frac00 fa += float32(src.Pix[off01]) * p.frac01 fa += float32(src.Pix[off10]) * p.frac10 fa += float32(src.Pix[off11]) * p.frac11 return color.Alpha{A: uint8(fa + 0.5)} }
func FindSDFAlpha(img *image.Alpha, x, y, maxRadius int) int { w := img.Bounds().Dx() distance := maxRadius*maxRadius + 1 alpha := uint8(0) if (image.Point{x, y}.In(img.Bounds())) { alpha = img.Pix[y*w+x] } a := uint8(0) for radius := 1; (radius <= maxRadius) && (radius*radius < distance); radius++ { for line := -radius; line <= radius; line++ { nx, ny := x+line, y+radius if (image.Point{nx, ny}.In(img.Bounds())) { a = img.Pix[ny*w+nx] } else { a = 0 } //fmt.Println(line, x, ny, a, alpha) if a != alpha { nx = x - nx ny = y - ny d := (nx * nx) + (ny * ny) if d < distance { distance = d } } //} } for line := -radius; line <= radius; line++ { nx, ny := x+line, y-radius if (image.Point{nx, ny}.In(img.Bounds())) { a = img.Pix[ny*w+nx] } else { a = 0 } if a != alpha { nx = x - nx ny = y - ny d := (nx * nx) + (ny * ny) if d < distance { distance = d } } //} } for line := -radius; line < radius; line++ { nx, ny := x+radius, y+line if (image.Point{nx, ny}.In(img.Bounds())) { a = img.Pix[ny*w+nx] } else { a = 0 } if a != alpha { nx = x - nx ny = y - ny d := (nx * nx) + (ny * ny) if d < distance { distance = d } } //} } for line := -radius; line < radius; line++ { nx, ny := x-radius, y+line if (image.Point{nx, ny}.In(img.Bounds())) { a = img.Pix[ny*w+nx] } else { a = 0 } if a != alpha { nx = x - nx ny = y - ny d := (nx * nx) + (ny * ny) if d < distance { distance = d } } //} } } SDF := float32(math.Sqrt(float64(distance))) if alpha == 0 { SDF = -SDF } SDF *= 127.5 / float32(maxRadius) SDF += 127.5 if SDF < 0 { SDF = 0 } else if SDF > 255 { SDF = 255 } return int(SDF + 0.5) }
func resizeAlpha(src *image.Alpha, size int) *image.Alpha { dst := image.NewAlpha(image.Rect(0, 0, size, size)) draw.ApproxBiLinear.Scale(dst, dst.Bounds(), src, src.Bounds(), draw.Src, nil) return dst }
func newSetFuncAlpha(p *image.Alpha) SetFunc { return func(x, y int, r, g, b, a uint32) { i := p.PixOffset(x, y) p.Pix[i] = uint8(a >> 8) } }