Package xgraphics

import "github.com/BurntSushi/xgbutil/xgraphics"
Overview
Index

Overview ▾

Package xgraphics defines an X image type and provides convenience functions for reading and writing X pixmaps and bitmaps. It is a work-in-progress, and while it works for some common X server configurations, it does not work for all X server configurations. Package xgraphics also provides some support for drawing text on to images using freetype-go, scaling images using graphics-go, simple alpha blending, finding EWMH and ICCCM window icons and efficiently drawing any image into an X pixmap. (Where "efficient" means being able to specify sub-regions of images to draw, so that the entire image isn't sent to X.) If more elaborate image routines are required, I recommend using draw2d. (The xgraphics.Image type satisfies the draw.Image interface, which allows it to work with draw2d.)

In general, xgraphics paints pixmaps to windows using using the BackPixmap approach. (Setting the background pixmap of the window to the pixmap containing your image, and clearing the window's background when the pixmap is updated.) It also provides experimental support for another mechanism: copying the contents of your image's pixmap directly to the window. (This requires responding to expose events to redraw the pixmap.) The former approach requires less book-keeping, but supposedly has some issues with some video cards. The latter approach is probably more reliable, but requires more book-keeping.

Note that while text drawing functions are provided, it is not necessary to use them to write text on images. Namely, there is nothing X specific about them. They are strictly for convenience.

A quick example

This is a simple example the converts any value satisfying the image.Image interface into an *xgraphics.Image value, and creates a new window with that image painted in the window. (The XShow function probably doesn't have any practical applications outside serving as an example, but can be useful for debugging what an image looks like.)

imgFile, err := os.Open(imgPath)
if err != nil {
	log.Fatal(err)
}

img, _, err := image.Decode(imgFile)
if err != nil {
	log.Fatal(err)
}

ximg := xgraphics.NewConvert(XUtilValue, img)
ximg.XShow()

A complete working example named 'show-image' that's similar to this can be found in the examples directory of the xgbutil package. More involved examples, 'show-window-icons' and 'pointer-painting', are also provided.

Portability

The xgraphics package *assumes* a particular kind of X server configuration. Namely, this configuration specifies bits per pixel, image byte order, bitmap bit order, scanline padding and unit length, image depth and so on. Handling all of the possible values for each configuration option will greatly inflate the code, but is on the TODO list.

I am undecided (perhaps because I haven't thought about it too much) about whether to hide these configuration details behind multiple xgraphics.Image types or hiding everything inside one xgraphics.Image type. I lean toward the latter because the former requires a large number of types (and therefore a lot of code duplication). One design decision that I've already made is that images should be converted to the format used by the X server (xgraphics currently assumes this is BGRx) once when the image is created. Without this, an xgraphics.Image type wouldn't be required, and images would have to be converted to the X image format every time an image is drawn into a pixmap. This results in a lot of overhead. Moreover, Go's interfaces allow an xgraphics.Image type to work anywhere that an image.Image or a draw.Image value is expected.

The obvious down-side to this approach is that optimizations made in image drawing routines in other libraries won't be able to apply to xgraphics.Image values (since the optimizations are probably hard-coded for image types declared in Go's standard library). This isn't well suited to the process of creating some canvas to draw on, and using another library to draw on the canvas. (At least, it won't be as fast as possible.) I can't think of any way around this, other than having the library add an optimization step specifically for xgraphics.Image values. Of course, the other approach is to convert image formats only when drawing to X and completely subvert the xgraphics.Image type, but this seems worse than unoptimized image drawing routines. (Unfortunately, both things need to be fast.)

If your X server is not configured to what the xgraphics package expects, messages will be emitted to stderr when a new xgraphics.Image value is created. If you see any of these messages, please report them to xgbutil's project page: https://github.com/BurntSushi/xgbutil.

Index ▾

Variables
func Alpha(dest *Image, alpha int)
func Blend(dest *Image, src image.Image, sp image.Point)
func BlendBgColor(dest *Image, c color.Color)
func Extents(font *truetype.Font, fontSize float64, text string) (int, int)
func FindBestEwmhIcon(width, height int, icons []ewmh.WmIcon) *ewmh.WmIcon
func FreePixmap(X *xgbutil.XUtil, pixid xproto.Pixmap)
func GetFormat(X *xgbutil.XUtil, depth byte) *xproto.Format
func MustFont(font *truetype.Font, err error) *truetype.Font
func ParseFont(fontReader io.Reader) (*truetype.Font, error)
func Scale(img image.Image, width, height int) draw.Image
func TextMaxExtents(font *truetype.Font, fontSize float64, text string) (width int, height int)
type BGRA
    func BlendBGRA(dest, src BGRA) BGRA
    func (c BGRA) RGBA() (r, g, b, a uint32)
type Image
    func FindIcon(X *xgbutil.XUtil, wid xproto.Window, width, height int) (*Image, error)
    func New(X *xgbutil.XUtil, r image.Rectangle) *Image
    func NewBytes(X *xgbutil.XUtil, bs []byte) (*Image, error)
    func NewConvert(X *xgbutil.XUtil, img image.Image) *Image
    func NewDrawable(X *xgbutil.XUtil, did xproto.Drawable) (*Image, error)
    func NewEwmhIcon(X *xgbutil.XUtil, icon *ewmh.WmIcon) *Image
    func NewFileName(X *xgbutil.XUtil, fileName string) (*Image, error)
    func NewIcccmIcon(X *xgbutil.XUtil, iconPixmap, iconMask xproto.Pixmap) (*Image, error)
    func (im *Image) At(x, y int) color.Color
    func (im *Image) Bounds() image.Rectangle
    func (im *Image) ColorModel() color.Model
    func (im *Image) CreatePixmap() error
    func (im *Image) Destroy()
    func (im *Image) For(each func(x, y int) BGRA)
    func (im *Image) ForExp(each func(x, y int) (r, g, b, a uint8))
    func (im *Image) PixOffset(x, y int) int
    func (im *Image) SavePng(name string) error
    func (im *Image) Scale(width, height int) *Image
    func (im *Image) Set(x, y int, c color.Color)
    func (im *Image) SetBGRA(x, y int, c BGRA)
    func (im *Image) SubImage(r image.Rectangle) *Image
    func (im *Image) Text(x, y int, clr color.Color, fontSize float64, font *truetype.Font, text string) (int, int, error)
    func (im *Image) Window(parent xproto.Window) *xwindow.Window
    func (im *Image) WritePng(w io.Writer) error
    func (im *Image) XDraw()
    func (im *Image) XDrawChecked() error
    func (im *Image) XExpPaint(wid xproto.Window, x, y int)
    func (im *Image) XPaint(wid xproto.Window)
    func (im *Image) XPaintRects(wid xproto.Window, rects ...image.Rectangle)
    func (im *Image) XShow() *xwindow.Window
    func (im *Image) XShowExtra(name string, quit bool) *xwindow.Window
    func (im *Image) XSurfaceSet(wid xproto.Window) error
Bugs

Package files

convert.go doc.go image.go new.go text.go util.go xsurface.go

Variables

var BGRAModel color.Model = color.ModelFunc(bgraModel)

Model for the BGRA color type.

func Alpha

func Alpha(dest *Image, alpha int)

Alpha will modify the alpha channel of the image such that: existingAlpha = existingAlpha * (givenAlpha / 100.0)

func Blend

func Blend(dest *Image, src image.Image, sp image.Point)

Blend alpha blends the src image (starting at the spt Point) into the dest image. If you're blending into a solid background color, use BlendBgColor instead. (It's more efficient.) Blend does not (currently) blend with the destination's alpha channel, only the source's alpha channel.

func BlendBgColor

func BlendBgColor(dest *Image, c color.Color)

BlendBgColor blends the Image (receiver) into the background color specified. This is more efficient than creating a background image and blending with Blend.

func Extents

func Extents(font *truetype.Font, fontSize float64, text string) (int, int)

Extents returns the *correct* max width and height extents of a string given a font. See freetype.MeasureString for the deets.

func FindBestEwmhIcon

func FindBestEwmhIcon(width, height int, icons []ewmh.WmIcon) *ewmh.WmIcon

FindBestEwmhIcon takes width/height dimensions and a slice of *ewmh.WmIcon and finds the best matching icon of the bunch. We always prefer bigger. If no icons are bigger than the preferred dimensions, use the biggest available. Otherwise, use the smallest icon that is greater than or equal to the preferred dimensions. The preferred dimensions is essentially what you'll likely scale the resulting icon to. If width and height are 0, then the largest icon found will be returned.

func FreePixmap

func FreePixmap(X *xgbutil.XUtil, pixid xproto.Pixmap)

FreePixmap is a convenience function for destroying a pixmap resource on the X server. If you're using an xgraphics.Image value, then its Destroy method will call this for you.

func GetFormat

func GetFormat(X *xgbutil.XUtil, depth byte) *xproto.Format

GetFormat searches SetupInfo for a Format matching the depth provided.

func MustFont

func MustFont(font *truetype.Font, err error) *truetype.Font

MustFont panics if err is not nil or if the font is nil.

func ParseFont

func ParseFont(fontReader io.Reader) (*truetype.Font, error)

ParseFont reads a font file and creates a freetype.Font type

func Scale

func Scale(img image.Image, width, height int) draw.Image

Scale is a simple wrapper around graphics.Scale.

func TextMaxExtents

func TextMaxExtents(font *truetype.Font, fontSize float64,
    text string) (width int, height int)

Returns the max width and height extents of a string given a font. This is calculated by determining the number of pixels in an "em" unit for the given font, and multiplying by the number of characters in 'text'. Since a particular character may be smaller than one "em" unit, this has a tendency to overestimate the extents. It is provided because I do not know how to calculate the precise extents using freetype-go. TODO: This does not currently account for multiple lines. It may never do so.

type BGRA

type BGRA struct {
    B, G, R, A uint8
}

BGRA is the representation of color for each pixel in an X pixmap. BUG(burntsushi): This is hard-coded when it shouldn't be.

func BlendBGRA

func BlendBGRA(dest, src BGRA) BGRA

Blend returns the blended alpha color for src and dest colors. This assumes that the destination has alpha = 1.

func (BGRA) RGBA

func (c BGRA) RGBA() (r, g, b, a uint32)

RGBA satisfies the color.Color interface.

type Image

type Image struct {
    // X images must be tied to an X connection.
    X *xgbutil.XUtil

    // X images must also be tied to a pixmap (its drawing surface).
    // Calls to 'XDraw' will draw data to this pixmap.
    // Calls to 'XPaint' will tell X to show the pixmap on some window.
    Pixmap xproto.Pixmap

    // Pix holds the image's pixels in BGRA order, so that they don't need
    // to be swapped for every PutImage request.
    Pix []uint8

    // Stride corresponds to the number of elements in Pix between two pixels
    // that are vertically adjacent.
    Stride int

    // The geometry of the image.
    Rect image.Rectangle

    // Whether this is a sub-image or not.
    // This is useful to know when sending data or setting surfaces.
    // Namely, sub-images cannot be set as surfaces and sub-images, when
    // being drawn, only have its pixels sent to X instead of the whole image.
    Subimg bool
}

func FindIcon

func FindIcon(X *xgbutil.XUtil, wid xproto.Window,
    width, height int) (*Image, error)

FindIcon takes a window id and attempts to return an xgraphics.Image of that window's icon. It will first try to look for an icon in _NET_WM_ICON that is closest to the size specified. If there are no icons in _NET_WM_ICON, then WM_HINTS will be checked for an icon. If an icon is found from either one and doesn't match the size specified, it will be scaled to that size. If the width and height are 0, then the largest icon will be returned with no scaling. If an icon is not found, an error is returned.

func New

func New(X *xgbutil.XUtil, r image.Rectangle) *Image

New returns a new instance of Image with colors initialized to black for the geometry given. New will also create an X pixmap. When you are no longer using this image, you should call Destroy so that the X pixmap can be freed on the X server. If 'X' is nil, then a new connection will be made. This is usually a bad idea, particularly if you're making a lot of small images, but can be used to achieve true parallelism. (Particularly useful when painting large images.)

func NewBytes

func NewBytes(X *xgbutil.XUtil, bs []byte) (*Image, error)

NewBytes uses the image package's decoder to convert the bytes given to an xgraphics.Imag value. Decoding an image can cause an error.

func NewConvert

func NewConvert(X *xgbutil.XUtil, img image.Image) *Image

NewConvert converts any image satisfying the image.Image interface to an xgraphics.Image type. If 'img' is an xgraphics.Image, it will be copied and a new image will be returned. Also, NewConvert attempts to optimize image conversion for some image formats. (i.e., *image.RGBA.)

func NewDrawable

func NewDrawable(X *xgbutil.XUtil, did xproto.Drawable) (*Image, error)

NewDrawable converts an X drawable into a xgraphics.Image. This is used in NewIcccmIcon.

func NewEwmhIcon

func NewEwmhIcon(X *xgbutil.XUtil, icon *ewmh.WmIcon) *Image

NewEwmhIcon converts EWMH icon data (ARGB) to an xgraphics.Image type. You should probably use xgraphics.FindIcon instead of this directly.

func NewFileName

func NewFileName(X *xgbutil.XUtil, fileName string) (*Image, error)

NewFileName uses the image package's decoder and converts a file specified by fileName to an xgraphics.Image value. Opening a file or decoding an image can cause an error.

func NewIcccmIcon

func NewIcccmIcon(X *xgbutil.XUtil, iconPixmap,
    iconMask xproto.Pixmap) (*Image, error)

NewIcccmIcon converts two pixmap ids (icon_pixmap and icon_mask in the WM_HINTS properts) to a single xgraphics.Image. It is okay for one of iconPixmap or iconMask to be 0, but not both. You should probably use xgraphics.FindIcon instead of this directly.

func (*Image) At

func (im *Image) At(x, y int) color.Color

At returns the color at the specified pixel.

func (*Image) Bounds

func (im *Image) Bounds() image.Rectangle

Bounds returns the rectangle representing the geometry of Image.

func (*Image) ColorModel

func (im *Image) ColorModel() color.Model

ColorModel returns the color.Model used by the Image struct.

func (*Image) CreatePixmap

func (im *Image) CreatePixmap() error

CreatePixmap allocates an X resource identifier for a pixmap. (It does not do any drawing.) You only need to call this if you're using XDraw/XExpPaint. If you're using XSurfaceSet/XDraw/XPaint, then CreatePixmap is called for you automatically.

func (*Image) Destroy

func (im *Image) Destroy()

Destroy frees the pixmap resource being used by this image. It should be called whenever the image will no longer be drawn or painted.

func (*Image) For

func (im *Image) For(each func(x, y int) BGRA)

For transforms every pixel color to the color returned by 'each' given an (x, y) position.

func (*Image) ForExp

func (im *Image) ForExp(each func(x, y int) (r, g, b, a uint8))

ForExp is like For, but bypasses image.Color types. (So it should be faster.)

func (*Image) PixOffset

func (im *Image) PixOffset(x, y int) int

PixOffset returns the index of the frst element of the Pix data that corresponds to the pixel at (x, y).

func (*Image) SavePng

func (im *Image) SavePng(name string) error

SavePng writes the Image to a file with name as a png.

func (*Image) Scale

func (im *Image) Scale(width, height int) *Image

Scale will scale the image to the size provided. Note that this will destroy the current pixmap associated with this image. After scaling, XSurfaceSet will need to be called for each window that this image is painted to. (And obviously, XDraw and XPaint will need to be called again.)

func (*Image) Set

func (im *Image) Set(x, y int, c color.Color)

Set satisfies the draw.Image interface by allowing the color of a pixel at (x, y) to be changed.

func (*Image) SetBGRA

func (im *Image) SetBGRA(x, y int, c BGRA)

SetBGRA is like set, but without the type assertion.

func (*Image) SubImage

func (im *Image) SubImage(r image.Rectangle) *Image

SubImage provides a sub image of Image without copying image data. N.B. The standard library defines a similar function, but returns an image.Image. Here, we return xgraphics.Image so that we can use the extra methods defined by xgraphics on it.

This method is cheap to call. It should be used to update only specific regions of an X pixmap to avoid sending an entire image to the X server when only a piece of it is updated.

Note that if the intersection of `r` and `im` is empty, `nil` is returned.

func (*Image) Text

func (im *Image) Text(x, y int, clr color.Color, fontSize float64,
    font *truetype.Font, text string) (int, int, error)

Text takes an image and, using the freetype package, writes text in the position specified on to the image. A color.Color, a font size and a font must also be specified. Finally, the (x, y) coordinate advanced by the text extents is returned.

Note that the ParseFont helper function can be used to get a *truetype.Font value without having to import freetype-go directly.

If you need more control over the 'context' used to draw text (like the DPI), then you'll need to ignore this convenience method and use your own.

func (*Image) Window

func (im *Image) Window(parent xproto.Window) *xwindow.Window

Window is a convenience function for painting the provided Image value to a new window, destroying the pixmap created by that image, and returning the window value. The window is sized to the dimensions of the image.

func (*Image) WritePng

func (im *Image) WritePng(w io.Writer) error

WritePng encodes the image to w as a png.

func (*Image) XDraw

func (im *Image) XDraw()

XDraw will write the contents of Image to a pixmap. Note that this is more like a buffer. Drawing does not put the contents on the screen. After drawing, it is necessary to call XPaint to put the contents somewhere. Draw may return an X error if something has gone horribly wrong.

XSurfaceSet should be called before XDraw. (If not, X will yell at you.) More specifically, CreatePixmap needs to be called before XDraw, but it is done automatically in XSurfaceSet.

If you're using sub-images to update a particular region of the image, XDraw is where you'll see the performance benefit (not XPaint).

func (*Image) XDrawChecked

func (im *Image) XDrawChecked() error

XDrawChecked is the same as XDraw, but issues PutImageChecked requests instead. This should *only* be used for debugging purposes, as each PutImageChecked request blocks for a round trip to the X server.

func (*Image) XExpPaint

func (im *Image) XExpPaint(wid xproto.Window, x, y int)

XExpPaint achieves a similar result as XPaint and XSurfaceSet, but uses CopyArea instead of setting a background pixmap and using ClearArea. CreatePixmap must be called before using XExpPaint. XExpPaint can be called on sub-images. x and y correspond to the destination x and y to copy the image to.

This should not be used on the same image with XSurfaceSet and XPaint.

func (*Image) XPaint

func (im *Image) XPaint(wid xproto.Window)

XPaint will write the contents of the pixmap to a window. Note that painting will do nothing if XDraw hasn't been called. XPaint is what switches the buffer (drawn to using XDraw) into the window to be visible. That is, multiple calls to XDraw can be made, and the screen will only be updated once with a call to XPaint.

func (*Image) XPaintRects

func (im *Image) XPaintRects(wid xproto.Window, rects ...image.Rectangle)

XPaintRects is a convenience function for issuing XDraw requests on each sub-image generated by the rects in the slice provided, and then painting the updated pixmap all at once to the window provided. This is efficient because no pixels are copied when taking a SubImage, and each XDraw call on a sub-image updates the pixels represented by that sub-image and only that sub-image.

func (*Image) XShow

func (im *Image) XShow() *xwindow.Window

XShow creates a new window and paints the image to the window. This is useful for debugging, or if you're creating an image viewer. XShow also returns the xwindow.Window value, in case you want to do further processing. (Like attach event handlers.)

func (*Image) XShowExtra

func (im *Image) XShowExtra(name string, quit bool) *xwindow.Window

XShowName is just like XShow, except it sets the name of the window to the name provided, and will quit the current event loop if 'quit' is true when the window is closed. If name is empty and quit is false, then the behavior is precisely the same as XShow.

func (*Image) XSurfaceSet

func (im *Image) XSurfaceSet(wid xproto.Window) error

XSurfaceSet will set the given window's background to this image's pixmap. Note that an image can have multiple surfaces, which is why the window id still needs to be passed to XPaint. A call to XSurfaceSet simply tells X that the window specified should use the pixmap in Image as its background image. Note that XSurfaceSet cannot be called on a sub-image. (An error will be returned if you do.) XSurfaceSet will also allocate an X pixmap if one hasn't been created for this image yet. (Generating a pixmap id can cause an error, so this call could return an error.)

Bugs