// 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 }
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 }
// 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 }
// 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 }
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 }
// 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 }
// 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 }
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 }
// 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 }
// 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 }