func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { n, dy := 4*r.Dx(), r.Dy() d0 := dst.PixOffset(r.Min.X, r.Min.Y) s0 := src.PixOffset(sp.X, sp.Y) var ddelta, sdelta int if r.Min.Y <= sp.Y { ddelta = dst.Stride sdelta = src.Stride } else { // If the source start point is higher than the destination start // point, then we compose the rows in bottom-up order instead of // top-down. Unlike the drawCopyOver function, we don't have to check // the x coordinates because the built-in copy function can handle // overlapping slices. d0 += (dy - 1) * dst.Stride s0 += (dy - 1) * src.Stride ddelta = -dst.Stride sdelta = -src.Stride } for ; dy > 0; dy-- { copy(dst.Pix[d0:d0+n], src.Pix[s0:s0+n]) d0 += ddelta s0 += sdelta } }
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 setPix(img *image.RGBA, x, y int) { offset := img.PixOffset(x, y) img.Pix[offset] = 255 // R img.Pix[offset+1] = 255 // G img.Pix[offset+2] = 255 // B img.Pix[offset+3] = 255 // A }
func newSetFuncRGBA(p *image.RGBA) SetFunc { return func(x, y int, r, g, b, a uint32) { i := p.PixOffset(x, y) p.Pix[i+0] = uint8(r >> 8) p.Pix[i+1] = uint8(g >> 8) p.Pix[i+2] = uint8(b >> 8) p.Pix[i+3] = uint8(a >> 8) } }
func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { x0, x1, dx = x1-1, x0-1, -1 y0, y1, dy = y1-1, y0-1, -1 } } sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y sx0 := sp.X + x0 - r.Min.X mx0 := mp.X + x0 - r.Min.X sx1 := sx0 + (x1 - x0) i0 := dst.PixOffset(x0, y0) di := dx * 4 for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { for i, sx, mx := i0, sx0, mx0; sx != sx1; i, sx, mx = i+di, sx+dx, mx+dx { ma := uint32(m) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() } sr, sg, sb, sa := src.At(sx, sy).RGBA() if op == Over { dr := uint32(dst.Pix[i+0]) dg := uint32(dst.Pix[i+1]) db := uint32(dst.Pix[i+2]) da := uint32(dst.Pix[i+3]) // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. // We work in 16-bit color, and so would normally do: // dr |= dr << 8 // and similarly for dg, db and da, but instead we multiply a // (which is a 16-bit color, ranging in [0,65535]) by 0x101. // This yields the same result, but is fewer arithmetic operations. 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) } else { dst.Pix[i+0] = uint8(sr * ma / m >> 8) dst.Pix[i+1] = uint8(sg * ma / m >> 8) dst.Pix[i+2] = uint8(sb * ma / m >> 8) dst.Pix[i+3] = uint8(sa * ma / m >> 8) } } i0 += dy * dst.Stride } }
func convertRGBAtoWin(dest *win.DIB, src *image.RGBA) { var x, y, i, si int for x = dest.Rect.Min.X; x < dest.Rect.Max.X; x++ { for y = dest.Rect.Min.Y; y < dest.Rect.Max.Y; y++ { si = src.PixOffset(x, y) i = dest.PixOffset(x, y) dest.Pix[i+0] = src.Pix[si+2] dest.Pix[i+1] = src.Pix[si+1] dest.Pix[i+2] = src.Pix[si+0] } } }
func downscaleRGBA(ii *image.RGBA, or image.Rectangle) *image.RGBA { // ii: input image, ir: input rectangle // ti: temporary image, tr: temporary rectangle // oi: output image, or: output rectangle // // ii (ir) ti (tr) oi (or) // +----------+ => +----+ => +----+ // | | | | | | // | | | | +----+ // | | | | // +----------+ +----+ ir := ii.Bounds() tr := image.Rect(or.Min.X, ir.Min.Y, or.Max.X, ir.Max.Y) ti := image.NewRGBA(tr) oi := image.NewRGBA(or) c := NewClampedLinearTransformation(tr.Min.X, ir.Min.X, tr.Max.X, ir.Max.X) for j := tr.Min.Y; j < tr.Max.Y; j++ { jp := j for i, i0, i1 := tr.Min.X, 0, c.t(tr.Min.X); i < tr.Max.X; i++ { i0, i1 = i1, c.t(i+1) o0, o1 := ii.PixOffset(i0, jp), ii.PixOffset(i1, jp) t := RGBA128{} for ip := o0; ip < o1; ip += 4 { t.AddRGBA(ii.Pix[ip+0], ii.Pix[ip+1], ii.Pix[ip+2], ii.Pix[ip+3]) } ti.SetRGBA(i, j, t.Div(uint32(i1-i0)).ToRGBA()) } } c = NewClampedLinearTransformation(or.Min.Y, tr.Min.Y, or.Max.Y, tr.Max.Y) for i := or.Min.X; i < or.Max.X; i++ { ip := i for j, j0, j1 := or.Min.Y, 0, c.t(or.Min.Y); j < or.Max.Y; j++ { j0, j1 = j1, c.t(j+1) o0, o1 := ti.PixOffset(ip, j0), ti.PixOffset(ip, j1) t := RGBA128{} for jp := o0; jp < o1; jp += ti.Stride { t.AddRGBA(ti.Pix[jp+0], ti.Pix[jp+1], ti.Pix[jp+2], ti.Pix[jp+3]) } oi.SetRGBA(i, j, t.Div(uint32(j1-j0)).ToRGBA()) } } return oi }
// This. This seems to be the solution. 0.72ns/op. RGBA -> RGBA is 0.02ns/op. Draw is 180ns/op. // from https://github.com/BurntSushi/xgbutil/blob/master/xgraphics/convert.go func convertRGBAtoXgb(dest *xgraphics.Image, src *image.RGBA) { var x, y, i, si int for x = dest.Rect.Min.X; x < dest.Rect.Max.X; x++ { for y = dest.Rect.Min.Y; y < dest.Rect.Max.Y; y++ { si = src.PixOffset(x, y) i = dest.PixOffset(x, y) dest.Pix[i+0] = src.Pix[si+2] dest.Pix[i+1] = src.Pix[si+1] dest.Pix[i+2] = src.Pix[si+0] dest.Pix[i+3] = src.Pix[si+3] } } }
func newAtFuncRGBA(p *image.RGBA) AtFunc { return func(x, y int) (r, g, b, a uint32) { i := p.PixOffset(x, y) r = uint32(p.Pix[i+0]) r |= r << 8 g = uint32(p.Pix[i+1]) g |= g << 8 b = uint32(p.Pix[i+2]) b |= b << 8 a = uint32(p.Pix[i+3]) a |= a << 8 return } }
func boxBlurH(dst, src *image.RGBA, radius int) { so := src.PixOffset(xy(src.Bounds().Min)) do := dst.PixOffset(xy(dst.Bounds().Min)) w, h := dxy(dst.Bounds()) r2 := 2*radius + 1 var val [nRGBAchan]int for y := 0; y < h; y++ { fv := src.Pix[so:] lv := src.Pix[so+(w-1)*nRGBAchan:] for i := 0; i < nRGBAchan; i++ { val[i] = (radius + 1) * int(fv[i]) } rp := so for x := 0; x < radius; x++ { for i := 0; i < nRGBAchan; i++ { val[i] += int(src.Pix[rp]) rp++ } } x, lp, dp := 0, so, do for ; x <= radius; x++ { for i := 0; i < nRGBAchan; i++ { val[i] += int(src.Pix[rp]) - int(fv[i]) dst.Pix[dp] = uint8(val[i] / r2) rp++ dp++ } } for ; x < w-radius; x++ { for i := 0; i < nRGBAchan; i++ { val[i] += int(src.Pix[rp]) - int(src.Pix[lp]) dst.Pix[dp] = uint8(val[i] / r2) lp++ rp++ dp++ } } for ; x < w; x++ { for i := 0; i < nRGBAchan; i++ { val[i] += int(lv[i]) - int(src.Pix[lp]) dst.Pix[dp] = uint8(val[i] / r2) lp++ dp++ } } so += src.Stride do += dst.Stride } }
func boxBlurV(dst, src *image.RGBA, radius int) { so := src.PixOffset(xy(src.Bounds().Min)) do := dst.PixOffset(xy(dst.Bounds().Min)) w, h := dxy(dst.Bounds()) r2 := 2*radius + 1 var val [nRGBAchan]int for x := 0; x < w; x++ { fv := src.Pix[so:] lv := src.Pix[so+(h-1)*src.Stride:] for i := 0; i < nRGBAchan; i++ { val[i] = (radius + 1) * int(fv[i]) } rp := so for y := 0; y < radius; y++ { for i := 0; i < nRGBAchan; i++ { val[i] += int(src.Pix[rp+i]) } rp += src.Stride } y, lp, dp := 0, so, do for ; y <= radius; y++ { for i := 0; i < nRGBAchan; i++ { val[i] += int(src.Pix[rp+i]) - int(fv[i]) dst.Pix[dp+i] = uint8(val[i] / r2) } rp += src.Stride dp += dst.Stride } for ; y < h-radius; y++ { for i := 0; i < nRGBAchan; i++ { val[i] += int(src.Pix[rp+i]) - int(src.Pix[lp+i]) dst.Pix[dp+i] = uint8(val[i] / r2) } lp += src.Stride rp += src.Stride dp += dst.Stride } for ; y < h; y++ { for i := 0; i < nRGBAchan; i++ { val[i] += int(lv[i]) - int(src.Pix[lp+i]) dst.Pix[dp+i] = uint8(val[i] / r2) } lp += src.Stride dp += dst.Stride } so += nRGBAchan do += nRGBAchan } }
func RenderTile(xmin, xmax, ymin, ymax int, rt *renderTile, srt *image.RGBA) { var x, y, poff int var col color.RGBA for y = ymin; y < ymax; y++ { for x = xmin; x < xmax; x++ { rt.CastRay(x, y, &col) poff = srt.PixOffset(x, y) srt.Pix[poff+0] = col.R srt.Pix[poff+1] = col.G srt.Pix[poff+2] = col.B srt.Pix[poff+3] = col.A } } Chan <- 1 }
func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) { dx, dy := r.Dx(), r.Dy() d0 := dst.PixOffset(r.Min.X, r.Min.Y) s0 := src.PixOffset(sp.X, sp.Y) var ( ddelta, sdelta int i0, i1, idelta int ) if r.Min.Y < sp.Y || r.Min.Y == sp.Y && r.Min.X <= sp.X { ddelta = dst.Stride sdelta = src.Stride i0, i1, idelta = 0, dx*4, +4 } else { // If the source start point is higher than the destination start point, or equal height but to the left, // then we compose the rows in right-to-left, bottom-up order instead of left-to-right, top-down. d0 += (dy - 1) * dst.Stride s0 += (dy - 1) * src.Stride ddelta = -dst.Stride sdelta = -src.Stride i0, i1, idelta = (dx-1)*4, -4, -4 } for ; dy > 0; dy-- { dpix := dst.Pix[d0:] spix := src.Pix[s0:] for i := i0; i != i1; i += idelta { sr := uint32(spix[i+0]) * 0x101 sg := uint32(spix[i+1]) * 0x101 sb := uint32(spix[i+2]) * 0x101 sa := uint32(spix[i+3]) * 0x101 dr := uint32(dpix[i+0]) dg := uint32(dpix[i+1]) db := uint32(dpix[i+2]) da := uint32(dpix[i+3]) // The 0x101 is here for the same reason as in drawRGBA. a := (m - sa) * 0x101 dpix[i+0] = uint8((dr*a/m + sr) >> 8) dpix[i+1] = uint8((dg*a/m + sg) >> 8) dpix[i+2] = uint8((db*a/m + sb) >> 8) dpix[i+3] = uint8((da*a/m + sa) >> 8) } d0 += ddelta s0 += sdelta } }
func (z *Rasterizer) rasterizeDstRGBASrcUniformOpSrc(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) { 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 uniform src assumption. i := y*dst.Stride + 4*x pix[i+0] = uint8((sr * ma / 0xffff) >> 8) pix[i+1] = uint8((sg * ma / 0xffff) >> 8) pix[i+2] = uint8((sb * ma / 0xffff) >> 8) pix[i+3] = uint8((sa * ma / 0xffff) >> 8) } } }
// resizeRGBA returns a scaled copy of the RGBA image slice r of m. // The returned image has width w and height h. func resizeRGBA(m *image.RGBA, r image.Rectangle, w, h int) image.Image { ww, hh := uint64(w), uint64(h) dx, dy := uint64(r.Dx()), uint64(r.Dy()) // See comment in Resize. n, sum := dx*dy, make([]uint64, 4*w*h) for y := r.Min.Y; y < r.Max.Y; y++ { pixOffset := m.PixOffset(r.Min.X, y) for x := r.Min.X; x < r.Max.X; x++ { // Get the source pixel. r64 := uint64(m.Pix[pixOffset+0]) g64 := uint64(m.Pix[pixOffset+1]) b64 := uint64(m.Pix[pixOffset+2]) a64 := uint64(m.Pix[pixOffset+3]) pixOffset += 4 // Spread the source pixel over 1 or more destination rows. py := uint64(y) * hh for remy := hh; remy > 0; { qy := dy - (py % dy) if qy > remy { qy = remy } // Spread the source pixel over 1 or more destination columns. px := uint64(x) * ww index := 4 * ((py/dy)*ww + (px / dx)) for remx := ww; remx > 0; { qx := dx - (px % dx) if qx > remx { qx = remx } qxy := qx * qy sum[index+0] += r64 * qxy sum[index+1] += g64 * qxy sum[index+2] += b64 * qxy sum[index+3] += a64 * qxy index += 4 px += qx remx -= qx } py += qy remy -= qy } } } return average(sum, w, h, n) }
func drawFillSrc(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { sr, sg, sb, sa := src.RGBA() // The built-in copy function is faster than a straightforward for loop to fill the destination with // the color, but copy requires a slice source. We therefore use a for loop to fill the first row, and // then use the first row as the slice source for the remaining rows. i0 := dst.PixOffset(r.Min.X, r.Min.Y) i1 := i0 + r.Dx()*4 for i := i0; i < i1; i += 4 { dst.Pix[i+0] = uint8(sr >> 8) dst.Pix[i+1] = uint8(sg >> 8) dst.Pix[i+2] = uint8(sb >> 8) dst.Pix[i+3] = uint8(sa >> 8) } firstRow := dst.Pix[i0:i1] for y := r.Min.Y + 1; y < r.Max.Y; y++ { i0 += dst.Stride i1 += dst.Stride copy(dst.Pix[i0:i1], firstRow) } }
func drawFillOver(dst *image.RGBA, r image.Rectangle, sr, sg, sb, sa uint32) { // The 0x101 is here for the same reason as in drawRGBA. a := (m - sa) * 0x101 i0 := dst.PixOffset(r.Min.X, r.Min.Y) i1 := i0 + r.Dx()*4 for y := r.Min.Y; y != r.Max.Y; y++ { for i := i0; i < i1; i += 4 { dr := &dst.Pix[i+0] dg := &dst.Pix[i+1] db := &dst.Pix[i+2] da := &dst.Pix[i+3] *dr = uint8((uint32(*dr)*a/m + sr) >> 8) *dg = uint8((uint32(*dg)*a/m + sg) >> 8) *db = uint8((uint32(*db)*a/m + sb) >> 8) *da = uint8((uint32(*da)*a/m + sa) >> 8) } i0 += dst.Stride i1 += dst.Stride } }
func drawFillOver(dst *image.RGBA, r image.Rectangle, src *image.Uniform) { sr, sg, sb, sa := src.RGBA() // The 0x101 is here for the same reason as in drawRGBA. a := (m - sa) * 0x101 i0 := dst.PixOffset(r.Min.X, r.Min.Y) i1 := i0 + r.Dx()*4 for y := r.Min.Y; y != r.Max.Y; y++ { for i := i0; i < i1; i += 4 { dr := uint32(dst.Pix[i+0]) dg := uint32(dst.Pix[i+1]) db := uint32(dst.Pix[i+2]) da := uint32(dst.Pix[i+3]) dst.Pix[i+0] = uint8((dr*a/m + sr) >> 8) dst.Pix[i+1] = uint8((dg*a/m + sg) >> 8) dst.Pix[i+2] = uint8((db*a/m + sb) >> 8) dst.Pix[i+3] = uint8((da*a/m + sa) >> 8) } i0 += dst.Stride i1 += dst.Stride } }
func bilinearRGBA(src *image.RGBA, x, y float32) color.RGBA { 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) fr := float32(src.Pix[off00+0]) * p.frac00 fg := float32(src.Pix[off00+1]) * p.frac00 fb := float32(src.Pix[off00+2]) * p.frac00 fa := float32(src.Pix[off00+3]) * p.frac00 fr += float32(src.Pix[off01+0]) * p.frac01 fg += float32(src.Pix[off01+1]) * p.frac01 fb += float32(src.Pix[off01+2]) * p.frac01 fa += float32(src.Pix[off01+3]) * p.frac01 fr += float32(src.Pix[off10+0]) * p.frac10 fg += float32(src.Pix[off10+1]) * p.frac10 fb += float32(src.Pix[off10+2]) * p.frac10 fa += float32(src.Pix[off10+3]) * p.frac10 fr += float32(src.Pix[off11+0]) * p.frac11 fg += float32(src.Pix[off11+1]) * p.frac11 fb += float32(src.Pix[off11+2]) * p.frac11 fa += float32(src.Pix[off11+3]) * p.frac11 return color.RGBA{ R: uint8(fr + 0.5), G: uint8(fg + 0.5), B: uint8(fb + 0.5), A: uint8(fa + 0.5), } }
// internal function, but shared by all system implementations: &img.Pix[0] is not necessarily the first pixel in the image func pixelDataPos(img *image.RGBA) int { return img.PixOffset(img.Rect.Min.X, img.Rect.Min.Y) }
// Converts image to text version func convertImage(img image.Image, settings map[string]interface{}) (result []byte, err error) { // Set up parameters var p parameters p = defaults() if v, ok := settings["bgcolor"].(string); ok && len(v) > 0 { p.bgcolor = v } if v, ok := settings["browser"].(string); ok && len(v) > 0 { p.browser = v } if v, ok := settings["characters"].(string); ok && len(v) > 0 { p.characters = v } if v, ok := settings["contrast"].(string); ok && len(v) > 0 { p.contrast, _ = strconv.Atoi(v) } if v, ok := settings["fontsize"].(string); ok && len(v) > 0 { p.fontsize = v } if v, ok := settings["grayscale"].(string); ok && len(v) > 0 { p.grayscale, _ = strconv.Atoi(v) } if v, ok := settings["textType"].(string); ok && len(v) > 0 { p.texttype = v } if v, ok := settings["width"].(string); ok && len(v) > 0 { p.width, _ = strconv.Atoi(v) if p.width < 1 || p.width > 500 { p.width = 100 } } // Rescale proportions to make output prettier in text height := int(float32(p.width) / (float32(img.Bounds().Dx()) / float32(img.Bounds().Dy()))) if p.browser == "ie" { height = height * 65 / 100 } else { height = height * 43 / 100 } // Produce minified image to work with start := time.Now() var m *image.RGBA if p.width < img.Bounds().Dx() && p.width >= 150 && (img.Bounds().Dx()*img.Bounds().Dy()/p.width) < 5000 { temp, err := resize(img, p.width, height) if err != nil { return result, err } m = temp.(*image.RGBA) } else { m = image.NewRGBA(image.Rect(0, 0, p.width, height)) graphics.Scale(m, img) } // Modify image as required if p.grayscale == 1 { grayscale(m) } else if p.grayscale == 2 { monochrome(m) } // Initialize buffer var buffer bytes.Buffer buffer.WriteString("<table align=\"center\" cellpadding=\"10\">\n<tr bgcolor=\"" + p.bgcolor + "\"><td>\n\n") buffer.WriteString("<!-- IMAGE BEGINS HERE -->\n<font size=\"" + p.fontsize + "\">\n<pre>") // Prepare variables htmlStart := "<font color=#" htmlEnd := "</font>" var current, previous uint = 0, 0 next := nextChar(p.texttype, p.characters) b := m.Bounds() // Loop over all pixels and add HTML-font converted data to the output buffer for y := b.Min.Y; y < b.Max.Y; y++ { j := m.PixOffset(0, y) current = uint(m.Pix[j+0])<<16 | uint(m.Pix[j+1])<<8 | uint(m.Pix[j+2]) buffer.WriteString(htmlStart + hex(current) + ">" + next()) previous = current for x := b.Min.X + 1; x < b.Max.X; x++ { i := m.PixOffset(x, y) current = uint(m.Pix[i+0])<<16 | uint(m.Pix[i+1])<<8 | uint(m.Pix[i+2]) if previous != current { buffer.WriteString(htmlEnd + htmlStart + hex(current) + ">" + next()) } else { buffer.WriteString(next()) } previous = current } buffer.WriteString(htmlEnd + "<br>") } // Finish up buffer buffer.WriteString("\n</pre></font>") buffer.WriteString("\n<!-- IMAGE ENDS HERE -->\n") buffer.WriteString("\n<FONT COLOR=LIGHTBLUE SIZE=2>Rendering time: " + time.Since(start).String() + "</FONT><BR>\n") result = buffer.Bytes() return }