ToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Unrevised from 10.0 to 10.1.
10.0 version

Pixmaps in Common Graphics

This document contains the following sections:

1.0 Introduction to pixmaps
2.0 Using properties
3.0 Pixmap properties
4.0 Creating a pixmap programmatically
5.0 Textures
   5.1 Texture properties
   5.2 Texture-infos
6.0 Cached pixmaps
7.0 Pixmaps on picture controls
8.0 Outline controls and pixmaps
9.0 Pixmap handle functions
10.0 Converting from pixmap files to lisp code
11.0 Enhancing the IDE with pixmaps
12.0 Mouse cursors


1.0 Introduction to pixmaps

A pixmap stores and displays a graphical image as a rectangular array of pixel color values. (The term "pixmap" is short for "pixel map".) A pixmap that uses only a single bit to denote the color of each pixel (resulting in a monochrome image) is often referred to as a bitmap. Bitmap is also sometimes used to refer to any pixmap.

While the term "pixmap" is often used in a general way to describe the actual array of pixels or the image that they create, Common Graphics now uses the term to describe a class of objects that are used to manage pixmap images.

Applications may use Common Graphics pixmap objects (referred to as pixmaps) to manage their images. A pixmap can be created either by loading it from a pixmap file with load-pixmap, or by defining it programmatically with a make-instance form. Once defined, a pixmap can then be used either directly by copying it to a window or printer stream with copy-to-stream, or indirectly by associating it with a static-picture, picture-button, or multi-picture-button control. (Note that while a bitmap-pane is sometimes said to have a backing-store pixmap or bitmap, this is actually an internal object and not a pixmap.)



2.0 Using properties

This document describes much of the programmatic interface to pixmaps rather briefly in terms of properties. When we mention, for example, that a pixmap has a "name property", this generally implies that the name can be specified when creating a pixmap by passing a :name initarg to make-instance, that the name can be read later by calling name on the pixmap, and that the name can be modified later with setf and name.

The names of the initarg and accessor symbols will always be the same as the property name (rare exceptions occur and will be noted). Some properties are defined as read-only, and so the setf will not be defined. When editing properties in the inspector, the property definition also tells the inspector what sort of extended editing to use when the button on the right side of the property row is clicked, and what sort of replacement values will be allowed. Properties also know how to enact needed side effects when modified.



3.0 Pixmap properties

