Copyright (c) 2012-2016 Hyperion Entertainment and contributors.

Programming AmigaOS 4: Drawing Graphics

From AmigaOS Documentation Wiki
Jump to: navigation, search

This article was adapted from Amiga Future magazine's series on developing for AmigaOS....

In this edition we would like to take a took at three libraries, which work together to let us see graphics and text on screen, in one go. Especially in the area of fonts there are now many new possibilities.

We would like to start however with the graphics.library, which offers drawing functions on bitmap layers.

Graphics library

Basically speaking the layers.library deals with the organisation of data, and intuition, on the other hand, takes care of the interaction with the user. In between is the graphics.library and it deals with the display of data on screen. AmigaOS 4.0 includes the Picasso driver system (albeit in PPC native form). AmigaOS 4.1 introduces a new graphics.library API which replaces the Picasso driver system. New options have resulted in the compositing that was introduced in AmigaOS 4.1. This allows the "real" transparent areas in windows and window decorations.

Layers in background

Normal applications do not need to access the functions of layers.library directly, that perform their task in the "background". Many of the higher OS functions of Intuition and Datatypes however do. They deal with the organisation of the screen area and for an optimal refresh, in case only the needed parts for drawing something are released. On the other side, it is also possible to define clip-ranges yourself and all draw functions outside that area are ignored. This makes it, for instance, possible to prevent text from flowing over the right hand side of the window (or any other area).

New are the functions ILayer->ShowLayer() and ILayers->HideLayer(), which have already demonstrated their practicality in the last part of our workshop when used with IIntuition->ShowWindow() and IIntuition->HideWindow(). The fill of a background areas is easier to automate as well. Those interested are referred to the functions ILayers->CreateBackFillHook() and ILayers->DeleteBackFillHook(), as well as ILayers->SetBackFillHookAttrs() and Ilayers->GetBackFillHookAttrs().

Fonts

It all gets much more interesting however when we get to fonts, which are also displayed on screen by graphics.library (With the IGraphics->Text() function). Just this once we will deal with the Workbench. The old IntelliFont Installation Program has been sent for retirement and replaced by FontManager. This program allows installing new fonts for the Amiga. This is necessary because of certain information posted in the .font files (for bitmap fonts) or for example .otag files (scaled fonts). The former well known Agfa Intelli-fonts are still supported by FontManager as well, even though there are hardly fonts available in this format. It gets more interesting with the TrueType fonts from the PC scene and the from the printer realm originating PostScript (Type1) fonts. This is not enough for FontManager. Other, rather unknown font formats, are supported as well, like for example OpenType fonts.

As soon as the fonts are initialised on the system with the Typemanager it doesn't make any difference from the programs point of view what the type of the font actually is. If it is set in the configuration they are even displayed anti-aliased, this means with smoothed sides. The setting for this is found in the "Font-Prefs" settings program.

AF107 prefs-font eng.png

In case more information is needed about individual characters, all important functions for this are available from the bullet.library are now directly available in the diskfont.library. The font specific libraries (bullet, type1 or ttf) are not to be used anymore !

Encoding Warning

AmigaOS 4 now supports different encoding tables as well. The ISO 8859-1 (latin 1) table was valid for Western Europe for a long time for example. It has been replaced by the 15x tables (Latin 9) with the introduction of the Euro. For Central Europe -2 is used, for Greece -7 etc.

While the first 128 characters of the ASCII table are uniform worldwide, the upper 128 bytes (value 128-255) can be assigned differently This is the range where country specific special characters like "?? can be found. With AmigaOS 4 it is possible to set which encoding should be used in the Locale preferences. When a font is opened the associated encoding is automatically looked up. If no suitable font is found it will look for one without encoding information. If both of those should fail, opening the font fails. Fonts with the same name but different encoding are not used under any circumstances. This all happens automatically again. Programming wise there is nothing extra to code for.

AF107 prefs-locale eng.png

This does however have one drawback, because as a programmer you can not predict anymore what character hides within ASCII-Code 168 for example. Normally you would find the copyright symbol "?" there. This often used character should therefore be replaced by "(c)".

The encoding is of course valid as well for the catalog file. Here it is just a case of writing the right "codeset" Tag in the CatalogTranslation-File (.ct). The rest is again taken care of by the operating system.

There is an ongoing effort to switch to Unicode and use UTF-8 but it will be a while until those features are implemented fully.

Direct layout

