mirror of
https://github.com/golang/go
synced 2024-11-26 04:27:58 -07:00
126 lines
3.7 KiB
Go
126 lines
3.7 KiB
Go
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
// Package draw provides basic graphics and drawing primitives,
|
||
|
// in the style of the Plan 9 graphics library
|
||
|
// (see http://plan9.bell-labs.com/magic/man2html/2/draw)
|
||
|
// and the X Render extension.
|
||
|
package draw
|
||
|
|
||
|
// BUG(rsc): This is a toy library and not ready for production use.
|
||
|
|
||
|
import "image"
|
||
|
|
||
|
// A draw.Image is an image.Image with a Set method to change a single pixel.
|
||
|
type Image interface {
|
||
|
image.Image;
|
||
|
Set(x, y int, c image.Color);
|
||
|
}
|
||
|
|
||
|
// 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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Border aligns r.Min in dst with sp in src and then replaces pixels
|
||
|
// in a w-pixel border around r in dst with the result of the Porter-Duff compositing
|
||
|
// operation ``src over dst.'' If w is positive, the border extends w pixels inside r.
|
||
|
// If w is negative, the border extends w pixels outside r.
|
||
|
func Border(dst Image, r Rectangle, w int, src image.Image, sp Point) {
|
||
|
i := w;
|
||
|
if i > 0 {
|
||
|
// inside r
|
||
|
Draw(dst, Rect(r.Min.X, r.Min.Y, r.Max.X, r.Min.Y+i), src, nil, sp); // top
|
||
|
Draw(dst, Rect(r.Min.X, r.Min.Y+i, r.Min.X+i, r.Max.Y-i), src, nil, sp.Add(Pt(0, i))); // left
|
||
|
Draw(dst, Rect(r.Max.X-i, r.Min.Y+i, r.Max.X, r.Max.Y-i), src, nil, sp.Add(Pt(r.Dx()-i, i))); // right
|
||
|
Draw(dst, Rect(r.Min.X, r.Max.Y-i, r.Max.X, r.Max.Y), src, nil, sp.Add(Pt(0, r.Dy()-i))); // bottom
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// outside r;
|
||
|
i = -i;
|
||
|
Draw(dst, Rect(r.Min.X-i, r.Min.Y-i, r.Max.X+i, r.Min.Y), src, nil, sp.Add(Pt(-i, -i))); // top
|
||
|
Draw(dst, Rect(r.Min.X-i, r.Min.Y, r.Min.X, r.Max.Y), src, nil, sp.Add(Pt(-i, 0))); // left
|
||
|
Draw(dst, Rect(r.Max.X, r.Min.Y, r.Max.X+i, r.Max.Y), src, nil, sp.Add(Pt(r.Dx(), 0))); // right
|
||
|
Draw(dst, Rect(r.Min.X-i, r.Max.Y, r.Max.X+i, r.Max.Y+i), src, nil, sp.Add(Pt(-i, 0))); // bottom
|
||
|
}
|