Pixmaps have the following properties. While pixmap properties can be modified at any time, they typically are specified at creation time since the various properties must be mutually compatible, and a pixmap is typically a static object. (To use a different image, it's best to create a separate pixmap.)

bits-per-pixel Specifies the number of bits that are used to denote each pixel value. Must be either 1, 4, 8, 16, or 24. Must be large enough for the largest pixel value used by the pixmap.
  • 1-bit-per-pixel values are 0 and 1
  • 4-bits-per-pixel values are 0 through 15
  • 8-bits-per-pixel values are 0 through 255
  • 16-bits-per-pixel values are 0 through 65535
  • 24-bits-per-pixel (true color) values are not a single index into a colormap, but instead uses one byte for each of the red, green, and blue components of the color.
colors A vector of RGB color structures that determines the color that will be used for each pixel value. Pixels with the value 0 will use the first color in the color vector, and in general pixels with the value N will use the (N+1)th member of the color vector. The colors vector should contain at least (one more than) as many members as the largest pixel value in the contents list.
contents A list of lists containing the individual pixel values of the pixmap. Each inner list contains the pixels for one row of the pixmap from left to right, and the outer list contains the rows from top to bottom. When pretty-printed, the contents list is oriented as the image will appear, giving a rough idea of what the real thing will look like.
mask-contents An optional property to define part of the pixmap as transparent. If supplied, it is a list of lists that corresponds to the contents list. Each member of the inner lists is either a 1 to indicate that the pixel at that location should be transparent, or a 0 to indicate that the pixel should be the color in the corresponding contents list (or in the texture, if a texture is supplied rather than a contents list).

When a mask is used, the main contents list (or texture) should have a zero for each transparent pixel. Therefore, the first entry in the colors vector of a pixmap that uses a mask will normally be a dummy color that never appears in the image, since index zero is reserved for transparent pixels.

Also note that if a pixmap uses a mask then it should not also use a pixmap-handle, because the "device-dependent bitmap" that the handle points to will not incorporate the mask. This restriction may be removed in the future.

An alternative to specifying the individual pixels for a mask is to call generate-mask, which will automatically create a mask that is transparent wherever a particular pixel value occurs.

height The height in pixels of the pixmap's texture. Normally this need not be specified, as it is computed automatically from the contents property.
invert-p A flag indicating whether the rows of the contents list are specified from top to bottom or from bottom to top.
  • If invert-p is nil, then the rows of the contents list will be pretty-printed so as to look right-side-up.
  • If invert-p is non-nil, then the pretty-printed contents will look upside down.

Note, though, that the operating system specifies pixmaps from bottom to top, so when invert-p is non-nil, this is actually the most natural orientation from the operating system's point of view, and an "inverted" pixmap will actually be somewhat faster. In addition, pixmaps created by calling load-pixmap will be "inverted" because that is the way that the data is read from the pixmap file most efficiently.

name An arbitrary symbol used for finding the pixmap programmatically. A pixmap may be associated with a control by specifying its name as the pixmap-name property of the control (assuming that the pixmap has been cached).

See Also Section 6.0 Cached pixmaps below.

palette-size This property is no longer supported. Please use the bits-per-pixel option instead (the first entry above).
pixmap-handle The handle to an optional more efficient version of the pixmap which is managed inside the operating system. This property should not be set directly by an application, but instead is set indirectly by calling open-pixmap-handle on the pixmap to create the handle. The handle can be destroyed later by calling close-pixmap-handle on the pixmap.

This property is optional because there is a trade-off to using a pixmap handle. While they draw faster, they essentially double the amount of memory required for the pixmap since the operating system is making its own copy of the pixels that are defined in Lisp for the pixmap. It is a good idea to close pixmap handles that are no longer being used by calling close-pixmap-handle on the pixmaps that had used them.

Sometimes Common Graphics automatically opens a pixmap handle for a pixmap, in particular if it is used by a control (since controls tend to be small enough that we can assume that the extra space is not very significant). A button-info (on a multi-picture-button) must use a pixmap handle, since its implementation currently depends on that, so the handle should not be removed from a pixmap that is used in a multi-picture-button.

source If this pixmap was created by calling load-pixmap on a pixmap file, then the path namestring will be preserved as the value of the source property. If the pixmap was defined programmatically instead, then the source property will be nil.
texture A texture object that describes each pixel in the pixmap. Typically the pixels are specified with the contents property, but alternatively a texture object may first be created and then specified here. This is most useful for backward compatibility with the older texture interface, where textures and texture-infos are used individually.
mask An optional second texture object that describes whether or not the pixmap is transparent at each pixel. Typically the mask pixels are specified with the mask-contents property, but alternately a texture object may first be created and then specified here. If supplied, the mask texture's bits-per-pixel must be 1, and the texture should have a 1 for each transparent pixel and a 0 for each non-transparent pixel.
texture-info A structure that internally contains the colors, bits-per-pixel, width, and height properties of the pixmap.

Provided as a property that is redundant with these other properties, primarily for backward compatibility with the older texture interface in applications that have created texture-info structures, so that these structures can be specified directly as the texture-info property of a pixmap as an alternative to specifying the other properties separately.

width The width in pixels of the texture-info's texture. Normally this need not be specified, as it is computed automatically from the contents property.


4.0 Creating a pixmap programmatically

An example of a programmatically created pixmap:

(make-instance 'pixmap
  :name :find-debug-prompt
  :bits-per-pixel 4
  :colors (vector black dark-red dark-green
             dark-yellow dark-blue dark-magenta
             dark-cyan light-gray gray red green
             yellow blue magenta cyan white)
  :contents
  '((7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7)
    (7 0 0 7 7 7 7 7 7 7 7 7 7 7 7 7)
    (7 0 0 0 7 7 7 7 7 7 7 7 7 7 7 7)
    (7 7 0 0 0 7 7 7 7 7 7 7 7 7 7 7)
    (7 7 7 0 0 0 7 7 7 7 7 7 7 7 7 7)
    (7 7 7 7 0 0 0 7 7 7 7 7 7 7 7 7)
    (7 7 7 7 7 0 0 0 7 7 7 7 7 7 7 7)
    (7 7 7 7 7 7 0 0 0 7 7 7 7 7 7 7)
    (7 7 7 7 7 0 0 0 7 7 7 7 7 7 7 7)
    (7 7 7 7 0 0 0 7 7 7 7 7 7 7 7 7)
    (7 7 7 0 0 0 7 7 7 7 7 7 7 7 7 7)
    (7 7 0 0 0 7 7 7 7 7 7 7 7 7 7 7)
    (7 0 0 0 7 7 7 7 7 0 0 0 0 0 7 7)
    (7 0 0 7 7 7 7 7 7 0 0 0 0 0 7 7)
    (7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7)
    (7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7)))

