示例#1
0
文件: draw.go 项目: imagexec/go-1
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
	}
}
示例#2
0
文件: draw.go 项目: h8liu/golang
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
}
示例#4
0
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)
	}
}
示例#5
0
文件: draw.go 项目: h8liu/golang
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
	}
}
示例#6
0
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]
		}
	}
}
示例#7
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
}
示例#8
0
// 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]
		}
	}
}
示例#9
0
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
	}
}
示例#10
0
文件: blur.go 项目: tajtiattila/blur
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
	}
}
示例#11
0
文件: blur.go 项目: tajtiattila/blur
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
	}
}
示例#12
0
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
}
示例#13
0
文件: draw.go 项目: h8liu/golang
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
	}
}
示例#14
0
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)
		}
	}
}
示例#15
0
// 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)
}
示例#16
0
文件: draw.go 项目: h8liu/golang
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)
	}
}
示例#17
0
文件: draw.go 项目: achanda/go
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
	}
}
示例#18
0
文件: draw.go 项目: h8liu/golang
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
	}
}
示例#19
0
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),
	}
}
示例#20
0
文件: area.go 项目: rizqi101/ui
// 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)
}
示例#21
0
// 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
}