Beispiel #1
0
// SourceRectVersion searches and returns an existing matching version,
// or a new one will be created and saved.
func (self *Image) SourceRectVersion(sourceRect image.Rectangle, width, height int, grayscale bool, outsideColor color.Color) (im *ImageVersion, err error) {
	if self.Grayscale() {
		grayscale = true // Ignore color requests when original image is grayscale
	}

	// Search for exact match
	for i := range self.Versions {
		v := &self.Versions[i]
		match := v.SourceRect.Rectangle() == sourceRect &&
			v.Width.GetInt() == width &&
			v.Height.GetInt() == height &&
			v.OutsideColor.EqualsColor(outsideColor) &&
			v.Grayscale.Get() == grayscale
		if match {
			return v, nil
		}
	}

	// No exact match, create version
	origImage, err := self.Versions[0].LoadImage()
	if err != nil {
		return nil, err
	}

	var versionImage image.Image
	if sourceRect.In(self.Rectangle()) {
		versionImage = ResizeImage(origImage, sourceRect, width, height)
	} else {
		if grayscale {
			versionImage = image.NewGray(image.Rect(0, 0, width, height))
		} else {
			versionImage = image.NewRGBA(image.Rect(0, 0, width, height))
		}
		// Fill version with outsideColor
		draw.Draw(versionImage.(draw.Image), versionImage.Bounds(), image.NewUniform(outsideColor), image.ZP, draw.Src)

		panic("todo scale and draw sub image")

	}
	self.Versions = append(self.Versions, newImageVersion(self.Filename(), self.ContentType(), sourceRect, width, height, grayscale))
	version := &self.Versions[len(self.Versions)-1]
	err = version.SaveImage(versionImage)
	if err != nil {
		return nil, err
	}
	err = Config.Backend.SaveImage(self)
	if err != nil {
		return nil, err
	}
	return version, nil
}
Beispiel #2
0
func getAverageColorForRegion(img image.Image, rc image.Rectangle) (color.Color, error) {
	if !rc.In(img.Bounds()) {
		return nil, fmt.Errorf("r is not withing img bounds")
	}

	var red, green, blue, alpha uint
	pixels := uint(rc.Dx() * rc.Dy())
	for i := rc.Min.X; i < rc.Max.X; i++ {
		for j := rc.Min.Y; j < rc.Max.Y; j++ {
			r, g, b, a := img.At(i, j).RGBA()
			red += uint(r)
			green += uint(g)
			blue += uint(b)
			alpha += uint(a)
		}
	}
	return color.RGBA64{uint16(red / pixels), uint16(green / pixels), uint16(blue / pixels), uint16(alpha / pixels)}, nil

}
Beispiel #3
0
// Unload copies the pixel data from the specified rectangle of the image into
// the buffer, which must be big enough to hold the result.
func (src *Image) Unload(r image.Rectangle, data []byte) (n int, err error) {
	src.Display.mu.Lock()
	defer src.Display.mu.Unlock()
	i := src
	if !r.In(i.R) {
		return 0, fmt.Errorf("image.Unload: bad rectangle")
	}
	bpl := BytesPerLine(r, i.Depth)
	if len(data) < bpl*r.Dy() {
		return 0, fmt.Errorf("image.Unload: buffer too small")
	}

	d := i.Display
	d.flush(false) // make sure next flush is only us
	ntot := 0
	for r.Min.Y < r.Max.Y {
		a := d.bufimage(1 + 4 + 4*4)
		dy := 8000 / bpl
		if dy <= 0 {
			return 0, fmt.Errorf("unloadimage: image too wide")
		}
		if dy > r.Dy() {
			dy = r.Dy()
		}
		a[0] = 'r'
		bplong(a[1:], uint32(i.id))
		bplong(a[5:], uint32(r.Min.X))
		bplong(a[9:], uint32(r.Min.Y))
		bplong(a[13:], uint32(r.Max.X))
		bplong(a[17:], uint32(r.Min.Y+dy))
		if err := d.flush(false); err != nil {
			return ntot, err
		}
		n, err := d.conn.ReadDraw(data[ntot:])
		ntot += n
		if err != nil {
			return ntot, err
		}
		r.Min.Y += dy
	}
	return ntot, nil
}
Beispiel #4
0
// Rect returns the sum of elements in the region
// 	r.Min.X <= x < r.Max.X
// 	r.Min.Y <= y < r.Max.Y
func (t *Table) Rect(r image.Rectangle) float64 {
	s := (*Image)(t)
	bnds := image.Rect(0, 0, s.Width, s.Height)
	if !r.In(bnds) {
		panic("out of bounds")
	}
	if r.Dx()*r.Dy() == 0 {
		return 0
	}
	area := s.At(r.Max.X-1, r.Max.Y-1)
	if r.Min.X > 0 {
		area -= s.At(r.Min.X-1, r.Max.Y-1)
	}
	if r.Min.Y > 0 {
		area -= s.At(r.Max.X-1, r.Min.Y-1)
	}
	if r.Min.X > 0 && r.Min.Y > 0 {
		area += s.At(r.Min.X-1, r.Min.Y-1)
	}
	return area
}
Beispiel #5
0
func (dst *Image) load(r image.Rectangle, data []byte) (int, error) {
	i := dst
	chunk := i.Display.bufsize - 64
	if !r.In(i.R) {
		return 0, fmt.Errorf("loadimage: bad rectangle")
	}
	bpl := BytesPerLine(r, i.Depth)
	n := bpl * r.Dy()
	if n > len(data) {
		return 0, fmt.Errorf("loadimage: insufficient data")
	}
	ndata := 0
	for r.Max.Y > r.Min.Y {
		dy := r.Max.Y - r.Min.Y
		if dy*bpl > chunk {
			dy = chunk / bpl
		}
		if dy <= 0 {
			return 0, fmt.Errorf("loadimage: image too wide for buffer")
		}
		n := dy * bpl
		a := i.Display.bufimage(21 + n)
		a[0] = 'y'
		bplong(a[1:], uint32(i.id))
		bplong(a[5:], uint32(r.Min.X))
		bplong(a[9:], uint32(r.Min.Y))
		bplong(a[13:], uint32(r.Max.X))
		bplong(a[17:], uint32(r.Min.Y+dy))
		copy(a[21:], data)
		ndata += n
		data = data[n:]
		r.Min.Y += dy
	}
	if err := i.Display.flush(false); err != nil {
		return ndata, err
	}
	return ndata, nil
}
Beispiel #6
0
// Used for floating-point precision.
// Rather than A - B - C + D, computes A + B + C + D.
// Only useful when original image was non-negative.
func absTableRect(t *rimg64.Table, r image.Rectangle) float64 {
	s := (*rimg64.Image)(t)
	bnds := image.Rect(0, 0, s.Width, s.Height)
	if !r.In(bnds) {
		panic("out of bounds")
	}
	if r.Dx()*r.Dy() == 0 {
		return 0
	}
	area := s.At(r.Max.X-1, r.Max.Y-1)
	if r.Min.X > 0 {
		// Change from plus to minus.
		area += s.At(r.Min.X-1, r.Max.Y-1)
	}
	if r.Min.Y > 0 {
		// Change from plus to minus.
		area += s.At(r.Max.X-1, r.Min.Y-1)
	}
	if r.Min.X > 0 && r.Min.Y > 0 {
		area += s.At(r.Min.X-1, r.Min.Y-1)
	}
	return area
}
Beispiel #7
0
// Cload is like Load, but uses image-compressed data.
func (dst *Image) Cload(r image.Rectangle, data []byte) (int, error) {
	dst.Display.mu.Lock()
	defer dst.Display.mu.Unlock()
	i := dst
	if !r.In(i.R) {
		return 0, fmt.Errorf("cloadimage: bad rectangle")
	}

	miny := r.Min.Y
	m := 0
	ncblock := compblocksize(r, i.Depth)
	for miny != r.Max.Y {
		maxy := atoi(data[0*12:])
		nb := atoi(data[1*12:])
		if maxy <= miny || r.Max.Y < maxy {
			return 0, fmt.Errorf("creadimage: bad maxy %d", maxy)
		}
		data = data[2*12:]
		m += 2 * 12
		if nb <= 0 || ncblock < nb || nb > len(data) {
			return 0, fmt.Errorf("creadimage: bad count %d", nb)
		}
		// TODO: error check?
		a := i.Display.bufimage(21 + nb)
		a[0] = 'Y'
		bplong(a[1:], i.id)
		bplong(a[5:], uint32(r.Min.Y))
		bplong(a[9:], uint32(miny))
		bplong(a[13:], uint32(r.Max.Y))
		bplong(a[17:], uint32(maxy))
		copy(a[21:], data)
		miny = maxy
		data = data[nb:]
		m += nb
	}
	return m, nil
}
Beispiel #8
0
func decompress(m *plan9Image, r image.Rectangle, data []byte) error {
	if !r.In(m.rect) {
		return errors.New("plan9font: decompress: bad rectangle")
	}
	bpl := bytesPerLine(r, m.depth)
	mem := make([]byte, compWindowSize)
	memi := 0
	omemi := -1
	y := r.Min.Y
	linei := m.byteoffset(r.Min.X, y)
	eline := linei + bpl
	datai := 0
	for {
		if linei == eline {
			y++
			if y == r.Max.Y {
				break
			}
			linei = m.byteoffset(r.Min.X, y)
			eline = linei + bpl
		}
		if datai == len(data) {
			return errDecompressBufferTooSmall
		}
		c := data[datai]
		datai++
		if c >= 128 {
			for cnt := c - 128 + 1; cnt != 0; cnt-- {
				if datai == len(data) {
					return errDecompressBufferTooSmall
				}
				if linei == eline {
					return errDecompressPhaseError
				}
				m.pix[linei] = data[datai]
				linei++
				mem[memi] = data[datai]
				memi++
				datai++
				if memi == len(mem) {
					memi = 0
				}
			}
		} else {
			if datai == len(data) {
				return errDecompressBufferTooSmall
			}
			offs := int(data[datai]) + ((int(c) & 3) << 8) + 1
			datai++
			if memi < offs {
				omemi = memi + (compWindowSize - offs)
			} else {
				omemi = memi - offs
			}
			for cnt := (c >> 2) + compShortestMatch; cnt != 0; cnt-- {
				if linei == eline {
					return errDecompressPhaseError
				}
				m.pix[linei] = mem[omemi]
				linei++
				mem[memi] = mem[omemi]
				memi++
				omemi++
				if omemi == len(mem) {
					omemi = 0
				}
				if memi == len(mem) {
					memi = 0
				}
			}
		}
	}
	return nil
}
Beispiel #9
0
// SourceRectVersion searches and returns an existing matching version,
// or a new one will be created and saved.
func (self *Image) VersionSourceRect(sourceRect image.Rectangle, width, height int, grayscale bool, outsideColor color.Color) (im *ImageVersion, err error) {
	if self.Grayscale() {
		grayscale = true // Ignore color requests when original image is grayscale
	}

	// Search for exact match
	for i := range self.Versions {
		v := &self.Versions[i]
		match := v.SourceRect.Rectangle() == sourceRect &&
			v.Width.GetInt() == width &&
			v.Height.GetInt() == height &&
			v.OutsideColor.EqualsColor(outsideColor) &&
			v.Grayscale.Get() == grayscale
		if match {
			return v, nil
		}
	}

	// No exact match, create version
	origImage, err := self.Versions[0].LoadImage()
	if err != nil {
		return nil, err
	}

	var versionImage image.Image
	if sourceRect.In(self.Rectangle()) {
		versionImage = ResizeImage(origImage, sourceRect, width, height)
		if grayscale && !self.Grayscale() {
			var grayVersion image.Image = image.NewGray(versionImage.Bounds())
			draw.Draw(grayVersion.(draw.Image), versionImage.Bounds(), versionImage, image.ZP, draw.Src)
			versionImage = grayVersion
		}
	} else {
		if grayscale {
			versionImage = image.NewGray(image.Rect(0, 0, width, height))
		} else {
			versionImage = image.NewRGBA(image.Rect(0, 0, width, height))
		}
		// Fill version with outsideColor
		draw.Draw(versionImage.(draw.Image), versionImage.Bounds(), image.NewUniform(outsideColor), image.ZP, draw.Src)
		// Where to draw the source image into the version image
		var destRect image.Rectangle
		if !(sourceRect.Min.X < 0 || sourceRect.Min.Y < 0) {
			panic("touching from outside means that sourceRect x or y must be negative")
		}
		sourceW := float64(sourceRect.Dx())
		sourceH := float64(sourceRect.Dy())
		destRect.Min.X = int(float64(-sourceRect.Min.X) / sourceW * float64(width))
		destRect.Min.Y = int(float64(-sourceRect.Min.Y) / sourceH * float64(height))
		destRect.Max.X = destRect.Min.X + int(float64(self.Width())/sourceW*float64(width))
		destRect.Max.Y = destRect.Min.Y + int(float64(self.Height())/sourceH*float64(height))
		destImage := ResizeImage(origImage, origImage.Bounds(), destRect.Dx(), destRect.Dy())
		draw.Draw(versionImage.(draw.Image), destRect, destImage, image.ZP, draw.Src)
	}

	// Save new image version
	self.Versions = append(self.Versions, newImageVersion(self.Filename(), self.ContentType(), sourceRect, width, height, grayscale))
	version := &self.Versions[len(self.Versions)-1]
	err = version.SaveImage(versionImage)
	if err != nil {
		return nil, err
	}
	err = Config.Backend.SaveImage(self)
	if err != nil {
		return nil, err
	}
	return version, nil
}
Beispiel #10
0
// VersionSourceRect searches and returns an existing matching version,
// or a new one will be created and saved.
func (self *Image) VersionSourceRect(sourceRect image.Rectangle, width, height int, grayscale bool, outsideColor color.Color) (im *ImageVersion, err error) {
	debug.Nop()
	// debug.Printf(
	// 	"VersionSourceRect: from %dx%d image take rectangle [%d,%d,%d,%d] (%dx%d) and scale it to %dx%d",
	// 	self.Width(),
	// 	self.Height(),
	// 	sourceRect.Min.X,
	// 	sourceRect.Min.Y,
	// 	sourceRect.Max.X,
	// 	sourceRect.Max.Y,
	// 	sourceRect.Dx(),
	// 	sourceRect.Dy(),
	// 	width,
	// 	height,
	// )

	if self.Grayscale() {
		grayscale = true // Ignore color requests when original image is grayscale
	}

	// Search for exact match
	for i := range self.Versions {
		v := &self.Versions[i]
		match := v.SourceRect.Rectangle() == sourceRect &&
			v.Width.GetInt() == width &&
			v.Height.GetInt() == height &&
			v.OutsideColor.EqualsColor(outsideColor) &&
			v.Grayscale.Get() == grayscale
		if match {
			return v, nil
		}
	}

	// No exact match, create version
	origImage, err := self.Versions[0].LoadImage()
	if err != nil {
		return nil, err
	}

	var versionImage image.Image
	if grayscale {
		versionImage = image.NewGray(image.Rect(0, 0, width, height))
	} else {
		versionImage = image.NewRGBA(image.Rect(0, 0, width, height))
	}
	if sourceRect.In(self.Rectangle()) {
		// debug.Print("VersionSourceRect: rectangle is within image")

		// versionImage = ResampleImage(origImage, sourceRect, width, height)
		subImage := SubImageWithoutOffset(origImage, sourceRect)
		err = graphics.Scale(versionImage.(draw.Image), subImage)
		if err != nil {
			return nil, err
		}
		if grayscale && !self.Grayscale() {
			var grayVersion image.Image = image.NewGray(versionImage.Bounds())
			draw.Draw(grayVersion.(draw.Image), versionImage.Bounds(), versionImage, image.ZP, draw.Src)
			versionImage = grayVersion
		}
	} else {
		// debug.Print("VersionSourceRect: rectangle is not completely within image, using outsideColor")

		// Fill version with outsideColor
		draw.Draw(versionImage.(draw.Image), versionImage.Bounds(), image.NewUniform(outsideColor), image.ZP, draw.Src)
		// Where to draw the source image into the version image
		var destRect image.Rectangle
		if !(sourceRect.Min.X < 0 || sourceRect.Min.Y < 0) {
			panic("touching from outside means that sourceRect x or y must be negative")
		}
		sourceW := float64(sourceRect.Dx())
		sourceH := float64(sourceRect.Dy())
		destRect.Min.X = int(float64(-sourceRect.Min.X) / sourceW * float64(width))
		destRect.Min.Y = int(float64(-sourceRect.Min.Y) / sourceH * float64(height))
		destRect.Max.X = destRect.Min.X + int(float64(self.Width())/sourceW*float64(width))
		destRect.Max.Y = destRect.Min.Y + int(float64(self.Height())/sourceH*float64(height))

		// destImage := ResampleImage(origImage, origImage.Bounds(), destRect.Dx(), destRect.Dy())
		// draw.Draw(versionImage.(draw.Image), destRect, destImage, image.ZP, draw.Src)
		subImage := SubImageWithoutOffset(origImage, sourceRect)
		destImage := SubImageWithoutOffset(versionImage, destRect)
		err = graphics.Scale(destImage.(draw.Image), subImage)
		if err != nil {
			return nil, err
		}
	}

	// Save new image version
	version := self.addVersion(self.Filename(), self.ContentType(), sourceRect, width, height, grayscale)
	err = version.SaveImage(versionImage)
	if err != nil {
		return nil, err
	}
	err = self.Save()
	if err != nil {
		return nil, err
	}
	return version, nil
}