Graphics

From Amiga Development Wiki
Jump to: navigation, search

Working with bitmaps and rastports

Bitmaps are low level primitives which consists of the pixel data for a graphics image. Rastports are high level objects which encapsulate bitmaps and encapsulate drawing information used with the bitmap.

Setting drawing colors

To set drawing colors you can use the pen system or RGB colour values. RGB color values are supported only on high color or true color targets. RGB colour is set with SetRPAttrs() using RPTAG_FgColor and RPTAG_BgColor tags.

RGB drawing mode

SetRPAttrs(rp,
   RPTAG_PenMode, FALSE, /* Switch pen mode off */
   RPTAG_FgColor, 0xffffff, /* Foreground color to white */
   RPTAG_BgColor, 0x000000, /* Background color to black */
   TAG_DONE);

You need to switch pen mode off only once if you are only using RGB color triplets. Setting a rendering pen with SetAPen(), SetBPen() and SetOPen() switch the pen mode on automatically and the original rendering pen is restored.

Pen drawing mode

Using pen mode is more complicated, but it is always only way to draw primitives to palette based rastports. You should always obtain the pen using your preferred color. When terminating your rendering target you have to release the drawing pen.

LONG pen = ObtainBestPen(colormap, 0xffffffff, 0xffffffff, 0xffffffff, TAG_DONE);
 
if (pen != -1)
{
   SetAPen(rp, pen);
 
   /* drawing operations */
 
   /* call ReleasePen() only when your render target is disposed */
   ReleasePen(colormap, pen);
}

Using drawing pens is only for advanced developers.

Drawing operations

Text

Here is an example how to draw test using RGB colour values:

void DrawText(struct RastPort *rp)
{
   SetRPAttrs(rp, RPTAG_PenMode, FALSE, RPTAG_FgColor, 0xFFFFFF, TAG_DONE); /* Change foreground colour to black */
   Move(rp, 20, 25); /* Start rendering to coordinates x=20, y=25 */
   Text(rp, sizeof("Hello world!"), "Hello world!");
}

Drawing lines

To draw lines you set the starting point with Move() and then draw line to destination point with Draw().

Move(rp, x, y);
Draw(rp, x+10, y);
Draw(rp, x+10, y+10);
Draw(rp, x, y+10);
Draw(rp, x, y);

Accessing single pixels

On palette based targets use ReadPixel() and WritePixel() to read/write pen values:

LONG ReadPixel(struct RastPort *rp, LONG x, LONG y);
VOID WritePixel(struct RastPort *rp, LONG x, LONG y);

On high color and true color targets ReadRGBPixel() and WriteRGBPixel() work with direct RGB values:

LONG ReadRGBPixel(struct RastPort *rp, LONG x, LONG y);
VOID WriteRGBPixel(struct RastPort *rp, LONG x, LONG y, ULONG rgb);

Image copy operations

Copy bitmap to bitmap

BltBitMap() is used to copy a rectangle from a source bitmap to another bitmap.

BltBitMap(struct BitMap *src, UWORD src_x, UWORD src_y, struct BitMap *dst, UWORD dst_x, UWORD dst_y, UWORD width, UWORD height, UBYTE minterm, UBYTE mask, APTR tempbuf);

The minterm should be always 0xc0. The mask parameter is used to select which bitmap planes are copied. This may speed up copy operation on OCS/AGA Amigas, but most of time this should be set to 0xff. When copies overlap you must provide large enough temporary buffer (tempbuf) to store one scan line from the bitmap.

BltBitMapRastPort() is similar, but the destination is rastport.

BltBitMapRastPort(struct BitMap *src, UWORD src_x, UWORD src_y, struct BitMap *dst, UWORD dst_x, UWORD dst_y, UWORD width, UWORD height, UBYTE minterm);

ClipBlit() is another form where both source and destinations are rastports.

ClipBlit(struct BitMap *src, UWORD src_x, UWORD src_y, struct RastPort *dst, UWORD dst_x, UWORD dst_y, UWORD width, UWORD height, UBYTE minterm);

When working with true color bitmaps it is possible use alpha blitting routines.