After all this theory now a practical side as well. As long as the text is simply displayed on the screen, IGraphics->Move() and IGraphics->Text will suffice. As the case may be also an IDiskfont->OpenDiskFont() and IGraphics->SetFont() for opening and using the font. All well-known ones function. Should however the single characters be rotated, clinched or use a different character spacing, things get a little more interesting. All this can be simply realised with the new functions of diskfont.library. One requirement, however, is that it is a scalable font. It will not function with the "old" bitmap fonts. Before we can get going, we first have to open the FontEngine. The available Engines are presented below.

  struct EGlyphEngine EEngine;
 
  if((EEngine.ege_BulletBase = IExec->OpenLibrary(<EngineName>,0)))
  {
    if((EEngine.ege_IBullet = (struct BulletIFace *) IExec->GetInterface(EEngine.ege_BulletBase,"main",1,NULL)))
    {
      if((IDiskfont->EOpenEngine(&EEngine)))
      {

And at the end of the program we of course have to close the FontEngine again.

        IDiskfont->ECloseEngine(&EEngine);
      }
      IExec->DropInterface(EEngine.ege_IBullet);
    }
    IExec->CloseLibrary(EEngine->ege_BulletBase);
  }

The different actions and the desired characters are set using IDiskfont->ESetInfo(). Everything is OK when the returncode is 0.

  if(!(error = IDiskfont->ESetInfo(EEngine,
                   OT_PointHeight, 20,   /* Zeichenhˆhe in Pixel */
                   OT_GlyphCode,   '@',  /* Ascii-Code des Zeichen */
                   TAG_DONE)))
  {

This completes the preparation of the data and it can also be read using IDiskfont->EObtainInfo(). The data can be transferred to the screen as well with IGraphics->BliBitMapXXX.

  struct GlyphMap glyph;
  if(!(error = IDiskfont->EObtainInfo(EEngine,
                                      OT_GlyphMap, &glyph,
                                      TAG_DONE)))
  {
    IGraphics->BlitBitMapRastPort(glyph.glm_BitMap);
    IDoskfont->EReleaseInfo(EEngine,OT_GlyphMap,&glyph,TAG_DONE);
  }

As this short excerpt already shows the BitMap data of the character is available in the GlyphMap structure and can be directly blitted to the window.. Using IDiskFont->EReleaseInfo() the loaded resources are released at the end. The complete source code with all dependencies can be found under "FontViewer.c". This also uses ReAction-gadgets which we will cover in a future edition.

/* Michael Christoph
 * FontViewer.c
 *
 * gcc FontViewer.c -o FontViewer -lauto
 */
 
/************************** PROGRAMM-PARAMETER ********************************/
 
#define PROGNAME "FontViewer"
#define VERSIONR "3.00"
#define PROGDATE "21.07.2013"
#define COPYRIGT "Copyright (c) Jul.2013"
#define AUTORNAM "by Meicky-Soft"
 
const char *version = "\0$VER: " PROGNAME " " VERSIONR " (" PROGDATE ") - " COPYRIGT " " AUTORNAM "\n";
 
/******************************* INCLUDES *************************************/
 
#include <exec/types.h>
#include <exec/alerts.h>
#include <exec/io.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <dos/dos.h>
#define RETURN_BREAK 8    /* Ergänzung zu den Defines in dos.h */
#include <dos/dostags.h>
#include <diskfont/diskfonttag.h>
#include <diskfont/diskfont.h>
#include <diskfont/glyph.h>
#include <diskfont/oterrors.h>
#include <devices/inputevent.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <graphics/gfx.h>
#include <graphics/regions.h>
#include <classes/window.h>
#include <reaction/reaction_macros.h>
#include <utility/tagitem.h>
 
#include <images/label.h>
#include <gadgets/layout.h>
#include <gadgets/space.h>
#include <gadgets/string.h>
#include <gadgets/integer.h>
#include <gadgets/getfile.h>
#include <gadgets/space.h>
#include <gadgets/scroller.h>
#include <gadgets/slider.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/diskfont.h>
 
#include <proto/window.h>
#include <proto/layout.h>
#include <proto/label.h>
#include <proto/string.h>
#include <proto/integer.h>
#include <proto/getfile.h>
#include <proto/space.h>
#include <proto/scroller.h>
#include <proto/slider.h>
#include <proto/integer.h>
 
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <math.h>
 
/******************************* DEFINES **************************************/
 
enum
{
  GAD_ID_Mainlayout,
 
  GAD_ID_FontFileLabel,
  GAD_ID_FontFileAsl,
  GAD_ID_FontDegreeLabel,
  GAD_ID_FontDegree,
  GAD_ID_FontSizeLabel,
  GAD_ID_FontSize,
  GAD_ID_CharSelectLabel,
  GAD_ID_CharSelect,
  GAD_ID_CharView,
  GAD_ID_MAX
};
 
#define BM_WIDTH   320   /* TempBitmap Breite */
#define BM_HEIGHT  200   /* TemoBitmap Höhe */
 
#define MIN(arg1,arg2) (arg1 < arg2 ? arg1 : arg2)
#define MAX(arg1,arg2) (arg1 > arg2 ? arg1 : arg2)
 
/***************************** PROTOTYPEN *************************************/
 
void RenderHookFunc(struct Hook *hook, Object *obj, struct gpRender *gpr);
 
/************************ VARIABLEN DEKLARATIONEN *****************************/
 
Object                *gb_WindowObj;
struct Window         *gb_Win;
struct Gadget         *gb_Gadgets[GAD_ID_MAX];
struct FileRequester  *gb_FileReq;
struct Hook            gb_IDCMPHook;
 
struct OutlineFont    *gb_OutlineFont;
struct EGlyphEngine   *gb_Engine;
struct IBox           *gb_DrawArea; 
struct TagItem        *gb_FontTags;
BPTR                   gb_FontFH;
PLANEPTR               gb_TempBitmap;
UWORD                  gb_GlyphBMModulo;
UWORD                  gb_GlyphRenderW;
UWORD                  gb_GlyphRenderH;
ULONG                  gb_CharSelect = 'A';
STRPTR                 gb_FontFile   = "FONTS:DejaVu Sans Bold.font";
ULONG                  gb_FontDegree = 0;
ULONG                  gb_FontSize   = 100;
STRPTR                 gb_EngineName = NULL;
ULONG                  gb_Xdpi       = 72;
ULONG                  gb_Ydpi       = 72;
 
/******************************************************************************/
 
void PrintRenderError(LONG result)
{
  STRPTR str = NULL;
 
  switch(result)
  {
    case OTERR_Success:        str = NULL; break;
    case OTERR_BadTag:         str = "BadTag"; break;
    case OTERR_UnknownTag:     str = "UnknownTag"; break;
    case OTERR_BadData:        str = "BadData"; break;
    case OTERR_NoMemory:       str = "NoMemory"; break;
    case OTERR_NoFace:         str = "NoFace"; break;
    case OTERR_BadFace:        str = "BadFace"; break;
    case OTERR_NoGlyph:        str = "NoGlyph"; break;
    case OTERR_BadGlyph:       str = "BadGlyph"; break;
    case OTERR_NoShear:        str = "NoShear"; break;
    case OTERR_NoRotate:       str = "NoRotate"; break;
    case OTERR_TooSmall:       str = "TooSmall"; break;
    case OTERR_UnknownGlyph:   str = "UnknownGlyph"; break;
    default: /*OTERR_Failure*/ str = "Failure"; break;
  }
 
  if(str) IDOS->Printf("ERROR %ld by font rendering: %s (for char %lc)\n",result,str,gb_CharSelect);
}
 
/******************************************************************************/
 
BOOL OpenFontEngine()
{
  if((gb_OutlineFont = IDiskfont->OpenOutlineFont(gb_FontFile,NULL,OFF_OPEN)))
  {
    gb_Engine = &gb_OutlineFont->olf_EEngine;
 
    return( TRUE );
  }
  return( FALSE );
}
 
/******************************************************************************/
 
void CloseFontEngine()
{
  if(gb_Engine) IDiskfont->CloseOutlineFont(gb_OutlineFont,NULL);
  gb_OutlineFont = NULL;
  gb_Engine = NULL;
 
  /* die Engine ist zu, auch die TempBitmap leeren */
  memset(gb_TempBitmap,0,BM_WIDTH*BM_HEIGHT/8);
}
 
/******************************************************************************/
 
void RenderGlyph()
{
  /* sicherstellen, daß nur bei gültiger Engine eine Renderanfrage erfolgt */
  if(!gb_Engine) return;
 
  struct GlyphMap *gm;
  LONG re;
 
  /* den alten Buffer-Inhalt löschen */
  memset(gb_TempBitmap,0,BM_WIDTH*BM_HEIGHT/8);
 
  /* sinus/cosinus Winkel berechnen */
  double sinval = sin(6.28318534 / 360 * (360 - gb_FontDegree));
  double cosval = cos(6.28318534 / 360 * (360 - gb_FontDegree));
 
  /* die Glypheinstellungen für die Fontengine setzen */
  if((re = IDiskfont->ESetInfo(gb_Engine,
                    OT_DeviceDPI,   (ULONG) (gb_Xdpi << 16 | gb_Ydpi),
                    OT_PointHeight, (ULONG) (gb_FontSize << 16),
                    OT_GlyphCode,   gb_CharSelect,
                    OT_RotateSin,   (LONG)(sinval * 0x10000),
                    OT_RotateCos,   (LONG)(cosval * 0x10000),
                    TAG_DONE)) == OTERR_Success)
  {
    /* und dann den gerenderten Glyph erfragen */
    if((re = IDiskfont->EObtainInfo(gb_Engine,OT_GlyphMap,&gm,TAG_DONE)) == OTERR_Success)
    {
      gb_GlyphBMModulo = gm->glm_BMModulo;
      gb_GlyphRenderW  = gm->glm_BMModulo * 8;
      gb_GlyphRenderH  = gm->glm_BMRows;
 
      /* die GlyphMap-Daten in's Chip-Ram kopieren zum späteren Blitten */
      IExec->CopyMem(gm->glm_BitMap,
                     gb_TempBitmap,
                     gm->glm_BMModulo * gm->glm_BMRows);
 
      IDiskfont->EReleaseInfo(gb_Engine, OT_GlyphMap, gm, TAG_DONE);
    }
    else PrintRenderError(re);
  }
  else PrintRenderError(re);
}
 
/******************************************************************************/
/******************************************************************************/
 
BOOL CreateAllObjects()
{
  /* alle Gadgets und das Window erzeugen */
 
  /* RenderHook initialisieren */
  gb_IDCMPHook.h_Entry    = (ULONG (*)())RenderHookFunc;
  gb_IDCMPHook.h_SubEntry = NULL;
 
 
  gb_Gadgets[GAD_ID_FontFileLabel] = (struct Gadget *) IIntuition->NewObject(ILabel->LABEL_GetClass(),NULL,
      LABEL_Text,               "File:",
      TAG_DONE);
 
  gb_Gadgets[GAD_ID_FontFileAsl] = (struct Gadget *) IIntuition->NewObject(IGetFile->GETFILE_GetClass(),NULL,
      GA_ID,                     GAD_ID_FontFileAsl,
      GA_RelVerify,              TRUE,
      GETFILE_TitleText,         "Select font",
      GETFILE_DrawersOnly,       FALSE,        /* auch Dateien anzeigen */
      GETFILE_DoSaveMode,        FALSE,        /* normaler Auswahlmodus */
      GETFILE_RejectIcons,       TRUE,         /* keine Icons anzeigen */
      GETFILE_FullFile,          gb_FontFile,  /* Vorgabe der Fontdatei */
      GETFILE_Pattern,           "#?.font",    /* nur .font Dateien anzeigen */
      GETFILE_ReadOnly,          TRUE,         /* Anzeigefeld nicht änderbar */
      TAG_DONE);
 
  gb_Gadgets[GAD_ID_FontSizeLabel] = (struct Gadget *) IIntuition->NewObject(ILabel->LABEL_GetClass(),NULL,
      LABEL_Text,               "Size:",
      TAG_DONE);
 
  gb_Gadgets[GAD_ID_FontSize]  = (struct Gadget *) IIntuition->NewObject(IInteger->INTEGER_GetClass(),NULL,
      GA_ID,                    GAD_ID_FontSize,
      GA_Immediate,             TRUE,
      GA_RelVerify,             TRUE,
      INTEGER_Number,           gb_FontSize,   /* Starten mit Größe 20 */
      INTEGER_Minimum,          5,    /* Minimale Größe ist 5 Pixel */
      INTEGER_Maximum,          150,  /* Maximale Größe ist 150 Pixel */
      INTEGER_MaxChars,         3,    /* 3 Stellen eingeben lassen */
      INTEGER_Arrows,           TRUE, /* +/- Buttons neben das Feld zeichen */
      TAG_DONE);
 
  gb_Gadgets[GAD_ID_FontDegreeLabel] = (struct Gadget *) IIntuition->NewObject(ILabel->LABEL_GetClass(),NULL,
      LABEL_Text,               "Angle:",
      TAG_DONE);
 
  gb_Gadgets[GAD_ID_FontDegree]   = (struct Gadget *) IIntuition->NewObject(ISlider->SLIDER_GetClass(),NULL,
      GA_ID,                    GAD_ID_FontDegree,
      GA_Immediate,             TRUE,
      GA_RelVerify,             TRUE,
      SLIDER_Min,               0,    /* Bereiv von 0 Grad */
      SLIDER_Max,               359,  /* Bereich bis 359 Grad */
      SLIDER_Level,             gb_FontDegree,  /* starten bei 0 Grad */
      SLIDER_Orientation,       SLIDER_HORIZONTAL,
      SLIDER_Ticks,             5,    /* fünf Hilfslisien für 0/90/180/270/360 Grad */
      SLIDER_KnobDelta,         15,   /* bei Klick neben den Knob um 15 Grad Schritte verändern */
      TAG_DONE);
 
  gb_Gadgets[GAD_ID_CharSelectLabel] = (struct Gadget *) IIntuition->NewObject(ILabel->LABEL_GetClass(),NULL,
      LABEL_Text,               "Character:",
      TAG_DONE);
 
  gb_Gadgets[GAD_ID_CharSelect]   = (struct Gadget *) IIntuition->NewObject(IScroller->SCROLLER_GetClass(),NULL,
      GA_ID,                    GAD_ID_CharSelect,
      GA_Immediate,             TRUE,
      GA_RelVerify,             TRUE,
      SCROLLER_Top,             gb_CharSelect - 32,   /* mit dem Buchstaben 'A' beginnen */
      SCROLLER_Visible,         1,
      SCROLLER_Total,           126 - 32,   /* 32 - 126 (Standard-ASCII) */
      SCROLLER_Orientation,     SCROLLER_HORIZONTAL,
      SCROLLER_Arrows,          TRUE,  /* links/rechts Pfeile neben den Scroller zeichen */
      TAG_DONE);
 
  gb_Gadgets[GAD_ID_CharView] = (struct Gadget *) IIntuition->NewObject(ISpace->SPACE_GetClass(),NULL,
      SPACE_MinWidth,           50,
      SPACE_MinHeight,          50,
      SPACE_Transparent,        FALSE,
      SPACE_BevelStyle,         BVS_FIELD,  // BVS_BUTTON BVS_GROUP BVS_FIELD BVS_NONE BVS_DROPBOX
      SPACE_RenderHook,         &gb_IDCMPHook,
      TAG_DONE);
 
 
  if(
     gb_Gadgets[GAD_ID_FontFileLabel] &&
     gb_Gadgets[GAD_ID_FontFileAsl] &&
     gb_Gadgets[GAD_ID_FontDegreeLabel] &&
     gb_Gadgets[GAD_ID_FontDegree] &&
     gb_Gadgets[GAD_ID_FontSizeLabel] &&
     gb_Gadgets[GAD_ID_FontSize] &&
     gb_Gadgets[GAD_ID_CharSelectLabel] &&
     gb_Gadgets[GAD_ID_CharSelect] &&
     gb_Gadgets[GAD_ID_CharView]
    )
 
  {
    /* das Fenster-Objekt erzeugen */
    gb_WindowObj = (Object *) IIntuition->NewObject(IWindow->WINDOW_GetClass(),NULL,
      WA_IDCMP,             IDCMP_GADGETUP | IDCMP_GADGETDOWN |
                            IDCMP_CLOSEWINDOW | IDCMP_REFRESHWINDOW,
      WA_Top,               60,
      WA_Left,              20,
      WA_SizeGadget,        TRUE,
      WA_SizeBBottom,       TRUE,
      WA_DepthGadget,       TRUE,
      WA_DragBar,           TRUE,
      WA_CloseGadget,       TRUE,
      WA_Activate,          TRUE,
      WA_Title,             PROGNAME " V" VERSIONR,
      WA_InnerWidth,        400,
      WA_InnerHeight,       200,
 
      WINDOW_Position,      WPOS_CENTERSCREEN,
      WINDOW_IconifyGadget, FALSE,
      //WINDOW_AppPort,       gb_AppPort,
 
      WINDOW_ParentGroup,   gb_Gadgets[GAD_ID_Mainlayout] = (struct Gadget *) IIntuition->NewObject(ILayout->LAYOUT_GetClass(),NULL,
        LAYOUT_Orientation,  LAYOUT_ORIENT_VERT,
        LAYOUT_SpaceOuter,   TRUE,
        LAYOUT_SpaceInner,   TRUE,
 
        /* rechte obere Hälfte mit den Einstellungen */
        LAYOUT_AddChild, IIntuition->NewObject(ILayout->LAYOUT_GetClass(),NULL,
          LAYOUT_Orientation,   LAYOUT_ORIENT_VERT,
          LAYOUT_BevelStyle,    BVS_GROUP,
          LAYOUT_Label,         "Settings",
          LAYOUT_SpaceOuter,    TRUE,
 
          LAYOUT_AddChild,      gb_Gadgets[GAD_ID_FontFileAsl],
          CHILD_Label,          gb_Gadgets[GAD_ID_FontFileLabel],
          CHILD_WeightedHeight, 0, /* keine Höhenänderung */
 
          LAYOUT_AddChild,      gb_Gadgets[GAD_ID_FontDegree],
          CHILD_Label,          gb_Gadgets[GAD_ID_FontDegreeLabel],
          CHILD_WeightedHeight, 0, /* keine Höhenänderung */
 
          LAYOUT_AddChild,      gb_Gadgets[GAD_ID_FontSize],
          CHILD_Label,          gb_Gadgets[GAD_ID_FontSizeLabel],
          CHILD_WeightedHeight, 0, /* keine Höhenänderung */
 
          LAYOUT_AddChild,      gb_Gadgets[GAD_ID_CharSelect],
          CHILD_Label,          gb_Gadgets[GAD_ID_CharSelectLabel],
          CHILD_WeightedHeight, 0, /* keine Höhenänderung */
 
          TAG_DONE),
          CHILD_WeightedHeight, 50,
 
        LAYOUT_AddChild,      gb_Gadgets[GAD_ID_CharView],
        CHILD_WeightedHeight, 50,
 
        TAG_DONE),  /* die Parent-Gruppe des Fensters */
      TAG_DONE);  /* das Fenster-Object */
 
    if(gb_WindowObj)
    {
      /* alles hat funktioniert */
    }
    else IDOS->Printf("ERROR: can not create window.\n");
  }
  else IDOS->Printf("ERROR: can not create gadgets.\n");
 
  return( gb_WindowObj ? TRUE : FALSE );
}
 
/******************************************************************************/
 
void DeleteAllObjects()
{
  if(gb_WindowObj)
  {
    IIntuition->DisposeObject(gb_WindowObj);  /* gibt alle Gadgets frei */
    gb_WindowObj = NULL;
  }
}
 
/******************************************************************************/
 
BOOL InternelOpenWindow()
{
  if((gb_Win = RA_OpenWindow(gb_WindowObj)))
  {
  }
 
  return( gb_Win ? TRUE : FALSE );
}
 
/******************************************************************************/
 
void InternelCloseWindow()
{
  if(gb_Win) RA_CloseWindow(gb_WindowObj);
  gb_Win = NULL;
}
 
/******************************************************************************/
 
void InternelIconifyWindow()
{
  if(RA_Iconify(gb_WindowObj))
  {
    gb_Win = NULL;
  }
}
 
/******************************************************************************/
 
void RenderHookFunc(struct Hook *hook, Object *obj, struct gpRender *gpr)
{
  /*
  ** Hook-Funktion zum Refresehn der Fontvorschau
  */
 
 
  /* die Größe unseres Ausgabebereiches ermitteln */
  IIntuition->GetAttr(SPACE_AreaBox,(Object*)gb_Gadgets[GAD_ID_CharView],(ULONG *)&gb_DrawArea);
 
  /* nur wenn das Fenster sichtbar ist, dürfen wir hineinzeichnen */
  if(gb_Win)
  {
    /* den kompletten (alten) Inhalt löschen */
    IGraphics->SetAPen(gpr->gpr_RPort,0);
    IGraphics->RectFill(gpr->gpr_RPort,gb_DrawArea->Left,gb_DrawArea->Top,gb_DrawArea->Left+gb_DrawArea->Width-1,gb_DrawArea->Top+gb_DrawArea->Height-1);
    IGraphics->SetAPen(gpr->gpr_RPort,1);
 
    const WORD offx = (gb_DrawArea->Width  - gb_GlyphRenderW) / 2;
    const WORD offy = (gb_DrawArea->Height - gb_GlyphRenderH) / 2;
 
    /* den vorher gerenderten Glyph jetzt in den RastPort des Fenster übertragen */
    /* mittig ausgeben, wenn möglich, bei zu schmalem Fenster linksbündig */
    IGraphics->BltTemplate(gb_TempBitmap,
                0,
                gb_GlyphBMModulo,
                gpr->gpr_RPort,
                gb_DrawArea->Left + MAX(offx,0),
                gb_DrawArea->Top  + MAX(offy,0),
                MIN(gb_GlyphRenderW,gb_DrawArea->Width),
                MIN(gb_GlyphRenderH,gb_DrawArea->Height)
               );
  }
}
 
/******************************************************************************/
 
ULONG MsgLoop()
{
  ULONG res = RETURN_ERROR;
 
  /* Fenster darstellen, wurde bisher "nur" erzeugt */
  if(InternelOpenWindow())
  {
    ULONG winsig;
 
    IIntuition->GetAttr(WINDOW_SigMask,gb_WindowObj,&winsig);
 
    /*
    ** sofort die Font-Engine für den Vorgabe-Font öffnen
    ** dann das eingestellte Zeichen rendern
    ** und durch eine Refresh-Anforderung an das Gadget anzeigen
    */
    OpenFontEngine();
    RenderGlyph();
    IIntuition->RefreshGadgets(gb_Gadgets[GAD_ID_CharView],gb_Win,NULL);
 
    /*
    ** normaler Nachrichtenloop, bis das Fenster geschlossen wird,
    ** oder CTRL-C an das Programm gesendet wird.
    */
    while(res == RETURN_ERROR)
    {
      ULONG result, code;
      ULONG sigs = IExec->Wait(winsig | SIGBREAKF_CTRL_C);
 
      /* nächste Nachricht abholen */
      while((result = RA_HandleInput(gb_WindowObj,&code)) != WMHI_LASTMSG)
      {
        /* den Nachrichtenanteil ausmaskieren ! */
        switch(result & WMHI_CLASSMASK)
        {
          case WMHI_GADGETUP:
               switch(result & WMHI_GADGETMASK)
               {
                 case GAD_ID_FontFileAsl:
                      {
                        ULONG ret = IIntuition->IDoMethod((Object *)gb_Gadgets[GAD_ID_FontFileAsl],GFILE_REQUEST,gb_Win);
                        IIntuition->GetAttr(GETFILE_FullFile,(Object*)gb_Gadgets[GAD_ID_FontFileAsl],(ULONG *)&gb_FontFile);
                        IIntuition->RefreshGadgets(gb_Gadgets[GAD_ID_FontFileAsl],gb_Win,NULL);
                        CloseFontEngine();
                        OpenFontEngine();
                        RenderGlyph();
                        IIntuition->RefreshGadgets(gb_Gadgets[GAD_ID_CharView],gb_Win,NULL);
                      }
                      break;
 
                 case GAD_ID_FontDegree:
                      {
                        IIntuition->GetAttr(SLIDER_Level,(Object*)gb_Gadgets[GAD_ID_FontDegree],(ULONG *)&gb_FontDegree);
                        RenderGlyph();
                        IIntuition->RefreshGadgets(gb_Gadgets[GAD_ID_CharView],gb_Win,NULL);
                      }
                      break;
 
                 case GAD_ID_FontSize:
                      {
                        IIntuition->GetAttr(INTEGER_Number,(Object*)gb_Gadgets[GAD_ID_FontSize],(ULONG *)&gb_FontSize);
                        RenderGlyph();
                        IIntuition->RefreshGadgets(gb_Gadgets[GAD_ID_CharView],gb_Win,NULL);
                      }
                      break;
 
                 case GAD_ID_CharSelect:
                      {
                        IIntuition->GetAttr(SCROLLER_Top,(Object*)gb_Gadgets[GAD_ID_CharSelect],(ULONG *)&gb_CharSelect);
                        gb_CharSelect += 32;
                        RenderGlyph();
                        IIntuition->RefreshGadgets(gb_Gadgets[GAD_ID_CharView],gb_Win,NULL);
                      }
                      break;
               }
               break;
 
          case WMHI_CLOSEWINDOW:
               res = RETURN_OK;
               break;
        }
      }
 
      if(sigs & SIGBREAKF_CTRL_C)
        res = RETURN_BREAK;
    }
 
    /* Fenster schließen */
    InternelCloseWindow();
  }
  else IDOS->PutStr("ERROR: can not open window.\n");
 
  return( res );
}
 
/******************************************************************************/
 
ULONG runprogram()
{
  ULONG ret = RETURN_ERROR;
 
  if(CreateAllObjects())
  {
    /* temporäre Bitmap für den gerenderten Glyph zur späteren Anzeige */
    if((gb_TempBitmap = IGraphics->AllocRaster(BM_WIDTH,BM_HEIGHT)))
    {
      ret = MsgLoop();
 
      IGraphics->FreeRaster(gb_TempBitmap,BM_WIDTH,BM_HEIGHT);
    }
    else IDOS->PutStr("Error: out of memory.\n");
  }
 
  CloseFontEngine();
  DeleteAllObjects();
 
  return( ret );
}
 
/******************************************************************************/
 
int main(int argc, char *argv[])
{
  return( runprogram() );
}
AF107 FontViewer grab.png

New functions in the diskfont.library V50

Opening and closing a scalable font

struct OutlineFont *OpenOutlineFont(STRPTR fileName, struct List *list, ULONG flags);
VOID CloseOutlineFont(struct OutlineFont *font, struct List *list);

Opening and closing the font-engine

LONG EOpenEngine(struct EGlyphEngine *engine);
VOID ECloseEngine(struct EGlyphEngine *engine);

Obtains data from the font engine and releases it again.

ULONG EObtainInfoA(struct EGlyphEngine *engine, struct TagItem *taglist);
ULONG EReleaseInfoA(struct EGlyphEngine *engine, struct TagItem *taglist);

Setting of OT_xxx tags in the font engine

ULONG ESetInfoA(struct EGlyphEngine *engine, struct TagItem *taglist);

Writes one character set to floppy disk (as bitmap font)

LONG WriteDiskFontHeaderA(struct TextFont *font,STRPTR filename, struct TagItem *taglist);
LONG WriteFontContents(BPTR fh, STRPTR fontname, struct FontContentsHeader *header);

Obtaining and releasing of font attributes

struct TTextAttr *ObtainTTextAttr(struct TextFont *textFont);
VOID FreeTTextAttr(struct TTextAttr *tta);

New functions in graphics.library V51

Composing two bitmaps together:

 uint32 CompositeTags(uint32 operator, struct BitMap *Source,struct BitMap *Dest, ...);
 int32 BltBitMapTags(uint32 tag, ...)

Font engines

If you wanted to support different fonts in your own program in the past there were different libraries with their own installation programs for that in the past. AmigaOS 4 unifies them all in the diskfont.library.

Library Installation Program Supported Font Formats
bullet.library Intellifont CompuGraphic
type1.library T1Manager PostScript type 1
ttf.library TTFManager TrueType
ft2.library TypeManager AmigaOS 4: CG, PS, TT and others

Everything standardised

We can thank the "European Computer Manufacturer's Association" (ECMA) for worldwide standardisation of the character sets under ISO-8859 and thus giving AmigaOS 4 a chance to display the correct characters on the screen by loading the correct font for it.

ISO 8859-1 Western European languages (Latin-1, German, English, French, Danish, Spanish and others)
ISO 8859-2 Central and Eastern European languages (Latin-2, Croatian, Polish, Slovakian, Czech, Hungarian and others)
ISO 8859-3 South European languages (Latin-3, Turkish)
ISO 8859-4 Scandinavian/Baltic languages(Latin-4, Estonian, Latvian, Lithuanian)
ISO 8859-5 Latin/Cyrillic (Cyrillic script)
ISO 8859-6 Latin/Arabic (Arabic script)
ISO 8859-7 Latin/Greek (New-Greek script)
ISO 8859-8 Latin/Hebrew (Hebrew script)
ISO 8859-9 Latin/Turkish (Latin-5, modified)
ISO 8859-10 Lappish/Nordic/Eskimo languages (Latin-6)
ISO 8859-11 Latin/Thai (Thai Script)
ISO 8859-13 Baltic languages (Latin-7)
ISO 8859-14 Celtic languages (Latin-8)
ISO 8859-15 Western European languages (Latin-9 with Euro-symbol)
ISO 8859-16 Eastern European languages (Latin-10)

Authors

Written by Michael Christoph
Translation by Richard Mulder.
Copyright (c) 2013 Michael Christoph