In the above example, each "7" pixel will be drawn in light-gray because light-gray is at position 7 in the color vector of the pixmap.

While the example color vector above uses the built-in constants for the system colors, the general format using arbitrary colors is:

:colors (vector (make-rgb :red 200 :green 200)
                (make-rbg :red 200 :green 100 :blue 200)
                ...)

As a side note, if a pixmap uses the exact color vector shown in this example, then it could instead specify (default-pixmap-color-vector) (see default-pixmap-color-vector) as its colors property, since it returns a vector with these particular colors. This saves space by reusing the same vector. The IDE uses the default-pixmap-color-vector for many of its picture buttons.

There is also (default-gray-pixmap-color-vector) (see default-gray-pixmap-color-vector) which is just like the default-pixmap-color-vector except that black (at position 0) has been replaced by the value of system-edge-shadow-color. Replacing a pixmap's color with this color vector can make it appear to be dimmed. If your application uses multiple pixmaps that can share the same color vector, then consider storing such color vectors globally and reusing them to save space.

You may also want to use the default-pixmap-color-vector for the following reason. When the IDE or a standalone application starts up, a few of the colors in the default-pixmap-color-vector and the default-gray-pixmap-color-vector are modified.

These special functions return the colors that the end user has set up in the Windows Control Panel. If you use the default-pixmap-color-vector for your application's buttons, then the buttons will adapt at runtime to match the end user's color preferences. You can demonstrate this by choosing a new color scheme in Control Panel and then switching back to the Allegro IDE. Using the default-pixmap-color-vector will cause your own application to adapt in the same way.



5.0 Textures

A texture specifies the rectangular array of pixel values that are used by a pixmap, without the additional information such as colors that are included in a pixmap (or texture-info) object. While a pixmap internally includes a texture, using a pixmap does not require dealing with the texture object directly at all, since the information contained in the texture is available directly from the pixmap by using its contents, bits-per-pixel, width, and height properties.


5.1 Texture properties

A texture shares some of the properties of a pixmap, and these properties are accessed in the same way. The shared properties are contents, bits-per-pixel, palette-size, width, and height. In addition, a texture has the following unique properties:

texture-array An internal two-dimensional array that implements the texture. The texture-array can be passed to Windows functions, like StretchDIBits, if direct WinAPI calls are made. Usually, the texture-array is not specified when making an instance; instead, the contents property is specified and the texture-array is created from the contents.
texture-array-height The height of the internal texture array.
texture-array-width The width of the internal texture array. This may be larger than the logical width of the texture (accessed with the width property) due to an internal need to pad the array rows to 32-bit boundaries (which is done automatically).

Here is how to create the texture used by the earlier pixmap example. This texture would be created automatically by that example, but an application can alternately create a texture in this way and then pass it to a pixmap.

