From 5c2c57e5dbfab67072cad83e7127035568ee3c8f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 29 Sep 2009 14:04:08 -0700 Subject: [PATCH] toy draw implementation R=r DELTA=471 (471 added, 0 deleted, 0 changed) OCL=35090 CL=35099 --- usr/rsc/draw/Makefile | 14 ++++ usr/rsc/draw/arith.go | 168 ++++++++++++++++++++++++++++++++++++++++++ usr/rsc/draw/color.go | 117 +++++++++++++++++++++++++++++ usr/rsc/draw/draw.go | 125 +++++++++++++++++++++++++++++++ usr/rsc/draw/event.go | 47 ++++++++++++ 5 files changed, 471 insertions(+) create mode 100644 usr/rsc/draw/Makefile create mode 100644 usr/rsc/draw/arith.go create mode 100644 usr/rsc/draw/color.go create mode 100644 usr/rsc/draw/draw.go create mode 100644 usr/rsc/draw/event.go diff --git a/usr/rsc/draw/Makefile b/usr/rsc/draw/Makefile new file mode 100644 index 0000000000..865fae1038 --- /dev/null +++ b/usr/rsc/draw/Makefile @@ -0,0 +1,14 @@ +# 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. + +include $(GOROOT)/src/Make.$(GOARCH) + +TARG=draw +GOFILES=\ + arith.go\ + color.go\ + draw.go\ + event.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/usr/rsc/draw/arith.go b/usr/rsc/draw/arith.go new file mode 100644 index 0000000000..4c09965977 --- /dev/null +++ b/usr/rsc/draw/arith.go @@ -0,0 +1,168 @@ +// 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 + +// A Point is an X, Y coordinate pair. +type Point struct { + X, Y int; +} + +// ZP is the zero Point. +var ZP Point + +// A Rectangle contains the Points with Min.X <= X < Max.X, Min.Y <= Y < Max.Y. +type Rectangle struct { + Min, Max Point; +} + +// ZR is the zero Rectangle. +var ZR Rectangle + +// Pt is shorthand for Point{X, Y}. +func Pt(X, Y int) Point { + return Point{X, Y} +} + +// Rect is shorthand for Rectangle{Pt(x0, y0), Pt(x1, y1)}. +func Rect(x0, y0, x1, y1 int) Rectangle { + return Rectangle{Point{x0,y0}, Point{x1,y1}} +} + +// Rpt is shorthand for Rectangle{min, max}. +func Rpt(min, max Point) Rectangle { + return Rectangle{min, max} +} + +// Add returns the sum of p and q: Pt(p.X+q.X, p.Y+q.Y). +func (p Point) Add(q Point) Point { + return Point{p.X+q.X, p.Y+q.Y} +} + +// Sub returns the difference of p and q: Pt(p.X-q.X, p.Y-q.Y). +func (p Point) Sub(q Point) Point { + return Point{p.X-q.X, p.Y-q.Y} +} + +// Mul returns p scaled by k: Pt(p.X*k p.Y*k). +func (p Point) Mul(k int) Point { + return Point{p.X*k, p.Y*k} +} + +// Div returns p divided by k: Pt(p.X/k, p.Y/k). +func (p Point) Div(k int) Point { + return Point{p.X/k, p.Y/k} +} + +// Eq returns true if p and q are equal. +func (p Point) Eq(q Point) bool { + return p.X == q.X && p.Y == q.Y +} + +// Inset returns the rectangle r inset by n: Rect(r.Min.X+n, r.Min.Y+n, r.Max.X-n, r.Max.Y-n). +func (r Rectangle) Inset(n int) Rectangle { + return Rectangle{Point{r.Min.X+n, r.Min.Y+n}, Point{r.Max.X-n, r.Max.Y-n}} +} + +// Add returns the rectangle r translated by p: Rpt(r.Min.Add(p), r.Max.Add(p)). +func (r Rectangle) Add(p Point) Rectangle { + return Rectangle{r.Min.Add(p), r.Max.Add(p)} +} + +// Sub returns the rectangle r translated by -p: Rpt(r.Min.Sub(p), r.Max.Sub(p)). +func (r Rectangle) Sub(p Point) Rectangle { + return Rectangle{r.Min.Sub(p), r.Max.Sub(p)} +} + +// Canon returns a canonical version of r: the returned rectangle +// has Min.X <= Max.X and Min.Y <= Max.Y. +func (r Rectangle) Canon() Rectangle { + if r.Max.X < r.Min.X { + r.Max.X = r.Min.X + } + if r.Max.Y < r.Min.Y { + r.Max.Y = r.Min.Y + } + return r; +} + +// Overlaps returns true if r and r1 cross; that is, it returns true if they share any point. +func (r Rectangle) Overlaps(r1 Rectangle) bool { + return r.Min.X < r1.Max.X && r1.Min.X < r.Max.X + && r.Min.Y < r1.Max.Y && r1.Min.Y < r.Max.Y +} + +// Empty retruns true if r contains no points. +func (r Rectangle) Empty() bool { + return r.Max.X <= r.Min.X || r.Max.Y <= r.Min.Y +} + +// InRect returns true if all the points in r are also in r1. +func (r Rectangle) In(r1 Rectangle) bool { + if r.Empty() { + return true + } + if r1.Empty() { + return false + } + return r1.Min.X <= r.Min.X && r.Max.X <= r1.Max.X + && r1.Min.Y <= r.Min.Y && r.Max.Y <= r1.Max.Y +} + +// Combine returns the smallest rectangle containing all points from r and from r1. +func (r Rectangle) Combine(r1 Rectangle) Rectangle { + if r.Empty() { + return r1 + } + if r1.Empty() { + return r + } + if r.Min.X > r1.Min.X { + r.Min.X = r1.Min.X + } + if r.Min.Y > r1.Min.Y { + r.Min.Y = r1.Min.Y + } + if r.Max.X < r1.Max.X { + r.Max.X = r1.Max.X + } + if r.Max.Y < r1.Max.Y { + r.Max.Y = r1.Max.Y + } + return r +} + +// Clip returns the largest rectangle containing only points shared by r and r1. +func (r Rectangle) Clip(r1 Rectangle) Rectangle { + if r.Empty() { + return r + } + if r1.Empty() { + return r1 + } + if r.Min.X < r1.Min.X { + r.Min.X = r1.Min.X + } + if r.Min.Y < r1.Min.Y { + r.Min.Y = r1.Min.Y + } + if r.Max.X > r1.Max.X { + r.Max.X = r1.Max.X + } + if r.Max.Y > r1.Max.Y { + r.Max.Y = r1.Max.Y + } + return r; +} + +// Dx returns the width of the rectangle r: r.Max.X - r.Min.X. +func (r Rectangle) Dx() int { + return r.Max.X - r.Min.X +} + +// Dy returns the width of the rectangle r: r.Max.Y - r.Min.Y. +func (r Rectangle) Dy() int { + return r.Max.Y - r.Min.Y +} + diff --git a/usr/rsc/draw/color.go b/usr/rsc/draw/color.go new file mode 100644 index 0000000000..bae35eba5c --- /dev/null +++ b/usr/rsc/draw/color.go @@ -0,0 +1,117 @@ +// 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 + +import "image" + +// A Color represents a color with 8-bit R, G, B, and A values, +// packed into a uint32—0xRRGGBBAA—so that comparison +// is defined on colors. +// Color implements image.Color. +// Color also implements image.Image: it is a +// 10⁹x10⁹-pixel image of uniform color. +type Color uint32 + +// Check that Color implements image.Color and image.Image +var _ image.Color = Black +var _ image.Image = Black + +var ( + Opaque Color = 0xFFFFFFFF; + Transparent Color = 0x00000000; + Black Color = 0x000000FF; + White Color = 0xFFFFFFFF; + Red Color = 0xFF0000FF; + Green Color = 0x00FF00FF; + Blue Color = 0x0000FFFF; + Cyan Color = 0x00FFFFFF; + Magenta Color = 0xFF00FFFF; + Yellow Color = 0xFFFF00FF; + PaleYellow Color = 0xFFFFAAFF; + DarkYellow Color = 0xEEEE9EFF; + DarkGreen Color = 0x448844FF; + PaleGreen Color = 0xAAFFAAFF; + MedGreen Color = 0x88CC88FF; + DarkBlue Color = 0x000055FF; + PaleBlueGreen Color = 0xAAFFFFFF; + PaleBlue Color = 0x0000BBFF; + BlueGreen Color = 0x008888FF; + GreyGreen Color = 0x55AAAAFF; + PaleGreyGreen Color = 0x9EEEEEFF; + YellowGreen Color = 0x99994CFF; + MedBlue Color = 0x000099FF; + GreyBlue Color = 0x005DBBFF; + PaleGreyBlue Color = 0x4993DDFF; + PurpleBlue Color = 0x8888CCFF; +) + +func (c Color) RGBA() (r, g, b, a uint32) { + x := uint32(c); + r, g, b, a = x>>24, (x>>16)&0xFF, (x>>8)&0xFF, x&0xFF; + r |= r<<8; + r |= r<<16; + g |= g<<8; + g |= g<<16; + b |= b<<8; + b |= b<<16; + a |= a<<8; + a |= a<<16; + return; +} + +// SetAlpha returns the color obtained by changing +// c's alpha value to a and scaling r, g, and b appropriately. +func (c Color) SetAlpha(a uint8) Color { + r, g, b, oa := c>>24, (c>>16)&0xFF, (c>>8)&0xFF, c&0xFF; + if oa == 0 { + return 0 + } + r = r*Color(a)/oa; + if r < 0 { + r = 0; + } + if r > 0xFF { + r = 0xFF; + } + g = g*Color(a)/oa; + if g < 0 { + g = 0; + } + if g > 0xFF { + g = 0xFF; + } + b = b*Color(a)/oa; + if b < 0 { + b = 0; + } + if b > 0xFF { + b = 0xFF; + } + return r<<24 | g<<16 | b<<8 | Color(a); +} + +func (c Color) Width() int { + return 1e9; +} + +func (c Color) Height() int { + return 1e9; +} + +func (c Color) At(x, y int) image.Color { + return c; +} + +func toColor(color image.Color) image.Color { + if c, ok := color.(Color); ok { + return c; + } + r, g, b, a := color.RGBA(); + return Color(r>>24<<24 | g>>24<<16 | b>>24<<8 | a>>24); +} + +func (c Color) ColorModel() image.ColorModel { + return image.ColorModelFunc(toColor); +} diff --git a/usr/rsc/draw/draw.go b/usr/rsc/draw/draw.go new file mode 100644 index 0000000000..4fc6a94ca6 --- /dev/null +++ b/usr/rsc/draw/draw.go @@ -0,0 +1,125 @@ +// 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 +} diff --git a/usr/rsc/draw/event.go b/usr/rsc/draw/event.go new file mode 100644 index 0000000000..b5bce50788 --- /dev/null +++ b/usr/rsc/draw/event.go @@ -0,0 +1,47 @@ +// 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 + +// A Context represents a single graphics window. +type Context interface { + // Screen returns an editable Image of window. + Screen() Image; + + // FlushImage flushes changes made to Screen() back to screen. + FlushImage(); + + // KeyboardChan returns a channel carrying keystrokes. + // An event is sent each time a key is pressed or released. + // The value k represents key k being pressed. + // The value -k represents key k being released. + // The specific set of key values is not specified, + // but ordinary character represent themselves. + KeyboardChan() <-chan int; + + // MouseChan returns a channel carrying mouse events. + // A new event is sent each time the mouse moves or a + // button is pressed or released. + MouseChan() <-chan Mouse; + + // ResizeChan returns a channel carrying resize events. + // An event is sent each time the window is resized; + // the client should respond by calling Screen() to obtain + // the new screen image. + // The value sent on the channel is always ``true'' and can be ignored. + ResizeChan() <-chan bool; + + // QuitChan returns a channel carrying quit requests. + // After reading a value from the quit channel, the application + // should exit. + QuitChan() <-chan bool; +} + +// A Mouse represents the state of the mouse. +type Mouse struct { + Buttons int; // bit mask of buttons: 1<<0 is left, 1<<1 middle, 1<<2 right + Point; // location of cursor + Nsec int64; // time stamp +} + -- 2.48.1