Raw Drawing and Animation
![]() |
![]() |
![]() |
![]() |
Raw Drawing and Animation
This chapter describes:
- PtRaw widget
- Color
- Drawing attributes
- Arcs, ellipses, polygons, and rectangles
- Lines, pixels, and pixel arrays
- Text
- Bitmaps
- Images
- Animation
- Direct mode
- Video memory offscreen
- Alpha blending support
- Chroma key support
- Extended raster operations
- Video modes
- Gradients
- Video overlay
- Layers
PtRaw widget
The Pg routines in the Photon library are the lowest-level drawing functions. They're used by the widget library to draw the widgets. You can use the Pg functions in a Photon application, but your application has to:
- handle any interaction with the user
- determine when the drawing is damaged (for example, when it's uncovered, or when the user moves the window)
- repair the drawing whenever it's damaged.
![]() |
You should use widgets whenever possible because they do all of the above themselves. |
If your application must do its own drawing, you should use the PtRaw widget. It does the following:
- Tells the application what has been damaged.
- Flushes the draw buffer almost whenever necessary. (You should flush the buffer explicitly; for example, before a blitting operation. Blitting shifts a rectangular area of your drawing by some distance; you want your drawing to be up-to-date before this happens.)
To create a PtRaw widget in PhAB, click on its icon in the widget palette:
Position it where you want your drawing to appear.
You can provide various functions for the PtRaw widget; they're called in the order given below when the widget is realized, and are then called as necessary:
- Pt_ARG_RAW_INIT_F
- An initialization function that's called before the widget's extent is calculated.
- Pt_ARG_RAW_EXTENT_F
- If provided, calculates the widget's extent when the widget is moved or resized.
- Pt_ARG_RAW_CALC_OPAQUE_F
- Calculates the raw widget's opacity tile list.
- Pt_ARG_RAW_CONNECT_F
- Called as the last stage in realizing the widget, just before any required regions are created.
- Pt_ARG_RAW_DRAW_F
- Does the drawing.
Most of the time you'll need to specify only the drawing function (see below). You can use PhAB's function editor (described in the Editing Resources and Callbacks in PhAB chapter) to edit these resources — but you must give the raw widget a unique instance name first. You can also set these resources in your application's code; for more information, see “Function resources” in the Manipulating Resources in Application Code chapter.
For information on PtRaw's resources, see the Photon Widget Reference.
Raw drawing function
When you create a PtRaw widget in PhAB and edit its Pt_ARG_RAW_DRAW_F function, you'll see default code like this:
void my_raw_draw_fn( PtWidget_t *widget,
PhTile_t *damage )
{
PtSuperClassDraw( PtBasic, widget, damage );
}
The call to PtSuperClassDraw() (described in the Building Custom Widgets guide) invokes PtBasic's draw function, which draws the raw widget's borders, fills the widget, and so on, as specified by its resources. The raw widget can do all this by itself, but using PtSuperClassDraw() reduces the complexity of the raw drawing function.
There are several things to consider in the raw drawing function:
- You'll need to know the raw widget's canvas.
- The origin for the drawing primitives is the top left corner of the canvas of the raw widget's parent, not the raw widget itself. You'll need to translate the coordinates.
- The raw widget can draw beyond its canvas, but it's not a good idea. You should set up clipping in the drawing function.
- The drawing function is passed a list of damaged areas that can be used to speed up repairs.
- For raw widgets whose contents change dynamically, you can define a model that describes what to draw.
These are described below, followed by some examples of simple drawing functions.
![]() |
Don't call
PtBkgdHandlerProcess()
in a
PtRaw
widget's drawing function.
Don't change any other widget in any way (creating, destroying, setting resources, and so on) in a raw widget's drawing function. It's safe to get resources from other widgets. Don't call the drawing function directly from your program. Instead, damage the widget by calling PtDamageWidget(), and let the library call the drawing function. |
Determining the raw widget canvas
You can determine the raw widget's canvas by calling PtCalcCanvas() as follows:
PhRect_t raw_canvas; PtCalcCanvas (widget, &raw_canvas);
You'll need this canvas to perform any required translations and clipping.
Translating coordinates
The origin for the drawing primitives is the upper left corner of the raw widget's parent's canvas. You'll probably find it easier to use the upper left corner of the raw widget's canvas as the origin.
Once you've determined the raw widget's canvas, you can do one of the following:
- Add the coordinates of the upper left corner of the raw widget's canvas
to any coordinates passed to the drawing primitives. For example, to
draw an ellipse centered at (80, 60) relative to the raw widget's canvas:
PhPoint_t c1 = { 80, 60 }; PhPoint_t r = { 72, 52 }; c1.x += raw_canvas.ul.x; c1.y += raw_canvas.ul.y; PgSetFillColor(Pg_YELLOW); PgDrawEllipse ( &c1, &r, Pg_DRAW_FILL );This is the preferred method.
- You can set the translation by calling
PgSetTranslation(),
passing to it the upper left corner of the raw widget's canvas:
PhPoint_t c1 = { 80, 60 }; PhPoint_t r = { 72, 52 }; PgSetTranslation (&raw_canvas.ul, Pg_RELATIVE); PgSetFillColor(Pg_YELLOW); PgDrawEllipse ( &c1, &r, Pg_DRAW_FILL );

Be sure to restore the old translation before leaving the raw widget's drawing function. Here's one way to do it: /* Restore the translation by subtracting the coordinates of the raw widget's canvas. */ raw_canvas.ul.x *= -1; raw_canvas.ul.y *= -1; PgSetTranslation (&raw_canvas.ul, Pg_RELATIVE);
Clipping
As mentioned above, it's possible to draw beyond the raw widget's extent in its drawing function, but it's not a good thing to do:
- It can mess up the rest of your application's interface.
- If the raw drawing beyond the raw widget's extent is damaged but the raw widget itself isn't, the raw widget's drawing function isn't called, so the damage won't be repaired.
It's possible to write the drawing function so that clipping isn't needed, but it can make your code more complicated. For example, if you try to write text that extends beyond the raw widget's canvas, you might need to draw partial letters. You'll also have to consider what happens if the user changes the size of the raw widget.
It's much easier to use PtClipAdd() to set the clipping area to be the raw widget's canvas and let the graphics driver restrict the drawing:
PtClipAdd ( widget, &raw_canvas);
Before leaving the drawing function, call PtClipRemove() to reset the clipping area:
PtClipRemove ();
Using damage tiles
If your raw widget's drawing function takes a lot of time, you might not want to redraw the entire canvas when a small portion of it has been damaged. You can speed up the repairs by using the drawing function's damage argument.
The damage argument is a pointer to a linked list of PhTile_t structures (see the Photon Library Reference), each of which includes these members:
- rect
- A PhRect_t structure that defines the damaged area.
- next
- A pointer to the next tile in the list.
The damaged areas are relative to the raw widget's disjoint parent (usually a PtWindow widget). Use PtWidgetOffset() to obtain the offset.
If there's more than one tile in the linked list, the first one covers the entire area covered by the rest. Either use the first tile and ignore the rest, or ignore the first and use the rest:
void rawDrawFunction (PtWidget_t *widget,
PhTile_t *damage)
{
if (damage->next != NULL) {
/* If there's more than one tile, skip the first. */
damage = damage->next;
}
while (damage != NULL) {
/* Examine 'damage' to see if any drawing
needs doing:
damage->rect.ul.x, damage->rect.ul.y,
damage->rect.lr.x, damage->rect.lr.y */
…
damage = damage->next; /* Go on to the next tile. */
}
}
The following functions (described in the Photon Library Reference) work with tiles:
- PhAddMergeTiles()
- Merge two list tiles, eliminating overlap
- PhClipTilings()
- Clip one list of tiles from another
- PhCoalesceTiles()
- Combine a list of tiles
- PhCopyTiles()
- Copy a list of tiles
- PhDeTranslateTiles()
- Subtract x and y offsets from the vertices of a list of tiles
- PhFreeTiles()
- Return a list of tiles to the internal tile pool
- PhGetTile()
- Retrieve a tile from the internal tile pool
- PhIntersectTilings()
- Determine the intersection of two lists of tiles
- PhMergeTiles()
- Remove all overlap from a list of tiles
- PhRectsToTiles()
- Create a list of tiles from an array of rectangles
- PhSortTiles()
- Sort a list of tiles
- PhTilesToRects()
- Create an array of rectangles from a list of tiles
- PhTranslateTiles()
- Add x and y offsets to the vertices of a list of tiles
Using a model for more complex drawing
If the contents of the raw widget are static, you can call the Pg drawing primitives directly from the raw drawing function. If the contents are dynamic, you'll need to define a data structure or model that describes them.
The structure of the model depends on your application; the raw drawing function must be able to traverse the model and draw the required graphics. Use the raw widget's Pt_ARG_USER_DATA or Pt_ARG_POINTER resource to save a pointer to the model.
Examples of simple PtRaw drawing functions
This drawing function draws a couple of ellipses, one of which is clipped:
void my_raw_draw_fn( PtWidget_t *widget,
PhTile_t *damage )
{
PhRect_t raw_canvas;
PhPoint_t c1 = { 80, 60 };
PhPoint_t c2 = { 30, 210 };
PhPoint_t r = { 72, 52 };
PtSuperClassDraw( PtBasic, widget, damage );
PtCalcCanvas(widget, &raw_canvas);
/* Set the clipping area to be the raw widget's
canvas. */
PtClipAdd ( widget, &raw_canvas);
/* Draw the ellipses. */
c1.x += raw_canvas.ul.x;
c1.y += raw_canvas.ul.y;
PgSetFillColor(Pg_YELLOW);
PgDrawEllipse ( &c1, &r, Pg_DRAW_FILL );
c2.x += raw_canvas.ul.x;
c2.y += raw_canvas.ul.y;
PgSetFillColor(Pg_RED);
PgDrawEllipse ( &c2, &r, Pg_DRAW_FILL );
/* Reset the clipping area. */
PtClipRemove ();
}
This function is the same, but it sets the translation:
void my_raw_draw_fn( PtWidget_t *widget,
PhTile_t *damage )
{
PhRect_t raw_canvas;
PhPoint_t c1 = { 80, 60 };
PhPoint_t c2 = { 30, 210 };
PhPoint_t r = { 72, 52 };
PtSuperClassDraw( PtBasic, widget, damage );
PtCalcCanvas(widget, &raw_canvas);
/* Set the clipping area to be the raw widget's
canvas. */
PtClipAdd ( widget, &raw_canvas);
/* Set the translation so that drawing operations
are relative to the raw widget's canvas. */
PgSetTranslation (&raw_canvas.ul, Pg_RELATIVE);
/* Draw the ellipses. */
PgSetFillColor(Pg_YELLOW);
PgDrawEllipse ( &c1, &r, Pg_DRAW_FILL );
PgSetFillColor(Pg_RED);
PgDrawEllipse ( &c2, &r, Pg_DRAW_FILL );
/* Restore the translation by subtracting the
coordinates of the raw widget's canvas. */
raw_canvas.ul.x *= -1;
raw_canvas.ul.y *= -1;
PgSetTranslation (&raw_canvas.ul, Pg_RELATIVE);
/* Reset the clipping area. */
PtClipRemove ();
}
Color
Colors are specified in the Photon microGUI with the PgColor_t type. The library and graphics drivers interpret this data type according to the current color model (described in the documentation for PgColor_t).
The default color model, Pg_CM_PRGB, uses a 32-bit Red-Green-Blue (RGB) representation:
| Reserved | Red | Green | Blue |
|---|---|---|---|
| 0000 0000 | rrrr rrrr | gggg gggg | bbbb bbbb |
Macros for the most commonly used colors are defined in <photon/Pg.h>.
Although PgColor_t uses 32 bits, only 24 bits are used per color. This representation is called true color. The Photon microGUI is a true-color windowing system; it uses this 24-bit RGB representation internally.
Most graphics cards currently use true color (24 bits) or high color (16 bits). However, some graphics drivers take advantage of the palette on older palette-based cards.
The following datatypes and functions that deal with color are described in the Photon Library Reference:
- PgAlphaValue()
- Extract the alpha component from a color value
- PgARGB()
- Convert alpha, red, green, and blue values to composite color format
- PgBackgroundShadings()
- Calculate top and bottom shading colors
- PgBlueValue()
- Extract the blue component from a color value
- PgCMY()
- Convert cyan, magenta, and yellow values to composite color format
- PgColorHSV_t
- Hue-Saturation-Value color value
- PgColorMatch()
- Query for best color matches
- PgGetColorModel()
- Get the current color model
- PgGetPalette()
- Query for current color palette
- PgGray()
- Generate a shade of gray
- PgGrayValue()
- Extract color brightness
- PgGreenValue()
- Extract the green component from a color value
- PgHSV()
- Convert hue, saturation, and value to composite color format
- PgHSV2RGB()
- Convert HSV colors to RGB
- PgRedValue()
- Extract the red component from a color
- PgRGB()
- Convert red, green, and blue values to composite color format
- PgRGB2HSV()
- Convert RGB colors to HSV
- PgSetColorModel()
- Set the current color model
- PgSetPalette()
- Set the color palette
Drawing attributes
When doing raw drawing, you can set various attributes, including fonts, palettes, fill colors, line colors and styles, and text colors. The attributes that you set affect all raw drawing operations until you set them again. For example, the line color affects all lines, pixels, and bitmaps that you draw using the drawing primitives.
![]() |
You don't need to set these attributes if you're using widgets; the
drawing attributes are set based on the widgets' definitions and resources.
However, in all other cases you should set these attributes before you begin drawing. The defaults are undefined and drawing before setting the relevant attributes may produce unexpected results. |
General attributes
The functions that set general drawing attributes are:
- PgDefaultMode()
- Reset draw mode and plane mask to their default values
- PgSetDrawMode()
- Set draw mode
- PgSetPlaneMask()
- Protect video memory from being modified
Text attributes
The text attributes affect all the text that you draw by calling the drawing primitives described in “Text,” below. The functions that set text attributes are:
- PgDefaultText()
- Reset the text attribute to its system default
- PgSetFont()
- Set text font
- PgSetTextColor()
- Set text color
- PgSetTextDither()
- Set text dither pattern
- PgSetTextTransPat()
- Set draw transparency
- PgSetTextXORColor()
- Set a color for XOR drawing
- PgSetUnderline()
- Set colors for underlining text
Fill attributes
The fill attributes affect all the drawing that you do by calling the primitive functions described in
The functions that set fill attributes are:
- PgDefaultFill()
- Reset the fill attribute to its default value
- PgSetFillColor()
- Set exact fill color
- PgSetFillDither()
- Set specific dither pattern and colors
- PgSetFillTransPat()
- Set draw transparency
- PgSetFillXORColor()
- Set a color for XOR drawing
Stroke (line) attributes
The stroke attributes affect all the drawing that you do by calling the primitive functions described in
The functions that set stroke attributes are:
- PgDefaultStroke()
- Reset the stroke attribute to its system default
- PgSetStrokeCap()
- Set what the ends of lines look like
- PgSetStrokeColor()
- Set the color of subsequent outlines
- PgSetStrokeDither()
- Apply a color pattern to outlines
- PgSetStrokeTransPat()
- Use a masking pattern to set draw transparency on outlines
- PgSetStrokeXORColor()
- Use the XOR of a color to draw outlines
- PgSetStrokeDash()
- Set dashed lines
- PgSetStrokeWidth()
- Set line thickness
- PgSetStrokeFWidth()
- Set line thickness
Arcs, ellipses, polygons, and rectangles
The Photon libraries include a number of primitive functions that you can use to draw shapes, including:
- rectangles
- rounded rectangles
- beveled boxes, rectangles, and arrows
- polygons
- arcs, circles, chords, and pies
- spans — complex shapes
![]() |
Don't use these drawing primitives in an interface that uses widgets;
widgets redisplay themselves when damaged, so anything drawn on top
of them disappears.
To display arcs, lines, etc. in an interface:
|
By using each primitive's flags, you can easily draw an outline (stroke), draw the filled “inside” (fill), or draw both as a filled outline.
The current fill and stroke attributes are used. For more information, see “Drawing attributes,” earlier in this chapter.
| To: | Set flags to: |
|---|---|
| Fill the primitive, using the current fill attributes | Pg_DRAW_FILL |
| Outline the primitive, using the current stroke attributes | Pg_DRAW_STROKE |
| Fill the primitive and outline it, using the current fill and stroke attributes | Pg_DRAW_FILL_STROKE |
The mx versions of these functions place the address of the primitive into the draw buffer in your application's data space. When the draw buffer is flushed, the primitive is copied to the graphics driver. The non-mx versions copy the primitive itself into the draw buffer.
Rectangles
You can draw rectangles, using the current drawing attributes, by calling PgDrawIRect() or PgDrawRect().
PgDrawRect() uses a PhRect_t structure (see the Photon Library Reference) for the rectangle coordinates, while PgDrawIRect() lets you specify the coordinates individually. Use whichever method you want.
The following example draws a rectangle that's filled, but not stroked (i.e. it has no border):
void DrawFillRect( void )
{
PgSetFillColor( Pg_CYAN );
PgDrawIRect( 8, 8, 152, 112, Pg_DRAW_FILL );
}
If you wish, you can call PgDrawRect() instead:
void DrawFillRect( void )
{
PhRect_t rect = { {8, 8}, {152, 112} };
PgSetFillColor( Pg_CYAN );
PgDrawRect( &rect, Pg_DRAW_FILL );
}
The following example draws a stroked, unfilled rectangle:
void DrawStrokeRect( void )
{
PgSetStrokeColor( Pg_BLACK );
PgDrawIRect( 8, 8, 152, 112, Pg_DRAW_STROKE );
}
This code draw a stroked, filled rectangle:
void DrawFillStrokeRect( void )
{
PgSetFillColor( Pg_CYAN );
PgSetStrokeColor( Pg_BLACK );
PgDrawIRect( 8, 8, 152, 112, Pg_DRAW_FILL_STROKE );
}

Filled and stroked rectangles.
Rounded rectangles
Rounded rectangles are programmed almost the same way as rectangles — just call PgDrawRoundRect() with a PhPoint_t parameter to indicate, in pixels, the roundness of the rectangle corners. The radii are truncated to the rectangle's sides.
The following example draws a black rounded rectangle with five pixels worth of rounding at the corners:
void DrawStrokeRoundRect( void )
{
PhRect_t rect = { {20, 20}, {100, 100} };
PhPoint_t radii = { 5, 5 };
PgSetStrokeColor( Pg_BLACK );
PgDrawRoundRect( &rect, &radii, Pg_DRAW_STROKE );
}
Beveled boxes, rectangles, and arrows
PgDrawBevelBox() draws a beveled box, which is a special type of rectangle:
- If you set Pg_DRAW_FILL or Pg_DRAW_FILL_STROKE in the flags argument, the area of the beveled box is filled according to the current fill attributes.
- If you set Pg_DRAW_STROKE OR Pg_DRAW_FILL_STROKE in the flags, the top and left edges are drawn according to the current stroke attributes, and the bottom and left edges are drawn with an extra color that's passed as one of the parameters.
- There's also a parameter to let you set the with or “depth” of the bevel.
This code draws a dark grey beveled box with a green and red bevel that's four pixels wide:
void DrawBevelBox( void )
{
PhRect_t r = { 8, 8, 152, 112 };
PgSetFillColor( Pg_DGREY );
PgSetStrokeColor( Pg_RED );
PgDrawBevelBox( &r, Pg_GREEN, 4, Pg_DRAW_FILL_STROKE );
}

A beveled box.
You can call PgDrawBeveled() to draw a beveled rectangle (optionally with clipped or rounded corners) or a beveled arrow. If you draw a rectangle with square corners, the results are the same as for PgDrawBevelBox(). Here's some code that draws clipped and rounded rectangles, and a set of arrows:
void DrawBeveled() {
PhRect_t clipped_rect = { {10, 10}, {150, 62} };
PhRect_t rounded_rect = { {10, 67}, {150, 119} };
PhPoint_t clipping = { 8, 8 };
PhPoint_t rounding = { 12, 12 };
PhRect_t rup = { {190, 20}, {230, 40} };
PhRect_t rdown = { {190, 90}, {230, 110} };
PhRect_t rleft = { {165, 45}, {185, 85} };
PhRect_t rright = { {235, 45}, {255, 85} };
/* Draw beveled rectangles: one clipped, one rounded. */
PgSetFillColor( Pg_GREEN );
PgSetStrokeColor( Pg_GREY );
PgDrawBeveled( &clipped_rect, &clipping, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_CLIP );
PgDrawBeveled( &rounded_rect, &rounding, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_ROUND );
/* Draw beveled arrows. */
PgSetFillColor( Pg_CYAN );
PgSetStrokeColor( Pg_GREY );
PgDrawBeveled( &rup, NULL, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_AUP );
PgDrawBeveled( &rdown, NULL, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_ADOWN );
PgDrawBeveled( &rleft, NULL, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_ALEFT );
PgDrawBeveled( &rright, NULL, Pg_BLACK, 2,
Pg_DRAW_FILL_STROKE | Pg_BEVEL_ARIGHT );
}

Beveled rectangles and arrows.
If you want to draw an arrow that fits inside a given rectangle (for example, the arrow for a scrollbar), call PgDrawArrow().
Polygons
You can create polygons by specifying an array of PhPoint_t points. If you use Pg_CLOSED as part of the flags, the last point is automatically connected to the first point, closing the polygon. You can also specify points relative to the first point (using Pg_POLY_RELATIVE).
The following example draws a blue-filled hexagon with a white outline:
void DrawFillStrokePoly( void )
{
PhPoint_t start_point = { 0, 0 };
int num_points = 6;
PhPoint_t points[6] = {
{ 32,21 }, { 50,30 }, { 50,50 },
{ 32,59 }, { 15,50 }, { 15,30 }
};
PgSetFillColor( Pg_BLUE );
PgSetStrokeColor( Pg_WHITE );
PgDrawPolygon( points, num_points, start_point,
Pg_DRAW_FILL_STROKE | Pg_CLOSED );
}
Overlapping polygons
Polygons that overlap themselves are filled using the so-called even-odd rule: if an area overlaps an odd number of times, it isn't filled. Another way of looking at this is to draw a horizontal line across the polygon. As you travel along this line and cross the first line, you're inside the polygon; as you cross the second line, you're outside. As an example, consider a simple polygon:

Filling a simple polygon.
This rule can be extended for more complicated polygons:
- When you cross an odd number of lines, you're inside the polygon, so the area is filled.
- When you cross an even number of lines, you're outside the polygon, so the area isn't filled

Filling an overlapping polygon.
Arcs, circles, chords, and pies
The PgDrawArc() function can be used for drawing:
- arcs
- circles
- ellipses
- elliptical arcs
- chords
- pie slices
You can also call PgDrawEllipse() to draw an ellipse.
The start and end angles of arc segments are specified in binary gradations (bi-grads), with 65536 bi-grads in a complete circle.
To draw a full circle or ellipse, specify the same value in bi-grads for the start and end angles. For example:
void DrawFullCurves( void )
{
PhPoint_t circle_center = { 150, 150 },
ellipse_center = { 150, 300 };
PhPoint_t circle_radii = { 100, 100 },
ellipse_radii = { 100, 50 };
/* Draw a white, unfilled circle. */
PgSetStrokeColor( Pg_WHITE );
PgDrawArc( &circle_center, &circle_radii, 0, 0,
Pg_DRAW_STROKE | Pg_ARC );
/* Draw an ellipse with a white outline, filled
with black. */
PgSetFillColor( Pg_BLACK );
PgDrawArc( &ellipse_center, &ellipse_radii, 0, 0,
Pg_DRAW_FILL_STROKE | Pg_ARC );
}
To draw a chord (a curve with the end points connected by a straight line), add Pg_ARC_CHORD to the flags parameter. For example:
void DrawChord( void )
{
PhPoint_t center = { 150, 150 };
PhPoint_t radii = { 100, 50 };
/* Draw an elliptical chord with a white outline,
filled with black. The arc is drawn from 0 degrees
through to 45 degrees (0x2000 bi-grads). */
PgSetStrokeColor( Pg_WHITE );
PgSetFillColor( Pg_BLACK );
PgDrawArc( ¢er, &radii, 0, 0x2000,
Pg_DRAW_FILL_STROKE | Pg_ARC_CHORD );
}
Similarly, to draw a pie section or curve, add Pg_ARC_PIE or Pg_ARC to the flags. For example:
void DrawPieCurve( void )
{
PhPoint_t pie_center = { 150, 150 },
arc_center = { 150, 300 };
PhPoint_t pie_radii = { 100, 50 },
arc_radii = { 50, 100 };
/* Draw an elliptical pie with a white outline,
filled with black. */
PgSetStrokeColor( Pg_WHITE );
PgSetFillColor( Pg_BLACK );
PgDrawArc( &pie_center, &pie_radii, 0, 0x2000,
Pg_DRAW_FILL_STROKE | Pg_ARC_PIE );
/* Draw a black arc. */
PgSetStrokeColor( Pg_BLACK );
PgDrawArc( &arc_center, &arc_radii, 0, 0x2000,
Pg_DRAW_STROKE | Pg_ARC );
}

Filled and stroked arcs.
Spans — complex shapes
If the shape you want to draw can't be expressed as any of the other shapes that the Photon microGUI supports, you can draw it as a series of spans by calling PgDrawSpan().
This function takes as one of its arguments an array of PgSpan_t records. The members are:
- short x1
- Starting x position.
- short x2
- Last x position.
- short y
- Y position.
Lines, pixels, and pixel arrays
Lines and pixels are drawn using the current stroke state (color, thickness, etc.). The drawing primitives are:
- PgDrawBezier(), PgDrawBeziermx()
- Draw a stroked and/or filled Bézier curve
- PgDrawGrid()
- Draw a grid
- PgDrawLine(), PgDrawILine()
- Draw a single line
- PgDrawPixel(), PgDrawIPixel()
- Draw a single point
- PgDrawPixelArray(), PgDrawPixelArraymx()
- Draw multiple points
- PgDrawTrend(), PgDrawTrendmx()
- Draw a trend graph
The following example draws red, green, and blue lines:
void DrawLines( void )
{
PgSetStrokeColor( Pg_RED );
PgDrawILine( 8, 8, 152, 8 );
PgSetStrokeColor( Pg_GREEN );
PgDrawILine( 8, 8, 152, 60 );
PgSetStrokeColor( Pg_BLUE );
PgDrawILine( 8, 8, 152, 112 );
}

Lines created by the drawing primitives.
Text
There are various routines that draw text, depending on your requirements:
- PgDrawMultiTextArea()
- Draw multiline text in an area
- PgDrawString(), PgDrawStringmx()
- Draw a string of characters
- PgDrawText(), PgDrawTextmx()
- Draw text
- PgDrawTextArea()
- Draw text within an area
- PgDrawTextChars()
- Draw the specified number of text characters
- PgExtentMultiText()
- Calculate the extent of a multiline text string
- PgExtentText()
- Calculate the extent of a string of text
Text is drawn using the current text attributes; for more information, see “Text attributes,” above. If you set flags to Pg_BACK_FILL, the text's extent is filled according to the current fill attributes (see “Fill attributes”). If you define an underline with PgSetUnderline(), the underline is drawn under the text and on top of the backfill.
For example, to print black text in 18-point Helvetica:
void DrawSimpleText( void )
{
char *s = "Hello World!";
PhPoint_t p = { 8, 30 };
char Helvetica18[MAX_FONT_TAG];
if(PfGenerateFontName("Helvetica", 0, 18,
Helvetica18) == NULL) {
perror("Unable to generate font name");
} else {
PgSetFont( Helvetica18 );
}
PgSetTextColor( Pg_BLACK );
PgDrawText( s, strlen( s ), &p, 0 );
}
To print black text on a cyan background:
void DrawBackFillText( void )
{
char *s = "Hello World!";
PhPoint_t p = { 8, 30 };
char Helvetica18[MAX_FONT_TAG];
if(PfGenerateFontName("Helvetica", 0, 18,
Helvetica18) == NULL) {
perror("Unable to generate font name");
} else {
PgSetFont( Helvetica18 );
}
PgSetTextColor( Pg_BLACK );
PgSetFillColor( Pg_CYAN );
PgDrawText( s, strlen( s ), &p, Pg_BACK_FILL );
}
To print black text with a red underline:
void DrawUnderlineText( void )
{
char *s = "Hello World!";
PhPoint_t p = { 8, 30 };
char Helvetica18[MAX_FONT_TAG];
if(PfGenerateFontName("Helvetica", 0, 18,
Helvetica18) == NULL) {
perror("Unable to generate font name");
} else {
PgSetFont( Helvetica18 );
}
PgSetTextColor( Pg_BLACK );
PgSetUnderline( Pg_RED, Pg_TRANSPARENT, 0 );
PgDrawText( s, strlen( s ), &p, 0 );
PgSetUnderline( Pg_TRANSPARENT, Pg_TRANSPARENT, 0 );
}
To print black text with a red underline on a cyan background:
void DrawBackFillUnderlineText( void )
{
char *s = "Hello World!";
PhPoint_t p = { 8, 30 };
char Helvetica18[MAX_FONT_TAG];
if(PfGenerateFontName("Helvetica", 0, 18,
Helvetica18) == NULL) {
perror("Unable to generate font name");
} else {
PgSetFont( Helvetica18 );
}
PgSetTextColor( Pg_BLACK );
PgSetFillColor( Pg_CYAN );
PgSetUnderline( Pg_RED, Pg_TRANSPARENT, 0 );
PgDrawText( s, strlen( s ), &p, Pg_BACK_FILL );
PgSetUnderline( Pg_TRANSPARENT, Pg_TRANSPARENT, 0 );
}

Text created by the drawing primitives.
Bitmaps
Bitmaps are drawn using the current text state. If you set flags to Pg_BACK_FILL, the blank pixels in the image are drawn using the current fill state. The drawing primitives for bitmaps are:
- PgDrawBitmap(), PgDrawBitmapmx()
- Draw a bitmap
- PgDrawRepBitmap(), PgDrawRepBitmapmx()
- Draw a bitmap several times
This example draws the bitmap with a transparent background:
void DrawSimpleBitmap( void )
{
PhPoint_t p = { 8, 8 };
PgSetTextColor( Pg_CELIDON );
PgDrawBitmap( TestBitmap, 0, &p, &TestBitmapSize,
TestBitmapBPL, 0 );
}

A bitmap with a transparent background.
This example draws the bitmap against a yellow background:
void DrawBackFillBitmap( void )
{
PhPoint_t p = { 8, 8 };
PgSetTextColor( Pg_CELIDON );
PgSetFillColor( Pg_YELLOW );
PgDrawBitmap( TestBitmap, Pg_BACK_FILL, &p,
&TestBitmapSize, TestBitmapBPL, 0 );
}

A backfilled bitmap.
Images
This section discusses:
- Palette-based images
- Direct-color images
- Gradient-color images
- Creating images
- Caching images
- Transparency in images
- Displaying images
- Manipulating images
- Releasing images
The Photon microGUI supports these main types of images:
- direct color
- Consisting of:
- image data — a matrix of colors (but not necessarily of type PgColor_t). Each element in the matrix is the color of a single pixel.
Direct-color images have a type that starts with Pg_IMAGE_DIRECT_.
- palette-based
- Consisting of:
- a palette — an array of type PgColor_t
- image data — a matrix whose elements are offsets into the palette.
Palette-based images have a type that starts with Pg_IMAGE_PALETTE_.
- gradient color
- Colors are algorithmically generated as a gradient between two given colors.
You can define any image by its pixel size, bytes per line, image data, and format. Images can be stored in structures of type PhImage_t (described in the Photon Library Reference). The type field of this data structure identifies the type of image.
Palette-based images
Palette-based images provide a fast, compact method for drawing images. Before drawing a palette-based image, you must set either a hard palette or soft palette to define the colors for the image.
Setting a hard palette changes the physical palette. All colors set with a PgSetFillColor() function are chosen from this palette. Other processes continue to choose colors from the Photon microGUI's global palette and may appear incorrect. When you release the hard palette, the other processes return to normal without being redrawn. You should always release the hard palette when your window loses focus.
Setting a soft palette lets you redefine how colors are interpreted for the given draw context without changing the physical palette. All colors in the soft palette are mapped to the physical palette.
![]() |
If your physical palette uses more colors than your graphics card supports, some colors are dropped, and the image won't look as nice. |
The image data (either bytes or nibbles) is an index into the current palette. For example:
PgColor_t ImagePalette[256];
char *ImageData;
PhPoint_t ImageSize;
int ImageBPL;
void DrawYourImage( PhPoint_t pos )
{
PgSetPalette( ImagePalette, 0, 0, 256,
Pg_PALSET_SOFT );
PgDrawImage( ImageData, Pg_IMAGE_PALETTE_BYTE, pos,
ImageSize, ImageBPL, 0 );
}
Direct-color images
With direct-color images, every pixel can be any color. But compared to palette-based images, the image data is larger and the image may take longer to draw. You can choose from several types of direct-color images, listed in the description of PhImage_t in the Photon Library Reference; each has a different image-pixel size and color accuracy.
Gradient-color images
With gradient-color images, colors are algorithmically generated as a gradient between two given colors.
Creating images
To create a PhImage_t structure:
- Call
PhCreateImage()
Or:
- Call
PxLoadImage()
to load an image from disk.
Or:
- Call
ApGetImageRes()
to load an image from a PhAB widget database.
Or:
- Get the value of the
Pt_ARG_LABEL_IMAGE
resource
of a PtLabel or PtButton widget
(provided the widget's
Pt_ARG_LABEL_TYPE
is Pt_IMAGE or Pt_TEXT_IMAGE).
Or:
- Allocate it and fill in the members by hand.
![]() |
It's better to call PhCreateImage() than to allocate the structure and fill it in by hand. Not only does PhCreateImage() provide the convenience of setting up a blank image, but it also observes the restrictions that the graphics drivers impose on image alignment, and so on. |
Caching images
The image_tag and palette_tag members of the PhImage_t structure are used for caching images when dealing with remote processes via phrelay (see the QNX Neutrino Utilities Reference) for example, when using phindows.
These tags are cyclic-redundancy checks (CRCs) for the image data and the palette, and can be computed by PtCRC() or PtCRCValue() If these tags are nonzero, phindows and phditto cache the images. Before sending an image, phrelay sends its tag. If phindows finds the same tag in its cache, it uses the image in the cache. This scheme reduces the amount of data transmitted.
![]() |
You don't need to fill in the tags if the images don't need to be saved in the cache. For example, set the tags to 0 if you're displaying animation by displaying images, and the images never repeat. |
PxLoadImage() and ApGetImageRes() set the tags automatically. PhAB generates the tags for any images generated through it (for example, in the pixmap editor).
Transparency in images
If you want parts of an image to be transparent, you can:
- Use a chroma key.
Or:
- Create a transparency mask for the image.
Chroma is accelerated by most hardware, whereas transparency bitmaps are always implemented in software.
Using chroma
To make a given color transparent in an image, using chroma if possible, call PhMakeTransparent(), passing it the image and the RGB color that you want to be made transparent.
Using a transparency mask
The transparency mask is stored in the mask_bm member of the PhImage_t structure. It's a bitmap that corresponds to the image data; each bit represents a pixel:
| If the bit is: | The corresponding pixel is: |
|---|---|
| 0 | Transparent |
| 1 | Whatever color is specified in the image data |
The mask_bpl member of the PhImage_t structure specifies the number of bytes per line for the transparency mask.
You can build a transparency mask by calling PhMakeTransBitmap().
![]() |
If you use PxLoadImage() to load a transparent image, set PX_TRANSPARENT in the flags member of the PxMethods_t structure. If you do this, the function automatically makes the image transparent; you don't need to create a transparency mask. |
Displaying images
There are various ways to display an image:
- If the image is stored in a PhImage_t structure, call
PgDrawPhImage()
or
PgDrawPhImagemx().
These functions automatically handle chroma key, alpha operations,
ghosting, transparency, and so on.
To draw the image repeatedly, call PgDrawRepPhImage() or PgDrawRepPhImagemx().
To draw a rectangular portion of the image, call PgDrawPhImageRectmx().
- If the image isn't stored in a PhImage_t data
structure, call
PgDrawImage()
or
PgDrawImagemx().
To draw the image repeatedly, call PgDrawRepImage() or PgDrawRepImagemx()
- If the image isn't stored in a PhImage_t structure and has a transparency mask, call PgDrawTImage() or PgDrawTImagemx().
- Set the Pt_ARG_LABEL_IMAGE resource for a PtLabel or PtButton widget (which use PgDrawPhImagemx() internally). The widget's Pt_ARG_LABEL_TYPE must be Pt_IMAGE or Pt_TEXT_IMAGE.
The mx versions of these functions place the address of the image into the draw buffer in your application's data space. When the draw buffer is flushed, the entire image is copied to the graphics driver. The non-mx versions copy the image itself into the draw buffer.
You can speed up the drawing by using shared memory. Call PgShmemCreate() to allocate the image data buffer:
my_image->image = PgShmemCreate( size, NULL );
If you do this, the image data isn't copied to the graphics driver.
Manipulating images
The following functions let you manipulate images:
- PiConvertImage()
- Convert an image to another type
- PiCropImage()
- Crop an image to the specified boundary
- PiDuplicateImage()
- Duplicate an image
- PiFlipImage()
- Flip all or part of an image
- PiGetPixel()
- Retrieve the value of a pixel within an image
- PiGetPixelFromData()
- Retrieve a value from a run of pixels
- PiGetPixelRGB()
- Retrieve the RGB value of a pixel within an image
- PiResizeImage()
- Resize an image
- PiSetPixel()
- Alter the value of a pixel within an image
- PiSetPixelInData()
- Set the value of a pixel in a run of pixels
Releasing images
The PhImage_t structure includes a flags member that can make it easier to release the memory used by an image. These flags indicate which members you would like to release:
- Ph_RELEASE_IMAGE
- Ph_RELEASE_PALETTE
- Ph_RELEASE_TRANSPARENCY_MASK
- Ph_RELEASE_GHOST_BITMAP
Calling PhReleaseImage() with an image frees any resources that have the corresponding bit set in the image flags.
![]() |
|
The flags for images created by ApGetImageRes(), PiCropImage(), PiDuplicateImage(), PiFlipImage(), and PxLoadImage() aren't set. If you want PhReleaseImage() to free the allocated members, you'll have to set the flags yourself:
my_image->flags = Ph_RELEASE_IMAGE |
Ph_RELEASE_PALETTE |
Ph_RELEASE_TRANSPARENCY_MASK |
Ph_RELEASE_GHOST_BITMAP;
When should you set the release flags? When you know that the image is referred to only by one entity. For example, if one widget will be using an image, then it should free the image once it's done with it. If you set the release flags appropriately, prior to setting the image resource, then this will happen automatically — that is, the widget will free the image and data when it's destroyed, or you apply a new setting for the resource.
If multiple widgets use the same image (they have their own copies of the image structure but share the data to conserve memory), then you need to be a little more clever and make sure the image is freed only when all the widgets are done with it, and never before. There are a number of ways to accomplish this. For example, you could:
- Release the image in a Pt_CB_DESTROYED, but you would need to be sure that no other widgets are using it. If you know that one widget will survive the rest, then release the image in its Pt_CB_DESTROYED. Otherwise you need a more sophisticated approach, like your own reference count.
- Alternatively, if you know one widget will outlast all the others using the image, then set the release flags in the structure prior to setting the image resource of that widget. All the rest should have the flags clear. Note that if you change the image resource on that widget, however, the image will be freed, thus invalidating all the other widgets' references to it!
The approach you take will depend on your situation and requirements.
If the image is stored in a widget, the allocated members of images are automatically freed when an new image is specified or the widget is destroyed, provided that the appropriate bits in the flags member of the PhImage_t structure are set before the image is added to the widget.
Animation
This section describes how you can create simple animation. There are two basic steps:
- creating a series of “snapshots” of the object in motion
- cycling through the snapshots
![]() |
It's better to use images for animation than bitmaps, as images aren't transparent (provided you haven't created a transparency mask). This means that the background doesn't need to be redrawn when replacing one image with another. As a result, there's no flicker when you use images. For other methods of eliminating flicker, see “Flickerless animation”, below. |
It's also possible to create animation by using a PtRaw widget and the Photon drawing primitives. See “PtRaw widget”, earlier in this chapter.
Creating a series of snapshots
To animate an image you'll need a series of snapshots of it in motion. For example, you can use a PtLabel widget (with a Pt_ARG_LABEL_TYPE of Pt_IMAGE or Pt_TEXT_IMAGE) for animation. Create one PtLabel widget where you want the animation to appear, and create another PtLabel widget for each snapshot. You can store these snapshots in a widget database or a file.
Using a widget database
As described in “Widget databases” in the Accessing PhAB Modules from Code chapter, you can use a picture module as a widget database. To use one for animation, do the following in PhAB:
- Create a picture module to use as widget database.
- Create an internal link to the picture module.
- Create the snapshots of the object in motion. Use the same widget type as you use where the animation is to appear. Give each snapshot a unique instance name.
In your application's initialization function, open the database by calling ApOpenDBase() or ApOpenDBaseFile(). Then, load the images with the ApGetImageRes() function. For example:
/* global data */
PhImage_t *images[4];
ApDBase_t *database;
int cur_image = -1,
num_images = 4;
int
app_init( int argc, char *argv[])
{
int i;
char image_name[15];
/* eliminate 'unreferenced' warnings */
argc = argc, argv = argv;
database = ApOpenDBase (ABM_image_db);
for (i = 0; i < num_images; i++)
{
sprintf (image_name, "image%d", i);
images[i] = ApGetImageRes (database, image_name);
}
return (PT_CONTINUE);
}
![]() |
ApGetImageRes() returns a pointer into the widget database. Don't close the database while you're still using the images in it. |
Using a file
You can also load the snapshots from a file into a PhImage_t structure, by using the PxLoadImage() function. This function supports a number of formats, including GIF, PCX, JPG, BMP, and PNG. For a complete list, see /usr/photon/dll/pi_io_*.
Cycling through the snapshots
No matter where you get the images, the animation is the same:
- Create a PtTimer widget in your application. PhAB displays it as a black box; it won't appear when you run your application.
- Specify the initial (Pt_ARG_TIMER_INITIAL) and repeat (Pt_ARG_TIMER_REPEAT) timer intervals.
- Create an activate (Pt_CB_TIMER_ACTIVATE) callback for the timer. In the callback, determine the next image to be displayed, and copy it into the destination widget.
For example, the callback for the timer could be as follows:
/* Display the next image for our animation example. */
/* Standard headers */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/* Toolkit headers */
#include <Ph.h>
#include <Pt.h>
#include <Ap.h>
/* Local headers */
#include "globals.h"
#include "abimport.h"
#include "proto.h"
int
display_image( PtWidget_t *widget,
ApInfo_t *apinfo,
PtCallbackInfo_t *cbinfo )
{
/* eliminate 'unreferenced' warnings */
widget = widget, apinfo = apinfo, cbinfo = cbinfo;
cur_image++;
if (cur_image >= num_images)
{
cur_image=0;
}
PtSetResource (ABW_base_image, Pt_ARG_LABEL_IMAGE,
images[cur_image], 0 );
PtFlush ();
return( Pt_CONTINUE );
}
ABW_base_image is the widget name of the PtLabel widget in which the animation appears.
Flickerless animation
There are two ways to eliminate flicker in animation:
- Create a PtOSContainer (an offscreen-context container)
and make it the parent of all the widgets in the area being animated.
Or:
- Use the PmMem...() memory-context functions to build the image in memory, and display it when complete.
PtOSContainer
When you do animation in a child of an offscreen-context container, the PtOSContainer renders the draw stream into offscreen video memory, taking advantage of any hardware-acceleration features that the graphics driver supports. The graphics hardware can then blit the image directly onto the screen, resulting in flicker-free widgets and/or animation.
![]() |
PtRaw (like any other widget) can be a child of PtOSContainer. This means that you can have flicker-free animation even when using the Photon drawing primitives. |
Memory-context functions
You can call these functions to use a memory context to reduce flickering:
- PmMemCreateMC()
- Create a memory context
- PmMemFlush()
- Flush a memory context to its bitmap
- PmMemReleaseMC()
- Release a memory context
- PmMemSetChunkSize()
- Set the increment for growing a memory context's draw buffer
- PmMemSetMaxBufSize()
- Set the maximum size of a memory context's draw buffer
- PmMemSetType()
- Set the type of a memory context
- PmMemStart()
- Make a memory context active
- PmMemStop()
- Deactivate a memory context
Start by creating a memory context:
PmMemoryContext_t * PmMemCreateMC(
PhImage_t *image,
PhDim_t *dim,
PhPoint_t *translation );
The image structure must at least specify the type and size members. The image data buffer is optional, but if you want it in shared memory, you must provide it. The image type must be either Pg_IMAGE_DIRECT_888 or Pg_IMAGE_PALETTE_BYTE.
Once you've created the memory context:
- Call PmMemStart() to set the current draw context to the memory context.
- Call PmMemStop() when finished your drawing, to return to the default draw context.
- Call PmMemFlush() to get the resulting image.
When you no longer need the memory context, call PmMemReleaseMC().
Direct mode
In normal (nondirect) mode, an application sends drawing requests to the Photon manager. The graphics driver blocks on the Photon manager.

Communication in normal (nondirect) mode.
When an application enters direct mode, it's requesting that the graphics driver receive draw streams and service messages directly from the application, instead of from the Photon manager. The driver blocks on the application, which is now responsible for telling the graphics driver what to do.

Communication in direct mode.
While in direct mode, the application has complete control over the display, since no other applications are able to be serviced by the graphics driver. The graphics driver's region is also no longer sensitive to draw events (this way the Photon manager discards all other applications' requests for rendering services to this driver). The other benefit with this mode is that graphical services are no longer sent through the Photon event space, so performance is improved. The drawback for this mode is that applications that expect to capture draw events can't record the application's view.
For convenience, a new context type, called a PdDirectContext_t, has been created. This context, when activated, becomes the default context for the application, so all other Photon Pg* calls work correctly while in this mode.
![]() |
While in this mode, the origin of all drawing operations is the upper left corner of the display, since the requests are no longer clipped or translated by the Photon event space. Your application can still translate and clip the events by calling PgSetTranslation() and PgSetClipping() if necessary. |
The following functions deal with direct mode:
- PdCreateDirectContext()
- Create a direct-mode context
- PdDirectStart()
- Enter direct mode
- PdDirectStop()
- Leave direct mode
- PdGetDevices()
- Get region IDs for the currently available draw devices
- PdReleaseDirectContext()
- Leave direct mode and release the direct-mode context
- PdSetTargetDevice()
- Set the target device
- PgWaitVSync()
- Wait for vertical synchronization
Here are some things to keep in mind:
- When you enter or leave direct mode, all video RAM contexts (except the display), are destroyed on the driver side (an OSINFO event is shot out by the driver so applications are notified and can reinitialize any video memory contexts). This includes video RAM used by PdOffscreenContext_t structures and anything used by the video overlay API.
- When you leave direct mode, an expose event is also sent out by the driver, so all other applications redraw themselves.
- When you're in direct mode, the graphics driver region is no longer sensitive to draw events (so the Photon manager doesn't build up a massive list of draw events to be processed from other applications).
- If you have automatic double buffering turned on (e.g. devg-banshee -B ...), it's turned off while you're in direct mode (to let applications control the double buffering themselves).
Example
Here's how to get the address of any video memory context (including the display, which is considered to be one).
If you create a direct context by calling PdCreateDirectContext(), and then enter direct mode by calling PdDirectStart(), your application “owns” the graphics driver (PgFlush() goes to the video driver directly, instead of to the Photon server). You don't need to be in direct mode to get a pointer to offscreen RAM, but you do need to be to get a pointer to the primary display.
Here's some pseudo-code:
/* Create the direct context. */
direct_context = PdCreateDirectContext();
/* Start Direct Mode. */
PdDirectStart(direct_context);
/* Get the primary display. */
primary_display = PdCreateOffscreenContext( 0, 0, 0,
Pg_OSC_MAIN_DISPLAY);
/* Get a pointer to the display. */
vidptr = PdGetOffscreenContextPtr(primary_display);
/* Make sure the Photon driver isn't doing anything
(it shouldn't be at this point but this is just to
be sure that we haven't gotten ahead of the video
card's draw engine). */
PgWaitHWIdle();
/* Do what ever you do to the memory. */
Do_something(vidptr);
/* Leave direct mode, and destroy the direct context
(an alternative would be PdDirectStop if you don't
want to destroy the context). */
PdReleaseDirectContext(direct_context);
Video memory offscreen
These API calls allow you to use the leftover memory on a video card. When a video card is in a video mode, there's usually video RAM leftover that isn't being used by the display area. These areas of RAM can be used to do a variety of graphical operations while still using the accelerator on the video card. They're treated in the Photon microGUI basically the same way that a memory context is used, but should be much faster because there's hardware acceleration for these areas.
The functions and data structures include:
- PdCreateOffscreenContext()
- Create an offscreen context in video RAM
- PdDupOffscreenContext()
- Duplicate an offscreen context
- PdGetOffscreenContextPtr()
- Create a shared memory object reference to an offscreen context
- PdOffscreenContext_t
- Data structure that describes an offscreen context
- PdSetOffscreenTranslation()
- Set the translation for an offscreen context
- PdSetTargetDevice()
- Set the target device
- PgContextBlit()
- Copy data from a rectangle in one context to another context
- PgContextBlitArea()
- Copy data from an area in one context to another context
- PgSwapDisplay()
- Point the CRT of the video display at a given context
- PgWaitHWIdle()
- Wait until the video driver is idle
- PhDCRelease()
- Release a draw context
Here's an example that loads an image, creates an offscreen context for the image, and then blits the image data to the screen. It creates a window that contains a PtRaw, and uses PgContextBlit() in the PtRaw's raw drawing callback to redraw the image whenever the window is damaged or resized. You can specify a starting size for the window by passing -h and -w commandline options, followed by the path to an image (the format must be supported by PxLoadImage()).
![]() |
|
#include <Pt.h>
#include <photon/PxImage.h>
static PdOffscreenContext_t *context;
static void *my_alloc(long nbytes,int type);
static void raw_draw(PtWidget_t *widget,PhTile_t *damage);
int main(int argc,char *argv[])
{
int c;
PhDim_t dim = { 0,0 };
if(PtInit(NULL))
return(-1);
while((c = getopt(argc,argv,"h:w:")) != -1)
{
switch(c)
{
case 'h':
dim.h = atoi(optarg);
break;
case 'w':
dim.w = atoi(optarg);
break;
}
}
if(argv[optind])
{
PxMethods_t methods;
PhImage_t *image;
memset(&methods,0,sizeof(methods));
methods.px_alloc = my_alloc;
methods.flags = PX_DIRECT_COLOR;
if((image = PxLoadImage(argv[optind],&methods)) != NULL)
{
/* Create a context to render the image into. The context will be
created to be the size of the image and will store an exact copy
of the original. Note: if you are short on video RAM, you
might want to enable the Pg_OSC_MEM_SYS_ONLY flag to force
the context to go to system RAM. This will result in a slower
1:1 blit though because the video h/w will not be able to access
the image data directly - the data will have to be transferred
from system memory (over the PCI bus) to video memory. However
if using a s/w scaled blit (ie scaled blit not supported in your
h/w) it's better for the original image to be in system
RAM because otherwise the CPU has to read the original,
unscaled image from video RAM (over the PCI bus) to
scale, then put it back into video RAM (over the PCI bus).
The round trip (particularly the read) is expensive. */
if((context = PdCreateOffscreenContext(image->type,
image->size.w,image->size.h,0)) != NULL)
{
PtArg_t args[4];
PtWidget_t *window;
PhDrawContext_t *dc = PhDCSetCurrent(context);
if(!dim.w || !dim.h)
dim = image->size;
PgSetFillColor(Pg_WHITE);
PgDrawIRect(0,0,image->size.w - 1,image->size.h - 1,Pg_DRAW_FILL);
PgDrawPhImagemx(NULL,image,0);
PgFlush();
PgWaitHWIdle();
PhDCSetCurrent(dc);
image->flags |= Ph_RELEASE_IMAGE_ALL;
PhReleaseImage(image);
free(image);
/* create a PtWindow with a PtRaw inside to draw the image */
PtSetArg(&args[0],Pt_ARG_DIM,&dim,0);
PtSetArg(&args[1],Pt_ARG_WINDOW_TITLE,argv[optind],0);
if((window = PtCreateWidget(PtWindow,Pt_NO_PARENT,2,args)) != NULL)
{
PhRect_t arect = { { 0,0 },{ 0,0 } };
PtSetArg(&args[1],Pt_ARG_RAW_DRAW_F,raw_draw,0);
PtSetArg(&args[2],Pt_ARG_ANCHOR_FLAGS,
Pt_LEFT_ANCHORED_LEFT | Pt_RIGHT_ANCHORED_RIGHT |
Pt_TOP_ANCHORED_TOP | Pt_BOTTOM_ANCHORED_BOTTOM,
Pt_LEFT_ANCHORED_LEFT | Pt_RIGHT_ANCHORED_RIGHT |
Pt_TOP_ANCHORED_TOP | Pt_BOTTOM_ANCHORED_BOTTOM);
PtSetArg(&args[3],Pt_ARG_ANCHOR_OFFSETS,&arect,0);
if(PtCreateWidget(PtRaw,Pt_DFLT_PARENT,4,args) != NULL)
{
PtRealizeWidget(window);
PtMainLoop();
return(0);
}
}
}
}
}
return(-1);
}
static void *my_alloc(long nbytes,int type)
{
return(type == PX_IMAGE ? PgShmemCreate(nbytes,NULL) : malloc(nbytes));
}
static void raw_draw(PtWidget_t *widget,PhTile_t *damage)
{
/* raw widget draw function; simply blit the context onto the screen.
PgContextBlit() will take care of scaling */
PhRect_t src;
src.ul.x = src.ul.y = 0;
src.lr.x = context->dim.w - 1;
src.lr.y = context->dim.h - 1;
PgContextBlit(context,&src,PhDCGetCurrent(),PtCalcCanvas(widget,NULL));
}
Offscreen contexts can be invalidated by the graphics driver for a number of reasons. When this happens, the graphics driver sends to the Photon manager a Ph_EV_INFO event with a subtype of Ph_OFFSCREEN_INVALID. The event data is a single long describing why the offscreen areas have been invalidated. The possible reasons are as follows:
- Pg_VIDEO_MODE_SWITCHED
- The graphics driver has changed video modes.
- Pg_ENTERED_DIRECT
- An application has entered direct mode.
- Pg_EXITED_DIRECT
- An application has left direct mode.
- Pg_DRIVER_STARTED
- The video driver has just started execution.
Applications planning on using offscreen contexts should be sensitive to this event and reinitialize their off screen contexts accordingly.
Offscreen locks
You generally use offscreen locks with pointers that you gained via PdGetOffscreenContextPtr(). The locks ensure that:
- Draw stream commands don't draw while the offscreen context is locked.
- The memory is valid while the application is using it.
![]() |
Your application should lock offscreen memory for as little time as possible. If the graphics driver needs to do something with the offscreen memory, it tries to gain a lock itself, potentially blocking io-graphics for a long period of time (the result being that the display may not get updated, and the user thinks that the computer has locked up). |
The locks are implemented as semaphores in shared memory between io-graphics and the application.
The basic steps for using offscreen locks are:
- Create a lock for an offscreen context by calling PdCreateOffscreenLock(). You can arrange for a signal to be dropped on the application if a request is made to remove the offscreen context while it's locked.
- Lock the offscreen context, when required, by calling PdLockOffscreen(). You can optionally specify a timeout for the blocking.
- Unlock the offscreen context by calling PdUnlockOffscreen().
- When you no longer need to lock the offscreen context, destroy the lock by calling PdDestroyOffscreenLock().
When you're debugging, you can call PdIsOffscreenLocked() to determine whether or not the offscreen context is currently locked.
![]() |
If you've locked the context, call PdUnlockLockOffscreen() to unlock it before destroying the lock or releasing the offscreen context. |
Alpha blending support
Alpha blending is a technique of portraying transparency when drawing an object. It combines the color of an object to be drawn (the source) and the color of whatever the object is to be drawn on top of (the destination). The higher the portion of source color, the more opaque the object looks.
Alpha blending can be applied in three ways:
- As a global factor that applies to every pixel of the source
- With a map that indicates the alpha blending to be applied to each individual pixel. Alpha maps are “pinned” to the origin of your draw command and are tiled if the dimensions of the map are smaller than the dimension of the drawing operation.
- On a per-pixel basis
A 32-bit color is made up of four 8-bit channels: alpha, red, green, and blue. These channels are represented as (A, R, G, B). When referring to the source, the channels are denoted as As, Rs, Gs, and Bs; for the destination, they're Ad, Rd, Gd, and Bd.
The basic formula for alpha blending is:
Sm = source pixel * source multiplier
Dm = destination pixel * destination multiplier
destination pixel = Sm + Dm
There are several options for multipliers to achieve different blending effects. Flags are defined for source and destination multipliers in PgSetAlpha().
You can also perform an “alpha test”, which tests for certain conditions in the alpha channel before writing the source pixel to the destination. In an alpha test, pixels aren't blended — the source pixel is either written to the destination or it's not. For example, you can set the operation to only write the source pixel to the destination if the source alpha is less than the destination alpha.
The functions include:
- PgAlphaOff()
- Turn alpha blending operations off
- PgAlphaOn()
- Turn alpha blending operations on
- PgAlphaValue()
- Extract the alpha component from a color value
- PgARGB()
- Convert alpha, red, green, and blue values to composite color format
- PgSetAlpha()
- Set the parameters for alpha blending in detail
- PgSetAlphaBlend()
- Set the parameters for alpha blending simply
Chroma key support
Chroma-key operations are a method of masking out pixel data during a rendering operation (copies, image rendering, rectangles, etc.) based on a chroma color value. The basic modes of operation are:
- Masking on the source key color
- Masking on the destination key color
- Masking on everything but the source key color
- Masking on everything but the destination key color.
The functions include:
- PgChromaOff()
- Turn chroma key operations off
- PgChromaOn()
- Turn chroma operations on
- PgSetChroma()
- Set the chroma color and operation
Extended raster operations
The Photon microGUI supports 256 raster operations. Operations can be done using a combination of source pixel data, destination pixel data, and color expanded monochrome pattern pixel data. Extended raster operations are set the same way the normal raster operations are set, using PgSetDrawMode().
The extended raster operations are pervasive, meaning that they affect all subsequent drawing operations, including bit-blit operations and images. The old style raster operations still exist and behave the same way they did in earlier versions of the Photon microGUI.
The extended raster operations are defined as Pg_DrawModecharacters, in reverse notation, where the characters are chosen from the following:
| Character | Meaning |
|---|---|
| P | Pattern |
| S | Source |
| D | Destination |
| o | OR |
| a | AND |
| n | NOT |
| x | XOR |
For example:
- Pg_DrawModeS
- Copy all source data.
- Pg_DrawModePSo
- Logically OR the source data with the pattern data.
For a complete list of all raster operations available, see <photon/Pg.h>.
Here's some sample code:
PdOffscreenContext_t *context1; PhRect_t rsrc,rdst; /* Initialize the offscreen area and render the data we want in it. */ … /* Copy an image stored in an offscreen context to the display, ORing the source and pattern data together. */ rsrc.ul.x = rdst.ul.x = rsrc.ul.y = rdst.ul.y = 0; rsrc.lr.x = rdst.lr.x = rsrc.lr.y = rdst.lr.y = 100; PgSetDrawMode(Pg_DrawModePSo); PgSetFillDither(Pg_BLUE,Pg_BLACK,Pg_PAT_CHECKB8); PgContextBlit(context1, &rsrc, NULL, &rdst); /* OR a blue and black checkerboard pattern with source data and copy it to the display area. */ PgFlush();
Video modes
A video mode describes what the display (what you see on your monitor) looks like. The description includes:
- Width
- The width of the display, in pixels.
- Height
- The height of the display, in pixels.
- Pixel depth
- The number of bits used to represent a pixel. This affects how many unique colors you can see on the screen at one time.
- Refresh rate
- How many times per second the phosphor on the CRT of your monitor is updated (represented in Hz).
The Photon microGUI's method of video mode enumeration is similar to the VESA spec, where there are “mode numbers”, numerical representations of the width, height, and pixel depth of a video mode. The refresh rate is independent of the mode numbers (it's a different member of PgDisplaySettings_t).
The driver determines the mode numbers, so for one video card 640x480x8 might be mode 2, while on another card it might be mode 3022. Use PgGetVideoModeInfo() to determine the properties of any given mode number. Use PgGetVideoModeList() to get a list of the mode numbers supported by a particular graphics driver.
The functions for working with video modes are:
- PdSetTargetDevice()
- Set the target device
- PgGetGraphicsHWCaps()
- Determine the hardware capabilities
- PgGetVideoMode()
- Get the current video mode
- PgGetVideoModeInfo()
- Get information about a video mode
- PgGetVideoModeList()
- Query a graphics driver for a list of its supported video modes
- PgSetVideoMode()
- Set the current video mode
Here's some sample code:
PgVideoModes_t ModeList;
PgVideoModeInfo_t ModeInfo;
PgDisplaySettings_t ModeSetting;
int i=0, done=0;
if (PgGetVideoModeList(&ModeList))
{
/* Error -- driver doesn't support this. */
}
/* Use the default refresh rate for this mode. */
ModeSetting.refresh = 0;
while (!done)
{
if (PgGetVideoModeInfo(ModeList.modes[i], &ModeInfo))
{
/* Error code */
}
if ((ModeInfo.width == 640) && (ModeInfo.height == 480) &&
(ModeInfo.bits_per_pixel == 16))
{
/* We found the mode we were looking for. */
done = 1;
ModeSetting.mode = ModeList.modes[i];
}
i++;
if (i >= ModeList.num_modes)
{
/* Error -- Mode wasn't found. */
done=1;
}
}
PgSetVideoMode (&ModeSetting);
Gradients
A gradient is a gradual blend of two colors. The Photon library supports:
- Driver-level gradients — quick, but not sophisticated. Accuracy is sacrificed for speed.
- Application-level gradients — slower, but more accurate.
Driver-level gradients
Although the Photon library supports a large variety of gradients (see PhImage_t), there are times when you would just want a simple gradient to be rendered without having to store it in a PhImage_t. As a result, some basic gradient rendering operations have been added to the graphics driver:
- PgDrawGradient()
- Ask the graphics driver to render a gradient
Application-level gradients
These functions let you create your own gradients:
- PgBevelBox()
- Draw a beveled box with gradients
- PgCalcColorContrast()
- Compute light and dark colors to use for a gradient
- PgContrastBevelBox()
- Draw a beveled box with gradients and a given level of contrast
- PgDrawGradientBevelBox()
- Draw a beveled box with gradients and two flat colors
Video overlay
A video overlay scaler is a hardware feature that allows a rectangular area of the visible screen to be replaced by a scaled version of a different image. The prescaled video frames are typically stored in offscreen memory, and are fetched from memory and overlaid on top of the desktop display image in real time, by the overlay scaler.
Chroma keying is used to control what parts of the video frame are visible. Typically, the application picks a color to be the chroma-key color and draws a rectangle of this color where video content is to appear. When another application's window is placed on top of the video playback application, the chroma-colored rectangle is obscured. Since the video hardware is programmed to display video content only where the chroma-key color is drawn, video doesn't show through where the chroma-colored rectangle is obscured.
The following functions and data types deal with video overlay:
- PgConfigScalerChannel()
- Configure a video overlay scaler channel
- PgCreateVideoChannel()
- Create a channel for video streaming
- PgDestroyVideoChannel()
- Destroy resources associated with a video channel
- PgGetOverlayChromaColor()
- Return the color used for video overlay chroma-key operations
- PgGetScalerCapabilities()
- Get the capabilities of a video overlay scaler
- PgNextVideoFrame()
- Get the index of the next video buffer to fill
- PgScalerCaps_t
- Data structure that describes video overlay scaler capabilities
- PgScalerProps_t
- Data structure that describes video overlay scaler properties
- PgVideoChannel_t
- Data structure that describes a video overlay channel
Example
#include <stdio.h>
#include <Ph.h>
#define SRC_WIDTH 100
#define SRC_HEIGHT 100
#define DATA_FORMAT Pg_VIDEO_FORMAT_YV12
unsigned char *ybuf0, *ybuf1;
unsigned char *ubuf0, *ubuf1;
unsigned char *vbuf0, *vbuf1;
void
grab_ptrs(PgVideoChannel_t *channel)
{
/* Buffers have moved; get the pointers again. */
ybuf0 = PdGetOffscreenContextPtr(channel->yplane1);
ybuf1 = PdGetOffscreenContextPtr(channel->yplane2);
ubuf0 = PdGetOffscreenContextPtr(channel->uplane1);
ubuf1 = PdGetOffscreenContextPtr(channel->uplane2);
vbuf0 = PdGetOffscreenContextPtr(channel->vplane1);
vbuf1 = PdGetOffscreenContextPtr(channel->vplane2);
if (channel->yplane1)
fprintf(stderr, "ybuf0: %x, stride %d\n", ybuf0,
channel->yplane1->pitch);
if (channel->uplane1)
fprintf(stderr, "ubuf0: %x, stride %d\n", ubuf0,
channel->uplane1->pitch);
if (channel->vplane1)
fprintf(stderr, "vbuf0: %x, stride %d\n", vbuf0,
channel->vplane1->pitch);
if (channel->yplane2)
fprintf(stderr, "ybuf1: %x, stride %d\n", ybuf1,
channel->yplane2->pitch);
if (channel->uplane2)
fprintf(stderr, "ubuf1: %x, stride %d\n", ubuf1,
channel->uplane2->pitch);
if (channel->vplane2)
fprintf(stderr, "vbuf1: %x, stride %d\n", vbuf1,
channel->vplane2->pitch);
}
void
overlay_example()
{
PgVideoChannel_t *channel;
PgScalerCaps_t vcaps;
PgScalerProps_t props;
unsigned char *ptr;
unsigned short *ptr16;
int i = 0, j, k, index;
int color;
PhDrawContext_t *old;
int rc;
if ((channel = PgCreateVideoChannel(
Pg_VIDEO_CHANNEL_SCALER, 0)) == NULL) {
perror("PgCreateVideoChannel");
exit(1);
}
/*
* Cycle through the available formats looking for the one
* we're interested in.
*/
vcaps.size = sizeof (vcaps);
while (PgGetScalerCapabilities(channel, i++, &vcaps) == 0) {
if (vcaps.format == DATA_FORMAT)
break;
vcaps.size = sizeof (vcaps);
}
if (vcaps.format != DATA_FORMAT) {
fprintf(stderr, "Format not supported?\n");
exit(1);
}
props.size = sizeof (props);
props.format = DATA_FORMAT;
props.viewport.ul.x = 20;
props.viewport.ul.y = 20;
props.viewport.lr.x = 600;
props.viewport.lr.y = 440;
props.src_dim.w = SRC_WIDTH;
props.src_dim.h = SRC_HEIGHT;
props.flags =
Pg_SCALER_PROP_SCALER_ENABLE |
Pg_SCALER_PROP_DOUBLE_BUFFER |
Pg_SCALER_PROP_DISABLE_FILTERING;
if (PgConfigScalerChannel(channel, &props) == -1) {
fprintf(stderr, "Configure channel failed\n");
exit(1);
}
grab_ptrs(channel);
for (i = 0; i < 100; i++) {
index = PgNextVideoFrame(channel);
delay(50);
ptr = (void *)(index ? ybuf1 : ybuf0);
color = rand() & 0xff;
for (k = 0; k < props.src_dim.h; k++) {
memset(ptr, color, channel->yplane1->pitch);
ptr += channel->yplane1->pitch;
}
}
props.flags &= ~Pg_SCALER_PROP_DISABLE_FILTERING;
switch (PgConfigScalerChannel(channel, &props)) {
case -1:
fprintf(stderr, "Configure channel failed\n");
exit(1);
break;
case 1:
grab_ptrs(channel);
break;
case 0:
default:
break;
}
fprintf(stderr, "\"TV snow\" effect\n");
for (i = 0; i < 1000; i++) {
index = PgNextVideoFrame(channel);
ptr = (void *)(index ? ybuf1 : ybuf0);
for (k = 0; k < props.src_dim.h; k++) {
for (j = 0; j < channel->yplane1->pitch; j++)
*(ptr + j) = rand() & 0xff;
ptr = (void *)((char *)ptr + channel->yplane1->pitch);
}
/* Set the chromanance to neutral for monochrome */
ptr = ubuf0;
for (i = 0; i < props.src_dim.h; i++) {
memset(ptr, 128, props.src_dim.w / 2);
ptr += channel->uplane1->pitch;
}
ptr = vbuf0;
for (i = 0; i < props.src_dim.h; i++) {
memset(ptr, 128, props.src_dim.w / 2);
ptr += channel->vplane1->pitch;
}
if (rand() % 200 == 23) {
props.viewport.ul.x = rand() % 400;
props.viewport.ul.y = rand() % 300;
props.viewport.lr.x =
props.viewport.ul.x + SRC_WIDTH + rand() % 200;
props.viewport.lr.y =
props.viewport.ul.y + SRC_HEIGHT + rand() % 200;
if (PgConfigScalerChannel(channel, &props) == 1)
grab_ptrs(channel);
}
}
/*
* This isn't really necessary, since the video resources
* should automatically be released when the app exits
*/
PgDestroyVideoChannel(channel);
}
int
main(int argc, char *argv[])
{
PhAttach(NULL, NULL);
overlay_example();
fprintf(stderr, "Exiting normally\n");
}
Layers
Some display controllers allow you to transparently overlay multiple "screens" on a single display. Each overlay is called a layer.
Layers can be used to combine independent display elements. Because overlaying is performed by the graphics hardware, it can be more efficient than rendering all of the display elements onto a single layer. For example, a fast navigational display can be implemented with a scrolling navigational map on a background layer, and pop-up GUI elements, such as menus or a web browser, on a foreground layer.
Layer capabilities vary depending on the display controller and the driver. Some display controllers don't support layers. Different layers on the same display may have different capabilities. You should use PgGetLayerCaps() to determine whether a layer exists and which features are supported by the layer.
Layers are indexed per-display, starting from 0, from back to front in the default overlay order.
A layer is either active (shown) or inactive (hidden). It may not be possible to activate a layer if its configuration is incomplete (if, for example, the layer format is unspecified, or there aren't enough surfaces assigned to it). A layer's configuration persists when it's inactive. After a video mode switch, all layers revert to their default configuration.
The images on all the active layers of a display are combined, using alpha blending, chroma keying, or both, to produce the final image on the display.
Surfaces
The image on a layer is fetched from one or more offscreen contexts, also called surfaces. The number of surfaces needed by a layer is determined by the layer format. For example, a layer whose format is Pg_LAYER_FORMAT_ARGB888 requires one surface, while a layer whose format is Pg_LAYER_FORMAT_YUV420 requires three surfaces for a complete image. The format of a layer is set using PgSetLayerArg().
Viewports

Source and destination viewports.
The "source viewport" defines a rectangular window into the surface data. This window is used to extract a portion of the surface data for display by the layer.
The "destination viewport" defines a rectangular window on the display. This window defines where the layer will display its image.
Scrolling and scaling, if supported by the layer, can be implemented by adjusting the source and destination viewports. To scroll or pan an image, move the position of the source viewport. To scale an image, increase or decrease the size of the destination viewport.
Layer API
The layer API includes:
- PgGetLayerCaps()
- Query the capabilities of a layer
- PgCreateLayerSurface()
- Create an offscreen context displayable by a layer
- PgSetLayerSurface()
- Display an offscreen context on a layer
- PgSetLayerArg()
- Configure a layer parameter
- PgLockLayer()
- Lock a layer for exclusive use by an application
- PgUnlockLayer()
- Release a locked layer
- PgLayerCaps_t
- Data structure that describes the capabilities for a layer
![]() |
The layer API is incompatible with the existing video overlay API (PgCreateVideoChannel(), PgConfigScalerChannel(), PgNextVideoFrame(), and so on). Don't run two applications that use different APIs simultaneously. |
Note the following:
- Photon cannot render in offscreen contexts that are in a different format from the current video mode. Thus, it may not be possible to use Photon draw functions in an offscreen context allocated by PgCreateLayerSurface(). Instead, the application should use PdGetOffscreenContextPtr() to get a pointer to the video memory and write data directly into the video memory.
- If an application changes the main display's surface by calling PgSetLayerSurface(), Photon will continue to render on the old surface. The application should keep a pointer to the old main display surface and restore it when it releases the layer. See the code below for an example.
Using layers
To use layers, you typically do the following:
- Call PgGetLayerCaps() with successively incremented indexes to enumerate your hardware capabilities (unless you already know them). If PgGetLayerCaps() fails for all values, the driver doesn't support layers.
- If you want to prevent other applications from accessing a layer, call PgLockLayer().
- Allocate surfaces for the layer, and offscreen contexts for the surfaces, by calling PgCreateLayerSurface().
- Call PgSetLayerArg() with an arg argument of Pg_LAYER_ARG_LIST_BEGIN.
- Call PgSetLayerArg() to set other arguments as required.
- Call PgSetLayerSurface() to display a surface's offscreen context on a layer.
- Call PgSetLayerArg() to set other arguments as required. You can specify Pg_LAYER_ARG_ACTIVE to display the layer.
- Call PgSetLayerArg() with an arg argument of Pg_LAYER_ARG_LIST_END.
- If the layer format is one of the RGB or PAL8 formats, set the current draw context to render into a surface's associated draw context(s), and then use the Pg* functions to draw into the offscreen context.
- If the layer format is YUV, and so on, you typically dump data directly to the buffer (like the video channel buffers).
- If you locked a layer, you must use PgUnlockLayer() to unlock it before your application exits.
See the code below for an example of using the layers API.
Example
#include <errno.h>
#include <stdio.h>
#include <Ph.h>
int
FindFormatIndex(int layer, unsigned int format)
{
PgLayerCaps_t caps;
int format_idx = 0;
while (PgGetLayerCaps(layer, format_idx, &caps) != -1) {
if (caps.format == format)
return format_idx;
format_idx++;
}
return -1;
}
int
main(int argc, char **argv)
{
/*
* For best results, these values should match your video mode.
*/
#define LAYER_FORMAT Pg_LAYER_FORMAT_ARGB8888
#define SURFACE_WIDTH 1024
#define SURFACE_HEIGHT 768
struct _Ph_ctrl *ph;
PgLayerCaps_t caps;
PdOffscreenContext_t *surf;
PdOffscreenContext_t *scr = NULL;
PhDrawContext_t *olddc;
PhRid_t driver_rid = -1;
int layer_idx = -1;
int format_idx = -1;
int active = 1;
int i;
PhArea_t sarea, darea;
/*
* Arguments:
* -d <driver region>
* -l <layer index>
*/
while ((i = getopt(argc, argv, "d:l:")) != -1) {
switch(i) {
case 'd': /* driver region */
driver_rid = atol(optarg);
break;
case 'l': /* layer index */
layer_idx = atoi(optarg);
break;
default:
break;
}
}
if (layer_idx == -1) {
printf("Specify layer index.\n");
exit(-1);
}
if (driver_rid == -1) {
printf("Specify graphics driver region.\n");
exit(-1);
}
ph = PhAttach(NULL, NULL);
if (ph == NULL) {
perror("PhAttach");
exit(-1);
}
if (-1 == PdSetTargetDevice(PhDCGetCurrent(), driver_rid)) {
perror("PdSetTargetDevice");
exit(-1);
}
/* Check if the layer supports the required format */
format_idx = FindFormatIndex(layer_idx, LAYER_FORMAT);
if (format_idx == -1) {
printf("Layer doesn't support format\n");
exit(-1);
}
/* Get the layer capabilities */
PgGetLayerCaps(layer_idx, format_idx, &caps);
if (caps.caps & Pg_LAYER_CAP_MAIN_DISPLAY) {
/* Save a reference to the current display surface */
scr = PdCreateOffscreenContext(0, 0, 0,
Pg_OSC_MAIN_DISPLAY);
}
/* Allocate a surface for the layer */
surf = PgCreateLayerSurface(layer_idx, 0, format_idx,
SURFACE_WIDTH, SURFACE_HEIGHT,
Pg_OSC_MEM_PAGE_ALIGN);
if (surf == NULL)
exit(-1);
/* Draw some stuff on the surface */
olddc = PhDCSetCurrent(surf);
PgSetFillColor(Pg_BLACK);
PgDrawIRect(0, 0, SURFACE_WIDTH-1, SURFACE_HEIGHT-1,
Pg_DRAW_FILL);
PgSetFillColor(Pg_YELLOW);
PgDrawIRect(0, 0, 100, 100, Pg_DRAW_FILL);
PgSetFillColor(PgRGB(255,180, 0));
PgDrawIRect(70, 80, 600, 500, Pg_DRAW_FILL);
PhDCSetCurrent(olddc);
/* Lock the layer */
if (-1 == PgLockLayer(layer_idx))
exit(-1);
/* Start configuring arguments */
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_LIST_BEGIN, 0, 0);
/* Select the layer format */
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_FORMAT_INDEX,
&format_idx, sizeof(int));
/* This changes the current display surface */
PgSetLayerSurface(layer_idx, 0, surf);
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_ACTIVE,
&active, sizeof(int));
/* Configure other arguments ... */
if (!(caps.caps & Pg_LAYER_CAP_MAIN_DISPLAY)) {
sarea.pos.x = 0; sarea.pos.y = 0;
sarea.size.w = SURFACE_WIDTH;
sarea.size.h = SURFACE_HEIGHT;
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_SRC_VIEWPORT, &sarea,
sizeof(sarea));
darea.pos.x =0; darea.pos.y =0;
darea.size.w =SURFACE_WIDTH/2 ;
darea.size.h =SURFACE_HEIGHT/2 ;
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_DST_VIEWPORT, &darea,
sizeof(darea));
}
/* End configuration */
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_LIST_END, 0, 0);
/* Application continues ... */
sleep(3);
/* Finished using layer; Restore the current display
surface */
active = 0;
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_LIST_BEGIN, 0, 0);
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_ACTIVE, &active,
sizeof(int));
PgSetLayerSurface(layer_idx, 0, scr);
PgSetLayerArg(layer_idx, Pg_LAYER_ARG_LIST_END, 0, 0);
PgUnlockLayer(layer_idx);
if (scr) PhDCRelease(scr);
PhDCRelease(surf);
PhDetach(ph);
exit(0);
}
![]() |
![]() |
![]() |
![]() |

![[Previous]](prev.gif)
![[Contents]](contents.gif)
![[Index]](keyword_index.gif)
![[Next]](next.gif)