(make-instance 'texture
   :bits-per-pixel 4
   :contents
   '((7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7)
    (7 0 0 7 7 7 7 7 7 7 7 7 7 7 7 7)
    (7 0 0 0 7 7 7 7 7 7 7 7 7 7 7 7)
    (7 7 0 0 0 7 7 7 7 7 7 7 7 7 7 7)
    (7 7 7 0 0 0 7 7 7 7 7 7 7 7 7 7)
    (7 7 7 7 0 0 0 7 7 7 7 7 7 7 7 7)
    (7 7 7 7 7 0 0 0 7 7 7 7 7 7 7 7)
    (7 7 7 7 7 7 0 0 0 7 7 7 7 7 7 7)
    (7 7 7 7 7 0 0 0 7 7 7 7 7 7 7 7)
    (7 7 7 7 0 0 0 7 7 7 7 7 7 7 7 7)
    (7 7 7 0 0 0 7 7 7 7 7 7 7 7 7 7)
    (7 7 0 0 0 7 7 7 7 7 7 7 7 7 7 7)
    (7 0 0 0 7 7 7 7 7 0 0 0 0 0 7 7)
    (7 0 0 7 7 7 7 7 7 0 0 0 0 0 7 7)
    (7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7)
    (7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7)))

5.2 Texture-infos

A texture-info is a structure that is retained mostly for backward compatibility with the older but still supported texture interface. It internally holds the colors, width, height, and bits-per-pixel property values of a pixmap. When using a pixmap object, you do not need to know about the texture-info used by the pixmap because each of the values held in the texture-info are immediately accessible from the pixmap by using the accessor functions.

Note: In order to access these values from a texture-info itself, the older accessor functions texture-info-colors, texture-info-width, texture-info-height, and texture-info-bits-per-pixel must be used instead.

A texture-info may be created by calling make-texture-info and then passed as the :texture-info initarg when creating a pixmap, as an alternative to passing separate initargs for the four corresponding properties when creating the pixmap.



6.0 Cached pixmaps

A cached pixmap is simply a pixmap on which the function cache-pixmap has been called, and which therefore is a member of an internal global list of pixmaps.

An application may choose to cache a pixmap so that it can later look up the pixmap from its name, rather than keeping the pixmap itself around in a variable somewhere.

An IDE user may want to cache a pixmap so that it will appear in the list of available pixmaps that drops down when using the inspector to modify the pixmap-name property of a static-picture, picture-button, or multi-picture-button control.

A list of all cached pixmaps (with tiny representations of their images) can be viewed by evaluating the following form (in an editor or in the Debug window):

(inspect cg.pixmap:*cached-pixmaps*)

The following functions are useful when using cached pixmaps:



7.0 Pixmaps on picture controls

A control that can display a pixmap has several properties that provide alternate ways of specifying the pixmap that it will display. These properties exist on a static-picture, or picture-button control, or on a button-info object (which specifies a single button of a multi-picture-button control). If you are editing one of these objects in the inspector and change one of these properties, the other properties in this set will update themselves to match.

The properties are:



8.0 Outline controls and pixmaps

The outline and outline-item controls also use pixmaps as the other controls mentioned above do, but instead of having a single pixmap property, there are separate properties for the leaf, opened, and closed pixmaps. An icon cannot be used instead of a pixmap, and there is no pixmap-use-handle property since pixmap handles are always used for outlines. Typically these properties are specified for the outline as a whole rather than for each outline-item, but the latter is provided if needed. The pixmap properties of an outline or outline-item are:

These behave similarly to the corresponding control properties documented in Section 6.0 Cached pixmaps.



9.0 Pixmap handle functions

There are serveral functions for creating and deleting pixmap handles. ("Creating a pixmap handle" is a lazy way of saying "telling the operating system to create an optimized internal version of the pixmap and returning a handle to this internal pixmap to Common Graphics".) This section describes the differences between them.

When working with textures and texture-infos, the pixmap handle must be created and managed as a separate object. create-pixmap-handle creates and returns a pixmap handle, and destroy-pixmap-handle will destroy it later to free up operating system resources. (If destroy-pixmap-handle is not called explicitly, the handle will be destroyed automatically when Lisp or a standalone application exits.)

