func (ri *RevisitImage) RevisitMsg() (*RevisitMsg, error) { buf := bytes.NewBuffer(nil) switch ri.ImgType { case "image/jpeg": err := jpeg.Encode(buf, image.Image(image.Image(&ri.Rgbas[0])), nil) if err != nil { return nil, err } case "image/png": err := png.Encode(buf, image.Image(&ri.Rgbas[0])) if err != nil { return nil, err } case "image/gif": g := &gif.GIF{ Image: make([]*image.Paletted, 0), LoopCount: ri.LoopCount, Delay: make([]int, 0), } for index, src := range ri.Rgbas { b := src.Bounds() pal := image.NewPaletted(image.Rect(0, 0, b.Dx(), b.Dy()), ri.Palette[index]) draw.Draw(pal, pal.Bounds(), image.Image(&src), b.Min, draw.Src) g.Image = append(g.Image, pal) g.Delay = append(g.Delay, ri.Delay[index]) } buf := bytes.NewBuffer(nil) err := gif.EncodeAll(buf, g) if err != nil { return nil, err } dstImgBase64 := base64.StdEncoding.EncodeToString(buf.Bytes()) return &RevisitMsg{ Content: ImageData{ Data: fmt.Sprintf("data:%s;base64,%s", ri.ImgType, dstImgBase64), }, }, nil default: return nil, errors.New("invalid image type") } dstImgBase64 := base64.StdEncoding.EncodeToString(buf.Bytes()) return &RevisitMsg{ Content: ImageData{ Data: fmt.Sprintf("data:%s;base64,%s", ri.ImgType, dstImgBase64), }, }, nil }
func ImageTransformByProfile(src_image image.Image, src_prof, dst_prof *Profile) (image.Image, error) { var dst_image image.Image rect := src_image.Bounds() width := rect.Dx() height := rect.Dy() colorModel := src_image.ColorModel() // 今のところ RGBA, YCbCr のみ対応 if (colorModel != color.YCbCrModel) && (colorModel != color.RGBAModel) { return nil, fmt.Errorf("ImageTransformByProfile: Unsupported ColorModel(%d)", colorModel) } var src_rgba *image.RGBA var src_ycbcr *image.YCbCr if colorModel == color.YCbCrModel { // YCbCr の場合は RGB に変換する src_ycbcr = src_image.(*image.YCbCr) src_rgba = image.NewRGBA(rect) DrawYCbCr(src_rgba, rect, src_ycbcr, image.Pt(0, 0)) } else { src_rgba = src_image.(*image.RGBA) // type assertions } transform := CreateTransform(src_prof, DATA_RGBA_8, dst_prof, DATA_RGBA_8) defer transform.DeleteTransform() if transform == nil { return nil, fmt.Errorf("ImageTransformByProfile: CreateTransform Failedl(%d)", colorModel) } dst_rgba := image.NewRGBA(rect) src_pix := src_rgba.Pix dst_pix := dst_rgba.Pix len_pix := len(src_pix) transform.DoTransform(src_pix, dst_pix, len_pix) // YCbCr の場合は RGB から戻す if colorModel == color.YCbCrModel { dst_ycbcr := image.NewYCbCr(rect, src_ycbcr.SubsampleRatio) var x int var y int for y = 0; y < height; y++ { for x = 0; x < width; x++ { r, g, b, _ := dst_rgba.At(x, y).RGBA() yy, cb, cr := color.RGBToYCbCr(uint8(r), uint8(g), uint8(b)) yi := dst_ycbcr.YOffset(x, y) ci := dst_ycbcr.COffset(x, y) dst_ycbcr.Y[yi] = yy dst_ycbcr.Cb[ci] = cb dst_ycbcr.Cr[ci] = cr } } dst_image = image.Image(dst_ycbcr) } else { dst_image = image.Image(dst_rgba) } return dst_image, nil }
// Copy copies the part of the source image defined by src and sr and writes to // the part of the destination image defined by dst and the translation of sr // so that sr.Min translates to dp. func Copy(dst Image, dp image.Point, src image.Image, sr image.Rectangle, opts *Options) { mask, mp, op := image.Image(nil), image.Point{}, Over if opts != nil { // TODO: set mask, mp and op. } dr := sr.Add(dp.Sub(sr.Min)) DrawMask(dst, dr, src, sr.Min, mask, mp, op) }
func testInterlacedFailWith(t *testing.T, rgb bool) { src := readImage(t, "testdata/lenna.jpg") dst := image.Image(image.NewYCbCr(image.Rect(0, 0, 640, 480), image.YCbCrSubsampleRatio420)) if rgb { src = toRgb(src) dst = toRgb(dst) } convert(t, dst, src, false, true, NewBicubicFilter()) convert(t, dst, src, true, true, NewBicubicFilter()) }
func analyse(settings CropSettings, img image.Image, cropWidth, cropHeight, realMinScale float64) (Crop, error) { o := image.Image(image.NewRGBA(img.Bounds())) now := time.Now() edgeDetect(img, o) log.Println("Time elapsed edge:", time.Since(now)) debugOutput(settings.DebugMode, &o, "edge") now = time.Now() if settings.FaceDetection { err := faceDetect(settings, img, o) if err != nil { return Crop{}, err } log.Println("Time elapsed face:", time.Since(now)) debugOutput(settings.DebugMode, &o, "face") } else { skinDetect(img, o) log.Println("Time elapsed skin:", time.Since(now)) debugOutput(settings.DebugMode, &o, "skin") } now = time.Now() saturationDetect(img, o) log.Println("Time elapsed sat:", time.Since(now)) debugOutput(settings.DebugMode, &o, "saturation") now = time.Now() var topCrop Crop topScore := -1.0 cs := crops(o, cropWidth, cropHeight, realMinScale) log.Println("Time elapsed crops:", time.Since(now), len(cs)) now = time.Now() for _, crop := range cs { nowIn := time.Now() crop.Score = score(&o, &crop) log.Println("Time elapsed single-score:", time.Since(nowIn)) if crop.Score.Total > topScore { topCrop = crop topScore = crop.Score.Total } } log.Println("Time elapsed score:", time.Since(now)) if settings.DebugMode { drawDebugCrop(&topCrop, &o) debugOutput(true, &o, "final") } return topCrop, nil }
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 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 i0 := (y0 - dst.Rect.Min.Y) * dst.Stride for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { dpix := dst.Pix[i0:] for x, sx, mx := x0, sx0, mx0; x != x1; x, sx, mx = x+dx, 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() var dr, dg, db, da uint32 if op == Over { rgba := dpix[x-dst.Rect.Min.X] dr = uint32(rgba.R) dg = uint32(rgba.G) db = uint32(rgba.B) da = uint32(rgba.A) // 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 dr = (dr*a + sr*ma) / m dg = (dg*a + sg*ma) / m db = (db*a + sb*ma) / m da = (da*a + sa*ma) / m } else { dr = sr * ma / m dg = sg * ma / m db = sb * ma / m da = sa * ma / m } dpix[x-dst.Rect.Min.X] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} } i0 += dy * dst.Stride } }
func BenchmarkEdge(b *testing.B) { fname := "24391757.jpg" fi, _ := os.Open("./samples/" + fname) defer fi.Close() img, _, err := image.Decode(fi) if err != nil { b.Error(err) } o := image.Image(image.NewRGBA(img.Bounds())) b.ResetTimer() for i := 0; i < b.N; i++ { edgeDetect(img, o) } }
func analyse(img *image.Image, cropWidth, cropHeight, realMinScale float64) Crop { o := image.Image(image.NewRGBA((*img).Bounds())) now := time.Now() edgeDetect(img, &o) fmt.Println("Time elapsed edge:", time.Since(now)) debugOutput(&o, "edge") now = time.Now() if useFaceDetection { faceDetect(img, &o) fmt.Println("Time elapsed face:", time.Since(now)) debugOutput(&o, "face") } else { skinDetect(img, &o) fmt.Println("Time elapsed skin:", time.Since(now)) debugOutput(&o, "skin") } now = time.Now() saturationDetect(img, &o) fmt.Println("Time elapsed sat:", time.Since(now)) debugOutput(&o, "saturation") now = time.Now() var topCrop Crop topScore := -1.0 cs := crops(&o, cropWidth, cropHeight, realMinScale) fmt.Println("Time elapsed crops:", time.Since(now), len(cs)) now = time.Now() for _, crop := range cs { // nowIn := time.Now() crop.Score = score(&o, &crop) // fmt.Println("Time elapsed single-score:", time.Since(nowIn)) if crop.Score.Total > topScore { topCrop = crop topScore = crop.Score.Total } } fmt.Println("Time elapsed score:", time.Since(now)) if debug { drawDebugCrop(&topCrop, &o) } debugOutput(&o, "final") return topCrop }
func analyse(img *image.Image, cropWidth, cropHeight, realMinScale float64) Crop { o := image.Image(image.NewRGBA((*img).Bounds())) now := time.Now() edgeDetect(img, &o) fmt.Println("Time elapsed edge:", time.Since(now)) if debug { writeImageToPng(&o, "./smartcrop_edge.png") } now = time.Now() skinDetect(img, &o) fmt.Println("Time elapsed skin:", time.Since(now)) if debug { writeImageToPng(&o, "./smartcrop_skin.png") } now = time.Now() saturationDetect(img, &o) fmt.Println("Time elapsed sat:", time.Since(now)) if debug { writeImageToPng(&o, "./smartcrop_sat.png") } now = time.Now() var topCrop Crop topScore := -1.0 cs := crops(&o, cropWidth, cropHeight, realMinScale) fmt.Println("Time elapsed crops:", time.Since(now), len(cs)) now = time.Now() for _, crop := range cs { // nowIn := time.Now() crop.Score = score(&o, &crop) // fmt.Println("Time elapsed single-score:", time.Since(nowIn)) if crop.Score.Total > topScore { topCrop = crop topScore = crop.Score.Total } } fmt.Println("Time elapsed score:", time.Since(now)) if debug { drawDebugCrop(&topCrop, &o) writeImageToPng(&o, "./smartcrop_debug.png") } return topCrop }
// imgを32x32のブロックの画像に変換する func Cube(img *image.Image) image.Image { layer := image.Image(image.NewNRGBA(image.Rect(0, 0, 32, 32))) top := Rotate(img, 45) top = Resize(&top, 28, 15) left := Slide(img, 2, true, false) left = Resize(&left, 14, 24) right := Slide(img, 2, true, true) right = Resize(&right, 14, 24) Paste(&layer, 2, 0, &top) Paste(&layer, 2, 8, &left) Paste(&layer, 16, 6, &right) return layer }
func resizeEmojiGif(gifImg *gif.GIF) *gif.GIF { // Create a new RGBA image to hold the incremental frames. firstFrame := gifImg.Image[0].Bounds() b := image.Rect(0, 0, firstFrame.Dx(), firstFrame.Dy()) img := image.NewRGBA(b) resizedImage := image.Image(nil) // Resize each frame. for index, frame := range gifImg.Image { bounds := frame.Bounds() draw.Draw(img, bounds, frame, bounds.Min, draw.Over) resizedImage = resizeEmoji(img, firstFrame.Dx(), firstFrame.Dy()) gifImg.Image[index] = imageToPaletted(resizedImage) } // Set new gif width and height gifImg.Config.Width = resizedImage.Bounds().Dx() gifImg.Config.Height = resizedImage.Bounds().Dy() return gifImg }
// benchGlyph benchmarks rasterizing a TrueType glyph. // // Note that, compared to the github.com/google/font-go prototype, the height // here is the height of the bounding box, not the pixels per em used to scale // a glyph's vectors. A height of 64 corresponds to a ppem greater than 64. func benchGlyph(b *testing.B, colorModel byte, loose bool, height int, op draw.Op) { width, data := scaledBenchmarkGlyphData(height) z := NewRasterizer(width, height) bounds := z.Bounds() if loose { bounds.Max.X++ } dst, src := draw.Image(nil), image.Image(nil) switch colorModel { case 'A': dst = image.NewAlpha(bounds) src = image.Opaque case 'N': dst = image.NewNRGBA(bounds) src = image.NewUniform(color.NRGBA{0x40, 0x80, 0xc0, 0xff}) case 'R': dst = image.NewRGBA(bounds) src = image.NewUniform(color.RGBA{0x40, 0x80, 0xc0, 0xff}) default: b.Fatal("unsupported color model") } bounds = z.Bounds() b.ResetTimer() for i := 0; i < b.N; i++ { z.Reset(width, height) z.DrawOp = op for _, d := range data { switch d.n { case 0: z.MoveTo(d.px, d.py) case 1: z.LineTo(d.px, d.py) case 2: z.QuadTo(d.px, d.py, d.qx, d.qy) } } z.Draw(dst, bounds, src, image.Point{}) } }
func analyse(img *image.Image) Crop { o := image.Image(image.NewRGBA((*img).Bounds())) now := time.Now() edgeDetect(img, &o) fmt.Println("Time elapsed edge:", time.Since(now)) // writeImageToJpeg(&o, "/tmp/smartcrop_step1.jpg") now = time.Now() skinDetect(img, &o) fmt.Println("Time elapsed skin:", time.Since(now)) // writeImageToJpeg(&o, "/tmp/smartcrop_step2.jpg") now = time.Now() saturationDetect(img, &o) fmt.Println("Time elapsed sat:", time.Since(now)) // writeImageToJpeg(&o, "/tmp/smartcrop_step3.jpg") now = time.Now() var topCrop Crop topScore := -1.0 cs := crops(&o) fmt.Println("Time elapsed crops:", time.Since(now), len(cs)) now = time.Now() for _, crop := range cs { // nowIn := time.Now() crop.Score = score(&o, &crop) // fmt.Println("Time elapsed single-score:", time.Since(nowIn)) if crop.Score.Total > topScore { topCrop = crop topScore = crop.Score.Total } } fmt.Println("Time elapsed score:", time.Since(now)) return topCrop }
func windowOnClickHandler(me gxui.MouseEvent) { y := yMax - yMin // mandelbrot axis size x := xMax - xMin xp := float64(me.WindowPoint.X) // point clicked on screen in pixels yp := float64(me.WindowPoint.Y) // find point clicked in mandelbrot space xm := xMin + (xp/1024)*x ym := yMin + (yp/1024)*y // scale viewport of mandelbrot space if me.Button == gxui.MouseButtonLeft { x = x / 2 y = y / 2 } else { x = x * 2 y = y * 2 } yMax = ym + y/2 yMin = ym - y/2 xMax = xm + x/2 xMin = xm - x/2 //fmt.Print(xm, ym) //fmt.Print(yMax, yMin, xMax, xMin) source := image.Image(newMandelbrot()) rgba := image.NewRGBA(source.Bounds()) draw.Draw(rgba, source.Bounds(), source, image.ZP, draw.Src) texture = d.CreateTexture(rgba, 1) img.SetTexture(texture) window.Redraw() }
func appMain(driver gxui.Driver) { d = driver source := image.Image(newMandelbrot()) theme := flags.CreateTheme(driver) mx := source.Bounds().Max img = theme.CreateImage() window = theme.CreateWindow(mx.X, mx.Y, "Image viewer") window.SetScale(flags.DefaultScaleFactor) window.AddChild(img) rgba := image.NewRGBA(source.Bounds()) draw.Draw(rgba, source.Bounds(), source, image.ZP, draw.Src) texture = driver.CreateTexture(rgba, 1) img.SetTexture(texture) window.OnClick(windowOnClickHandler) window.OnClose(driver.Terminate) }
// Draw aligns r.Min in dst with pt in src and mask // and then replaces the rectangle r in dst with the // result of the Porter-Duff compositing operation // ``(src in mask) over dst.'' If mask is nil, the operation // simplifies to ``src over dst.'' // The implementation is simple and slow. func Draw(dst Image, r Rectangle, src, mask image.Image, pt Point) { // Plenty of room for optimizations here. dx, dy := src.Width(), src.Height() if mask != nil { if dx > mask.Width() { dx = mask.Width() } if dy > mask.Width() { dy = mask.Width() } } dx -= pt.X dy -= pt.Y if r.Dx() > dx { r.Max.X = r.Min.X + dx } if r.Dy() > dy { r.Max.Y = r.Min.Y + dy } 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(pt.Sub(r.Min))) { // Rectangles overlap: process backward? if pt.Y < r.Min.Y || pt.Y == r.Min.Y && pt.X < r.Min.X { x0, x1, dx = x1-1, x0-1, -1 y0, y1, dy = y1-1, y0-1, -1 } } var out *image.RGBA64Color for y := y0; y != y1; y += dy { for x := x0; x != x1; x += dx { sx := pt.X + x - r.Min.X sy := pt.Y + y - r.Min.Y if mask == nil { dst.Set(x, y, src.At(sx, sy)) continue } _, _, _, ma := mask.At(sx, sy).RGBA() switch ma { case 0: continue case 0xFFFFFFFF: dst.Set(x, y, src.At(sx, sy)) default: dr, dg, db, da := dst.At(x, y).RGBA() dr >>= 16 dg >>= 16 db >>= 16 da >>= 16 sr, sg, sb, sa := src.At(sx, sy).RGBA() sr >>= 16 sg >>= 16 sb >>= 16 sa >>= 16 ma >>= 16 const M = 1<<16 - 1 a := sa * ma / M dr = (dr*(M-a) + sr*ma) / M dg = (dg*(M-a) + sg*ma) / M db = (db*(M-a) + sb*ma) / M da = (da*(M-a) + sa*ma) / M if out == nil { out = new(image.RGBA64Color) } out.R = uint16(dr) out.G = uint16(dg) out.B = uint16(db) out.A = uint16(da) dst.Set(x, y, out) } } } }
func do(c appengine.Context, key string) (*bytes.Buffer, error) { client := urlfetch.Client(c) resp, err := client.Get(key) if err != nil { return nil, err } defer resp.Body.Close() c.Infof("HTTP GET returned status %v", resp.Status) img, _, err := image.Decode(resp.Body) if err != nil { return nil, err } bnds := img.Bounds() if bnds.Dx() > 1024 { c.Infof("Resizing image", bnds.Dx()) img = resize.Resize(1024, 0, img, resize.Lanczos3) } faces, err := findFaces(c, &img) // todo: should I pass back by reference? if err != nil { return nil, err } bnds = img.Bounds() m := image.NewRGBA(image.Rect(0, 0, bnds.Dx(), bnds.Dy())) draw.Draw(m, bnds, img, image.Point{0, 0}, draw.Src) brd, err := getBeardCached(c) if err != nil { return nil, err } for _, face := range faces { brd_resized := resize.Resize(uint(face.Rectangle.Width*2), 0, brd, resize.Lanczos3) brd_bnds := brd_resized.Bounds() vert := (face.Landmarks.MouthLeft.Y+face.Landmarks.MouthRight.Y)/2 - float32(brd_bnds.Dy())*0.5 rb := image.NewRGBA(image.Rect(0, 0, brd_bnds.Dx(), brd_bnds.Dy())) rad := float64(face.Attributes.Pose.Roll) * math.Pi / 180 graphics.Rotate(rb, brd_resized, &graphics.RotateOptions{rad}) mid := face.Rectangle.Left + face.Rectangle.Width/2 lt := mid - (float32(brd_bnds.Dx()) / 2) sr := image.Rect(0, 0, brd_bnds.Dx()*4, brd_bnds.Dy()*4) dp := image.Point{int(float64(lt)), int(float64(vert))} rt := image.Rectangle{dp, dp.Add(sr.Size())} draw.Draw(m, rt, rb, sr.Min, draw.Over) } img_out := image.Image(m) buffer := new(bytes.Buffer) if err := jpeg.Encode(buffer, img_out, nil); err != nil { return nil, err } return buffer, nil }
func TestRectDstMask(t *testing.T) { f, err := os.Open("../testdata/testpattern.png") if err != nil { t.Fatalf("Open: %v", err) } defer f.Close() src, _, err := image.Decode(f) if err != nil { t.Fatalf("Decode: %v", err) } m00 := transformMatrix(1, 0, 0) bounds := image.Rect(0, 0, 50, 50) dstOutside := image.NewRGBA(bounds) for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { dstOutside.SetRGBA(x, y, color.RGBA{uint8(5 * x), uint8(5 * y), 0x00, 0xff}) } } mk := func(q Transformer, dstMask image.Image, dstMaskP image.Point) *image.RGBA { m := image.NewRGBA(bounds) Copy(m, bounds.Min, dstOutside, bounds, Src, nil) q.Transform(m, m00, src, src.Bounds(), Over, &Options{ DstMask: dstMask, DstMaskP: dstMaskP, }) return m } qs := []Interpolator{ NearestNeighbor, ApproxBiLinear, CatmullRom, } dstMaskPs := []image.Point{ {0, 0}, {5, 7}, {-3, 0}, } rect := image.Rect(10, 10, 30, 40) for _, q := range qs { for _, dstMaskP := range dstMaskPs { dstInside := mk(q, nil, image.Point{}) for _, wrap := range []bool{false, true} { // TODO: replace "rectImage(rect)" with "rect" once Go 1.5 is // released, where an image.Rectangle implements image.Image. dstMask := image.Image(rectImage(rect)) if wrap { dstMask = srcWrapper{dstMask} } dst := mk(q, dstMask, dstMaskP) nError := 0 loop: for y := bounds.Min.Y; y < bounds.Max.Y; y++ { for x := bounds.Min.X; x < bounds.Max.X; x++ { which := dstOutside if (image.Point{x, y}).Add(dstMaskP).In(rect) { which = dstInside } if got, want := dst.RGBAAt(x, y), which.RGBAAt(x, y); got != want { if nError == 10 { t.Errorf("q=%T dmp=%v wrap=%v: ...and more errors", q, dstMaskP, wrap) break loop } nError++ t.Errorf("q=%T dmp=%v wrap=%v: x=%3d y=%3d: got %v, want %v", q, dstMaskP, wrap, x, y, got, want) } } } } } } }
// Resize an image to be w wide and h high func resize(original image.Image, w, h int) (image.Image, error) { src, ok := original.(*image.RGBA) if ok == false { b := original.Bounds() src = image.NewRGBA(b) draw.Draw(src, b, original, b.Min, draw.Src) } width := src.Bounds().Dx() height := src.Bounds().Dy() if width < 1 || height < 1 { return image.Image(src), fmt.Errorf("Image dimensions invalid -- %v, %v", width, height) } if h == 0 { // Maintain aspect ratio h = int(float32(w) / (float32(width) / float32(height))) } if w < 1 || h < 1 { return image.Image(src), fmt.Errorf("Resize values invalid -- %v, %v", w, h) } dst := image.NewRGBA(image.Rect(0, 0, w, h)) xRatio := float32(width) / float32(w) yRatio := float32(height) / float32(h) if width > w { // Blend pixels from larger source in smaller destination image b := src.Bounds() i := src.PixOffset(0, 0) checklist := make([]bool, len(src.Pix)>>2) for y := b.Min.Y; y < b.Max.Y; y++ { oy := int(float32(y) / yRatio) for x := b.Min.X; x < b.Max.X; x++ { ox := int(float32(x) / xRatio) o := dst.PixOffset(ox, oy) if !checklist[o>>2] { // Untouched pixel, do initial paint checklist[o>>2] = true dst.Pix[o+0] = src.Pix[i+0] dst.Pix[o+1] = src.Pix[i+1] dst.Pix[o+2] = src.Pix[i+2] dst.Pix[o+3] = src.Pix[i+3] } else { // Pixel already seen, paint with average blend dst.Pix[o+0] = uint8((uint64(dst.Pix[o+0]) + uint64(src.Pix[i+0])) >> 1) dst.Pix[o+1] = uint8((uint64(dst.Pix[o+1]) + uint64(src.Pix[i+1])) >> 1) dst.Pix[o+2] = uint8((uint64(dst.Pix[o+2]) + uint64(src.Pix[i+2])) >> 1) dst.Pix[o+3] = uint8((uint64(dst.Pix[o+3]) + uint64(src.Pix[i+3])) >> 1) } i += 4 } } } else { // Destination image larger than source, no blend required b := dst.Bounds() i := dst.PixOffset(0, 0) for y := b.Min.Y; y < b.Max.Y; y++ { oy := int(float32(y) * yRatio) for x := b.Min.X; x < b.Max.X; x++ { ox := int(float32(x) * xRatio) o := src.PixOffset(ox, oy) dst.Pix[i+0] = src.Pix[o+0] dst.Pix[i+1] = src.Pix[o+1] dst.Pix[i+2] = src.Pix[o+2] dst.Pix[i+3] = src.Pix[o+3] i += 4 } } } return dst, nil }
func (d *decoder) decode(r io.Reader, full bool) error { if rr, ok := r.(reader); ok { d.r = rr } else { d.r = bufio.NewReader(r) } // Check for DDS magic number _, err := io.ReadFull(d.r, d.tmp[:4]) if err != nil { return err } ident := string(d.tmp[0:4]) if ident != "DDS " { return fmt.Errorf("dds: wrong magic number") } // Decode the DDS header err = d.decodeHeader() if err != nil { return err } // Check if it's a supported format // For now, we'll only support DXT1,DXT3,DXT5 neededFlags := uint32(DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT) if d.h.Flags&neededFlags != neededFlags { return fmt.Errorf("dds: file header is missing necessary dds flags") } // Sanitize mipmap count if d.h.Flags&DDSD_MIPMAPCOUNT == 0 { d.h.MipMapCount = 1 } if !full { return nil } switch { case d.h.Ddspf.Flags&DDPF_FOURCC != 0: switch d.h.Ddspf.FourCC { case FOURCC_DXT1: d.img = make([]image.Image, d.h.MipMapCount) w, h := int(d.h.Width), int(d.h.Height) for i := 0; i < int(d.h.MipMapCount); i++ { //fmt.Printf("mipmap %v is %vx%v\n", i, w, h) img := glimage.NewDxt1(image.Rect(0, 0, w, h)) _, err = io.ReadFull(d.r, img.Pix) if err != nil { return err } d.img[i] = image.Image(img) w >>= 1 h >>= 1 } case FOURCC_DXT3: d.img = make([]image.Image, d.h.MipMapCount) w, h := int(d.h.Width), int(d.h.Height) for i := 0; i < int(d.h.MipMapCount); i++ { //fmt.Printf("mipmap %v is %vx%v\n", i, w, h) img := glimage.NewDxt3(image.Rect(0, 0, w, h)) _, err = io.ReadFull(d.r, img.Pix) if err != nil { return err } d.img[i] = image.Image(img) w >>= 1 h >>= 1 } case FOURCC_DXT5: d.img = make([]image.Image, d.h.MipMapCount) w, h := int(d.h.Width), int(d.h.Height) for i := 0; i < int(d.h.MipMapCount); i++ { //fmt.Printf("mipmap %v is %vx%v\n", i, w, h) img := glimage.NewDxt5(image.Rect(0, 0, w, h)) _, err = io.ReadFull(d.r, img.Pix) if err != nil { return err } d.img[i] = image.Image(img) w >>= 1 h >>= 1 } default: return fmt.Errorf("dds: unrecognized format %v", d.h.Ddspf) } case d.h.Ddspf.Flags&DDPF_RGB != 0: // Color formats if d.h.Ddspf.Flags&DDPF_ALPHAPIXELS != 0 { // Color formats with alpha switch { // A8R8G8B8 case d.h.Ddspf.RBitMask == 0x00FF0000 && d.h.Ddspf.GBitMask == 0x0000FF00 && d.h.Ddspf.BBitMask == 0x000000FF && d.h.Ddspf.ABitMask == 0xFF000000: d.img = make([]image.Image, d.h.MipMapCount) w, h := int(d.h.Width), int(d.h.Height) for i := 0; i < int(d.h.MipMapCount); i++ { img := glimage.NewBGRA(image.Rect(0, 0, w, h)) _, err = io.ReadFull(d.r, img.Pix) if err != nil { return err } d.img[i] = image.Image(img) w >>= 1 h >>= 1 } // A4R4G4B4 case d.h.Ddspf.RBitMask == 0x0F00 && d.h.Ddspf.GBitMask == 0x00F0 && d.h.Ddspf.BBitMask == 0x000F && d.h.Ddspf.ABitMask == 0xF000: d.img = make([]image.Image, d.h.MipMapCount) w, h := int(d.h.Width), int(d.h.Height) for i := 0; i < int(d.h.MipMapCount); i++ { img := glimage.NewBGRA4444(image.Rect(0, 0, w, h)) err = binary.Read(d.r, binary.LittleEndian, &img.Pix) if err != nil { return err } d.img[i] = image.Image(img) w >>= 1 h >>= 1 } // A1R5G5B5 case d.h.Ddspf.RBitMask == 0x7C00 && d.h.Ddspf.GBitMask == 0x03E0 && d.h.Ddspf.BBitMask == 0x001F && d.h.Ddspf.ABitMask == 0x8000: d.img = make([]image.Image, d.h.MipMapCount) w, h := int(d.h.Width), int(d.h.Height) for i := 0; i < int(d.h.MipMapCount); i++ { img := glimage.NewBGRA5551(image.Rect(0, 0, w, h)) err = binary.Read(d.r, binary.LittleEndian, &img.Pix) if err != nil { return err } d.img[i] = image.Image(img) w >>= 1 h >>= 1 } default: return fmt.Errorf("dds: unrecognized format %v", d.h.Ddspf) } } else { // Color formats without alpha switch { // R5G6B5 case d.h.Ddspf.RBitMask == 0xF800 && d.h.Ddspf.GBitMask == 0x07E0 && d.h.Ddspf.BBitMask == 0x001F && d.h.Ddspf.ABitMask == 0x0000: d.img = make([]image.Image, d.h.MipMapCount) w, h := int(d.h.Width), int(d.h.Height) for i := 0; i < int(d.h.MipMapCount); i++ { img := glimage.NewBGR565(image.Rect(0, 0, w, h)) err = binary.Read(d.r, binary.LittleEndian, &img.Pix) if err != nil { return err } d.img[i] = image.Image(img) w >>= 1 h >>= 1 } default: return fmt.Errorf("dds: unrecognized format %v", d.h.Ddspf) } } default: return fmt.Errorf("dds: unrecognized format %v", d.h.Ddspf) } return nil }
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. // The implementation is simple and slow. // TODO(nigeltao): Optimize this. func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) { dx, dy := src.Width()-sp.X, src.Height()-sp.Y if mask != nil { if dx > mask.Width()-mp.X { dx = mask.Width() - mp.X } if dy > mask.Height()-mp.Y { dy = mask.Height() - mp.Y } } if r.Dx() > dx { r.Max.X = r.Min.X + dx } if r.Dy() > dy { r.Max.Y = r.Min.Y + dy } // TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y. // TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y. // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. if dst0, ok := dst.(*image.RGBA); ok { if op == Over { // TODO(nigeltao): Implement a fast path for font glyphs (i.e. when mask is an image.Alpha). } else { if mask == nil { if src0, ok := src.(image.ColorImage); ok { drawFill(dst0, r, src0) return } if src0, ok := src.(*image.RGBA); ok { if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) { // TODO(nigeltao): Implement a fast path for the overlapping case. } else { drawCopy(dst0, r, src0, sp) return } } } } } 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))) { // Rectangles overlap: process backward? 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 } } var out *image.RGBA64Color sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { sx := sp.X + x0 - r.Min.X mx := mp.X + x0 - r.Min.X for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { // A nil mask is equivalent to a fully opaque, infinitely large mask. // We work in 16-bit color, so that multiplying two values does not overflow a uint32. const M = 1<<16 - 1 ma := uint32(M) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() ma >>= 16 } switch { case ma == 0: if op == Over { // No-op. } else { dst.Set(x, y, zeroColor) } case ma == M && op == Src: dst.Set(x, y, src.At(sx, sy)) default: sr, sg, sb, sa := src.At(sx, sy).RGBA() sr >>= 16 sg >>= 16 sb >>= 16 sa >>= 16 if out == nil { out = new(image.RGBA64Color) } if op == Over { dr, dg, db, da := dst.At(x, y).RGBA() dr >>= 16 dg >>= 16 db >>= 16 da >>= 16 a := M - (sa * ma / M) out.R = uint16((dr*a + sr*ma) / M) out.G = uint16((dg*a + sg*ma) / M) out.B = uint16((db*a + sb*ma) / M) out.A = uint16((da*a + sa*ma) / M) } else { out.R = uint16(sr * ma / M) out.G = uint16(sg * ma / M) out.B = uint16(sb * ma / M) out.A = uint16(sa * ma / M) } dst.Set(x, y, out) } } } }
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { clip(dst, &r, src, &sp, mask, &mp) if r.Empty() { return } // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. if dst0, ok := dst.(*image.RGBA); ok { if op == Over { if mask == nil { switch src0 := src.(type) { case *image.ColorImage: drawFillOver(dst0, r, src0) return case *image.RGBA: drawCopyOver(dst0, r, src0, sp) return case *image.NRGBA: drawNRGBAOver(dst0, r, src0, sp) return case *ycbcr.YCbCr: drawYCbCr(dst0, r, src0, sp) return } } else if mask0, ok := mask.(*image.Alpha); ok { switch src0 := src.(type) { case *image.ColorImage: drawGlyphOver(dst0, r, src0, mask0, mp) return } } } else { if mask == nil { switch src0 := src.(type) { case *image.ColorImage: drawFillSrc(dst0, r, src0) return case *image.RGBA: drawCopySrc(dst0, r, src0, sp) return case *image.NRGBA: drawNRGBASrc(dst0, r, src0, sp) return case *ycbcr.YCbCr: drawYCbCr(dst0, r, src0, sp) return } } } drawRGBA(dst0, r, src, sp, mask, mp, op) return } 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))) { // Rectangles overlap: process backward? 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 } } var out *image.RGBA64Color sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { sx := sp.X + x0 - r.Min.X mx := mp.X + x0 - r.Min.X for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { ma := uint32(m) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() } switch { case ma == 0: if op == Over { // No-op. } else { dst.Set(x, y, zeroColor) } case ma == m && op == Src: dst.Set(x, y, src.At(sx, sy)) default: sr, sg, sb, sa := src.At(sx, sy).RGBA() if out == nil { out = new(image.RGBA64Color) } if op == Over { dr, dg, db, da := dst.At(x, y).RGBA() a := m - (sa * ma / m) out.R = uint16((dr*a + sr*ma) / m) out.G = uint16((dg*a + sg*ma) / m) out.B = uint16((db*a + sb*ma) / m) out.A = uint16((da*a + sa*ma) / m) } else { out.R = uint16(sr * ma / m) out.G = uint16(sg * ma / m) out.B = uint16(sb * ma / m) out.A = uint16(sa * ma / m) } dst.Set(x, y, out) } } } }
// Convert a JSON Hyper-Schema compliant image media object into an image.Image. // Returns the image, or a non-nil error if there was a problem. func ObjectToImage(v interface{}) (image.Image, error) { return image.Image(image.NewGray(image.ZR)), nil }
func processBackward(dst Image, r image.Rectangle, src image.Image, sp image.Point) bool { return image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) && (sp.Y < r.Min.Y || (sp.Y == r.Min.Y && sp.X < r.Min.X)) }
// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. // The implementation is simple and slow. // TODO(nigeltao): Optimize this. func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Image, mp Point, op Op) { dx, dy := src.Width()-sp.X, src.Height()-sp.Y if mask != nil { if dx > mask.Width()-mp.X { dx = mask.Width() - mp.X } if dy > mask.Height()-mp.Y { dy = mask.Height() - mp.Y } } if r.Dx() > dx { r.Max.X = r.Min.X + dx } if r.Dy() > dy { r.Max.Y = r.Min.Y + dy } // TODO(nigeltao): Clip r to dst's bounding box, and handle the case when sp or mp has negative X or Y. // TODO(nigeltao): Ensure that r is well formed, i.e. r.Max.X >= r.Min.X and likewise for Y. // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. switch dst0 := dst.(type) { case *image.RGBA: if op == Over { if mask == nil { if src0, ok := src.(image.Uniform); ok { drawFillOver(dst0, r, src0) return } if src0, ok := src.(*image.RGBA); ok { if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) { // TODO(nigeltao): Implement a fast path for the overlapping case. } else { drawCopyOver(dst0, r, src0, sp) return } } } else if mask0, ok := mask.(*image.Alpha); ok { if src0, ok := src.(image.Uniform); ok { drawGlyphOver(dst0, r, src0, mask0, mp) return } } } else { if mask == nil { if src0, ok := src.(image.Uniform); ok { drawFillSrc(dst0, r, src0) return } if src0, ok := src.(*image.RGBA); ok { if dst0 == src0 && r.Overlaps(r.Add(sp.Sub(r.Min))) { // TODO(nigeltao): Implement a fast path for the overlapping case. } else { drawCopySrc(dst0, r, src0, sp) return } } } } drawRGBA(dst0, r, src, sp, mask, mp, op) return case DrawMasker: // Destination might wish to perform the draw operation itself if dst0.DrawMask(r, src, sp, mask, mp, op) { return } } 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))) { // Rectangles overlap: process backward? 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 } } var out *color.RGBA64 sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { sx := sp.X + x0 - r.Min.X mx := mp.X + x0 - r.Min.X for x := x0; x != x1; x, sx, mx = x+dx, sx+dx, mx+dx { ma := uint32(m) if mask != nil { _, _, _, ma = mask.At(mx, my).RGBA() } switch { case ma == 0: if op == Over { // No-op. } else { dst.Set(x, y, zeroColor) } case ma == m && op == Src: dst.Set(x, y, src.At(sx, sy)) default: sr, sg, sb, sa := src.At(sx, sy).RGBA() if out == nil { out = new(color.RGBA64) } if op == Over { dr, dg, db, da := dst.At(x, y).RGBA() a := m - (sa * ma / m) out.R = uint16((dr*a + sr*ma) / m) out.G = uint16((dg*a + sg*ma) / m) out.B = uint16((db*a + sb*ma) / m) out.A = uint16((da*a + sa*ma) / m) } else { out.R = uint16(sr * ma / m) out.G = uint16(sg * ma / m) out.B = uint16(sb * ma / m) out.A = uint16(sa * ma / m) } dst.Set(x, y, out) } } } }