BltBitMapAlpha(struct BitMap *src, UWORD src_x, UWORD src_y, struct BitMap *dst, UWORD dst_x, UWORD dst_y, UWORD width, UWORD height, struct TagItem *taglist);
BltBitMapRastPortAlpha(struct BitMap *src, UWORD src_x, UWORD src_y, struct RastPort *dst, UWORD dst_x, UWORD dst_y, UWORD width, UWORD height, struct TagItem *taglist);
Tag Description
BLTBMA_MIXLEVEL From 0x00000000 (0%) to 0xFFFFFFFF (100%).
BLTBMA_USESOURCEALPHA TRUE/FALSE, use alpha channel from a source bitmap.
BLTBMA_GLOBALALPHA An alias for BLTBMA_MIXLEVEL.
BLTBMA_DESTALPHAVALUE TRUE/FALSE, alter alpha channel in a destination bitmap.

Copy from bitmap to memory

To read pixel array from the bitmap you use ReadPixelArray():

ReadPixelArray(APTR dest, UWORD dest_x, UWORD dest_y, UWORD modulo, struct RastPort *rp, UWORD x, UWORD y, UWORD width, UWORD height, UBYTE format)
Parameter Description
dest Destination memory buffer area.
dest_x Destination x coordinate.
dest_y Destination y coordinate.
modulo Modulo to the next row in the destination buffer.
rp Source rastport to extract pixels from.
x Source x coordinate.
y Source y coordinate.
width Source width in pixels.
height Source height in pixels.
format Destination pixel format.

Copy from memory to bitmap

To write an array of pixels to the bitmap you use WritePixelArray() or WritePixelArrayAlpha():

WritePixelArray(APTR src, UWORD src_x, UWORD src_y, UWORD modulo, struct RastPort *rp, UWORD x, UWORD y, UWORD width, UWORD height, UBYTE format) 
WritePixelArrayAlpha(APTR dest, UWORD dest_x, UWORD dest_y, UWORD modulo, struct RastPort *rp, UWORD x, UWORD y, UWORD width, UWORD height, ULONG global_alpha)
Parameter Description
src Source memory buffer area.
src_x Source x coordinate.
src_y Source y coordinate.
modulo Modulo to the next row in the source buffer.
rp Destination rastport to write pixels to.
x Destination x coordinate.
y Destination y coordinate.
width Destination width in pixels.
height Destination height in pixels.
global_alpha Composite a source image onto a destination with this alpha (opacity) level. 0xffffffff is direct opaque blit, 0x7fffffff is 50% opacity.

Image scaling operations

Three different calls are available for image scaling.

BitMapScale(struct BitScaleArgs *args);
ScalePixelArray(APTR src, UWORD src_w, UWORD src_h, UWORD moduloe, struct RastPort *, UWORD dest_x, UWORD dest_y, UWORD dest_w, UWORD dest_h, UBYTE pixelformat);
ScalePixelArrayAlpha(APTR src, UWORD src_w, UWORD src_h, UWORD moduloe, struct RastPort *, UWORD dest_x, UWORD dest_y, UWORD dest_w, UWORD dest_h, ULONG global_alpha);

Due to its complexity BitMapScale() is not covered here.

Parameter Description
src Source memory buffer area.
src_w Source width in pixels.
src_h Source height in pixels.
modulo Modulo to the next row in the source buffer.
rp Destination rastport to write pixels to.
x Destination x coordinate.
y Destination y coordinate.
width Destination width in pixels.
height Destination height in pixels.
pixelformat Source pixel format (ScalePixelArray() only).
global_alpha Composite a source image onto a destination with this alpha (opacity) level. 0xffffffff is direct opaque blit, 0x7fffffff is 50% opacity (ScalePixelArrayAlpha() only).

ScalePixelArrayAlpha() takes an ARGB source buffer that is scaled to a destination rastport.

Create off-screen bitmaps and rastports

Sometimes it is necessary to render off-screen. This section describes how to create off-screen rastports on Amiga.

Create off-screen bitmap

Although bitmaps can be created manually (allocate some memory and call InitBitMap()) we strongly encourage to use AllocBitMap(). This ensure maximum compatibility and performance on different Amiga configurations.

struct BitMap *AllocBitMap(UWORD width, UWORD height, UWORD depth, UWORD flags, struct BitMap *reference_bitmap);

When parameter reference_bitmap is not null it will be used as a reference for newly allocated bitmap to determine the best possible pixel format. If reference bitmap is screen bitmap or bitmap with palette information this palette will be inherited to new bitmap.

Flag Description
BMF_CLEAR Clear allocated bitmap with zeros.
BMF_DISPLAYABLE Allocate bitmap from video memory.
BMF_MINPLANES When set you are guaranteed to get the best possible bitmap according to your request. When absent you get planar bitmaps only.
BMF_SPECIALFMT SHIFT_PIXFMT(pixel_format).