When working with pixmaps, open-pixmap-handle will create a handle and store it on the pixmap, while close-pixmap-handle will remove the handle from the pixmap and destroy it. These functions may also be called on a control that uses a pixmap in order to address its pixmap indirectly.

When working with controls that use pixmaps (such as static-pictures, or picture-buttons control, or button-infos, which specifies a single button of a multi-picture-button control), calling (setf pixmap-use-handle) on the control will establish whether its pixmap uses a handle.



10.0 Converting from pixmap files to lisp code

Since loading pixmaps from pixmap files is pretty fast, an application can simply include any needed pixmap files with it and load them at runtime. But if you would rather embed the pixmap data in your Lisp code to avoid distributing the pixmap files, then you can convert the pixmap data into Lisp source code.

The simplest way is to call the function import-pixmaps. It loads an entire set of pixmap files and creates a new source code file that you can incorporate directly into your application code to create the pixmaps.

An older alternative is to call save-lisp-pixmap on a pixmap that has been loaded from a pixmap file with load-pixmap, which will create a new Lisp source code file with the programmatic definition of the pixmap in it. load-lisp-pixmap will then load this file and return the saved pixmap. The saved file may optionally be compiled in the meantime, and then load-lisp-pixmap called on the compiled file.

load-lisp-pixmap cannot be used at runtime, though, if the individual source code or fasl files have been replaced by an image file for distribution. In order to use save-lisp-pixmap for a generated application without distributing the pixmap source or fasl files, you would need to either edit the source code file that save-lisp-pixmap creates to force it to store the returned pixmap in some accessible location, or else make your application code call find-pixmap on the name of the pixmap that was saved. (The generated code does call cache-pixmap on the created pixmap.)

An alternative to using save-lisp-pixmap at all is to evaluate a form like (pprint (recreation-code my-pixmap)) in the debug window, and then to simply cut and paste the printed source code into your application. (See recreation-code.)

This allows placing multiple pixmaps into a single Lisp source file. Or inspect a pixmap object, go to the internals tab, and click on the button at the right side of the top line to invoke the "extended editor" for the pixmap. The modal editor window that pops up will contain the recreation code for the pixmap, which you could copy and paste into your application. (Though the editor is read-only, you can still copy the text from it.)



11.0 Enhancing the IDE with pixmaps

The generic function pixmap returns a pixmap to be associated with certain classes of objects. Normally this is a 4-bits-per-pixel 16 by 16 pixmap. The inspector will display a pixmap in its history list for any object for which a pixmap method returns a pixmap. For example, adding the following method would cause all conses that appear in the inspector's history list to be represented by the :macroexpand pixmap (which looks like (<->)).

(defmethod pixmap ((object cons))
  (find-pixmap :macroexpand))

Evaluate that form in the Debug window and then evaluate (inspect (cons 1 2)) and you will see the pixmap in the drop-down list in the Inspect dialog.

Evaluating

(defmethod pixmap ((object cons)) nil)

will undo the associated between conses and the icon.



12.0 Mouse cursors

Mouse cursors are now standard-objects with some of the same properties as pixmaps. The object is called a cursor and has the properties name, texture, mask, cursor-handle, and click-position (which is a position indicating the point within the cursor's image, relative to its upper left corner, that is considered to be the exact location pointed to by the cursor).

The properties texture-info, width, and height from pixmaps are not needed for mouse cursors since all cursors are the same size and depth. (The depth is always 1-bit-per-pixel, and the standard mouse cursor size is returned by the functions cg::mouse-cursor-width and cg::mouse-cursor-height.)

The function cursor and its setf get and set the mouse cursor of a window; the new cursor can be either a cursor object or a cursor handle. There is a cursor property on every window for its current cursor. Like pixmaps, cursors can be cached, using the function cache-cursor, retrieved with find-cursor, and uncached with uncache-cursor.

A cursor object will create its handle as needed when the cursor is assigned to a window.

Below is the IDE's definition of the "hand" mouse cursor. After defining a cursor in this way, you can immediately tell a window to use it with a form like

(setf (cursor my-window) (find-cursor :hand-cursor))

Note that black pixels require a "zero" in both the main texture and the mask texture, white pixels require a "one" in the main texture and a "zero" in the mask texture, and transparent pixels require a "zero" in the main texture and a "one" in the mask texture.

(cache-cursor
    (make-instance 'cursor
      :name :hand-cursor
      :click-position (make-position 13 4)
      :texture (make-instance 'texture
                 :bits-per-pixel 1
                 :contents
                 '(
                   #*00000000000000000000000000000000
                   #*00000000000000000000000000000000
                   #*00000000000000000000000000000000
                   #*00000000000000000000000000000000
                   #*00000000000000000000000000000000
                   #*00000000000011000000000000000000
                   #*00000000000011000000000000000000
                   #*00000000000011000000000000000000
                   #*00000000000011000000000000000000
                   #*00000000000011000000000000000000
                   #*00000000000011011000000000000000
                   #*00000000000011011011000000000000
                   #*00000000000011011011010000000000
                   #*00000000000011011011011000000000
                   #*00000000110011111111011000000000
                   #*00000000111011111111111000000000
                   #*00000000011011111111111000000000
                   #*00000000001011111111111000000000
                   #*00000000001111111111111000000000
                   #*00000000000111111111111000000000
                   #*00000000000111111111110000000000
                   #*00000000000011111111110000000000
                   #*00000000000011111111110000000000
                   #*00000000000001111111100000000000
                   #*00000000000001111111100000000000
                   #*00000000000000000000000000000000
                   #*00000000000000000000000000000000
                   #*00000000000000000000000000000000
                   #*00000000000000000000000000000000
                   #*00000000000000000000000000000000
                   #*00000000000000000000000000000000 
                   #*00000000000000000000000000000000
                   ))

      :mask (make-instance 'texture
              :bits-per-pixel 1
              :contents
              '(
                #*11111111111111111111111111111111
                #*11111111111111111111111111111111
                #*11111111111111111111111111111111
                #*11111111111111111111111111111111
                #*11111111111100111111111111111111
                #*11111111111000011111111111111111
                #*11111111111000011111111111111111
                #*11111111111000011111111111111111
                #*11111111111000011111111111111111
                #*11111111111000000011111111111111
                #*11111111111000000000111111111111
                #*11111111111000000000001111111111
                #*11111111111000000000000111111111
                #*11111110001000000000000011111111
                #*11111110000000000000000011111111
                #*11111110000000000000000011111111
                #*11111111000000000000000011111111
                #*11111111100000000000000011111111
                #*11111111100000000000000011111111
                #*11111111110000000000000011111111
                #*11111111110000000000000111111111
                #*11111111111000000000000111111111
                #*11111111111000000000000111111111
                #*11111111111100000000001111111111
                #*11111111111100000000001111111111
                #*11111111111100000000001111111111
                #*11111111111111111111111111111111
                #*11111111111111111111111111111111
                #*11111111111111111111111111111111
                #*11111111111111111111111111111111
                #*11111111111111111111111111111111 
                #*11111111111111111111111111111111
                ))))

In addition to the ability to define custom mouse cursors as above, a number of built-in mouse cursors are supplied. There are two groups of these.

The first is a set supplied by the operating system, and are the values of the following variables:

The value of each of these variables is actually a cursor-handle integer rather than a cursor object, but it may be used in the same way that a cursor object is used. These cursors are standard in the Windows operating system, and if the user is using a custom Windows desktop scheme, then these built-in cursor handles will invoke the corresponding mouse cursors of that scheme.

The second set of built-in cursors is supplied by Common Graphics. These cursor objects must be retrieved by calling find-cursor on one of the following keyword symbols, which are the names of the cursors:


Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
This page was not revised from the 10.0 page.
Created 2019.8.20.

ToCDocOverviewCGDocRelNotesFAQIndexPermutedIndex
Allegro CL version 10.1
Unrevised from 10.0 to 10.1.
10.0 version