Create off-screen rastport without clipping

To create simple off-screen rastport you only allocate some memory for rastport where you install your new pointer. These rastports are unsafe for rendering. Any drawing operation that is rendered outside the bitmap trash random memory.
#include <proto/exec.h>
#include <proto/graphics.h>
 
struct RastPort *CreateRastPort(int width, int height, struct BitMap *friend_bitmap, BOOL displayable)
{
   struct RastPort *rp = AllocMem(sizeof(*rp), MEMF_ANY);
 
   if (rp)
   {
      int depth = GetBitMapAttr(friend_bitmap, BMA_DEPTH);
      struct BitMap *bm = AllocBitMap(width, height, depth, BMF_MINPLANES | displayable ? BMF_DISPLAYABLE : 0);
 
      if (bm)
      {
         InitRastPort(rp);
         rp->BitMap = bm;
         return rp;
      }
 
      FreeMem(rp, sizeof(*rp);
   }
 
   return NULL;
}
 
void FreeRastPort(struct RastPort *rp)
{
   FreeBitMap(rp->BitMap);
   FreeMem(rp, sizeof(*rp));
}

Create off-screen rastport with clipping

To create safe rastports where rendering outside bitmap is not allowed some more advanced techniques are needed. Those rastports must be created using layers.library routines which allocate and initialize clipping structures for you.
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/layers.h>
 
struct RastPortData
{
   struct RastPort *rp;
   struct Layer_Info *li;
   struct Layer *l;
};
 
struct RastPortData *CreateRastPort(int width, int height, struct BitMap *friend_bitmap, BOOL displayable)
{
   struct RastPortData *rpd = AllocMem(sizeof(*rpd), MEMF_ANY);
 
   if (rpd)
   {
      int depth = GetBitMapAttr(friend_bitmap, BMA_DEPTH);
      struct BitMap *bm = AllocBitMap(width, height, depth, BMF_MINPLANES | displayable ? BMF_DISPLAYABLE : 0);
 
      if (bm)
      {
         rpd->li = NewLayerInfo();
 
         if (rpd->li)
         {
            rpd->l = CreateUpfrontLayer(rpd->li, bm, 0, 0 width - 1, height - 1, 0, NULL);
 
            if (rpd->l)
            {
               rpd->rp = rpd->l->rp;
               return rpd;
            }
 
            DisposeLayerInfo(rpd->li);
         }
 
         FreeBitMap(bm);
      }
 
      FreeMem(rpd, sizeof(*rpd);
   }
 
   return NULL;
}
 
void DeleteRastPort(struct RastPortData *rpd)
{
   FreeBitMap(rpd->rp->BitMap);
   DeleteLayer (0, rpd->l);
   DisposeLayerInfo(rpd->li);
   FreeMem(rpd, sizeof(*rpd));
}

Planar to chunky conversion

Old Amiga graphics are stored in planar mode where each individual pixel is spread across bitplanes to make one pixel. This is not only impractical when you want to manipulate graphics using the CPU but also very inefficient for modern display devices. To Kickstart 2.0, Commodore developers added ReadPixelArray8() for your help when you must convert planar graphics to 8-bit color look-up table (CLUT).

#include <proto/graphics.h>
 
BOOL PlanarToChunky(PLANEPTR *planes, int x, int y, int width, int height, int depth, UBYTE *chunkybuf)
{
   BOOL success = FALSE;
   struct BitMap *temp_bm = AllocBitMap(width, 1, depth, BMF_STANDARD, NULL);
 
   if (temp_bm)
   {
      struct RastPort src_rp, temp_rp;
      struct BitMap src_bitmap;
      int i;
 
      InitBitMap(&src_bitmap, depth, width, height);
 
      for (i = 0; i < depth; i++)
         src_bitmap.Planes[i] = planes[i];
 
      for (i = depth; i < 8; i++)
         src_bitmap.Planes[i] = NULL;
 
      InitRastPort(&src_rp);
      InitRastPort(&temp_rp);
 
      src_rp.BitMap = &src_bm;
      temp_rp.BitMap = temp_bm;
 
      ReadPixelArray8(&src_rp, x, y, x + width - 1, y + height - 1, chunkybuf, &temp_rp);
      FreeBitMap(temp_bm);
 
      success = TRUE;
   }
 
   return success;
}

The allocated chunky buffer must have space for at least (((width + 15) & ~15) * height) bytes.