Copyright (c) Hyperion Entertainment and contributors.
Programming AmigaOS 4: Transparent Windows
This article was adapted from Amiga Future magazine's series on developing for AmigaOS....
With AmigaOS 4.1 a new technology was introduced, which lets you display (partially) transparent windows thus showing the content behind the window. Additionally, with this technology you can create rounded window corners. We will take a closer look today at the programming characteristics behind these options.
In the first block we will deal with transparent windows, while in the second block we will see how you can create transparent areas within a window or how you can set the window based on the outline of an image.
Contents
Compositing
In order to be able to use compositing you need AmigaOS in the version 4.1 (so, SysBase->lib_Version >= 53). Moreover, your screen must have a colour depth of at least 16 Bit (so Highcolour or Truecolour). The user can also set in the GUI-Prefs whether transparency is allowed. 'Visual effects for layer' must be activated for it. You can certainly check that in the following way:
if(( dri = IIntuition->GetScreenDrawInfo(window->WScreen) )) { IIntuition->GetGUIAttrs(NULL,dri,GUIA_SpecialEffects,&specialfx,TAG_END); }
If the variable "specialfx" is set as TRUE, the user has activated the setting and the particular screen has at least 16 Bit colour depth.
Transparent windows
To make a window transparent, three new tags are sufficient. They must be passed as/when the window is opened to IIntuition->OpenWindowTags() or rather, IIntuition->NewObject(IWindow->WINDOW_GetClass(),...).
With the tag "WA_Opaqueness" you can determine how opaque or transparent the complete window should be. The possible values margin begins with 0 for completely transparent, which means, you cannot see anything from the contents or the window itself and the underlying screen content is fully displayed. There are also value margins up to 255 for completely opaque display, which corresponds to the standard display. In that case, the complete window content is shown and what is behind is not visible. A mean value of 128 would display one half of the window content and one half of the screen behind it. However, if you want to use this setting, you must additionally set the tag "WA_OverrideOpaqueness" to TRUE. Only then will the global settings from the GUI-Prefs program be ignored and a divergent transparency will be possible for the selected window.
With the third tag "WA_FadeTime" you can set up a delay time, which will be active when you open/close the window as well as when you change the transparent value (WA_Opaqueness). The value is a time span in microseconds (1,000,000 microseconds = 1 second). The standard is 0, which means, no delay. If you enter 1,000,000, it will take one second until the window is completely displayed when opened. Whatever, it's a nice gimmick.
You can also change all three tags retrospectively:
Intuition->SetWindowAttrs(window,WA_Opaqueness,opaqueness,TAG_END);
On the other hand, you can request the current tag values with
IIntuition->GetAttr(WA_Opaqueness,winobj,&opaqueness)
The so far covered details are included in the sample program "OpaquenessExample.c", where you can try them out.
/* Michael Christoph * OpaquenessExample.c * * gcc OpaquenessExample.c -o OpaquenessExample -l auto */ /******************************* INCLUDES *************************************/ #include <intuition/intuition.h> #include <intuition/gui.h> #include <intuition/icclass.h> #include <libraries/gadtools.h> #include <reaction/reaction.h> #include <reaction/reaction_macros.h> #include <classes/window.h> #include <gadgets/slider.h> #include <images/label.h> #include <proto/exec.h> #include <proto/dos.h> #include <proto/intuition.h> #include <proto/window.h> #include <proto/layout.h> #include <proto/slider.h> #include <proto/label.h> /******************************************************************************/ static const char *version USED = "\0$VER: OpaquenessExample 1.0 (06.01.2009) - (c) Jan.2009 by Meicky-Soft\n"; /******************************************************************************/ enum { OBJ_SLIDER_1, OBJ_SLIDER_2, OBJ_MAX }; #define OBJ(x) objects[x] #define GAD(x) (struct Gadget *)objects[x] /******************************************************************************/ int main() { struct RDArgs *rda; struct Window *window; struct DrawInfo *dri; Object *win; Object *objects[OBJ_MAX]; BOOL canfade = FALSE; uint32 specialfx = 0; uint32 fade_time = 50; uint32 opaqueness = 255; uint32 sigmask = 0; uint32 result = 0; uint16 code = 0; BOOL done = FALSE; enum { ARG_pubscreen, ARG_MAX }; ULONG args[ARG_MAX]={ 0 }; /* AmigaOS 4.1 is required */ if(SysBase->lib_Version >= 53) { /* Process program arguments */ if((rda = IDOS->ReadArgs("PUBSCREEN/K",(LONG*)args,NULL))) { win = WindowObject, WA_Title, "Transparent window", WA_DragBar, TRUE, WA_CloseGadget, TRUE, WA_SizeGadget, TRUE, WA_DepthGadget, TRUE, WA_Activate, TRUE, WA_Width, 400, WA_OverrideOpaqueness, TRUE, /* Ignore global settings */ WA_Opaqueness, 255, /* Open opaque */ WA_FadeTime, 500000, /* Delay in microseconds */ (args[ARG_pubscreen] ? WA_PubScreenName : TAG_IGNORE), args[ARG_pubscreen], WINDOW_Position, WPOS_CENTERSCREEN, WINDOW_Layout, VLayoutObject, LAYOUT_SpaceOuter, TRUE, LAYOUT_AddChild, OBJ(OBJ_SLIDER_1) = SliderObject, GA_ID, OBJ_SLIDER_1, GA_RelVerify, TRUE, SLIDER_Min, 10, SLIDER_Max, 100, SLIDER_Level, 50, SLIDER_KnobDelta, 10, SLIDER_Orientation, SLIDER_HORIZONTAL, SLIDER_Ticks, 11, SLIDER_ShortTicks, TRUE, SLIDER_LevelFormat, "%3ld", SLIDER_LevelPlace, PLACETEXT_IN, SLIDER_LevelMaxLen, 3, End, Label(" Fade Time (1/100s) "), CHILD_WeightedHeight, 0, LAYOUT_AddChild, OBJ(OBJ_SLIDER_2) = SliderObject, GA_ID, OBJ_SLIDER_2, GA_RelVerify, TRUE, SLIDER_Min, 20, SLIDER_Max, 255, SLIDER_Level, 255, SLIDER_KnobDelta, 10, SLIDER_Orientation, SLIDER_HORIZONTAL, SLIDER_Ticks, 21, SLIDER_ShortTicks, TRUE, SLIDER_LevelFormat, "%3ld", SLIDER_LevelPlace, PLACETEXT_IN, SLIDER_LevelMaxLen, 3, TAG_END), Label(" Opaqueness "), CHILD_WeightedHeight, 0, TAG_END), TAG_END); if((window = RA_OpenWindow(win))) { IIntuition->GetAttr(WINDOW_SigMask, win, &sigmask); /* Is compositing active and available? */ if((dri = IIntuition->GetScreenDrawInfo(window->WScreen))) { IIntuition->GetGUIAttrs(NULL,dri,GUIA_SpecialEffects,&specialfx,TAG_END); canfade = (BOOL)specialfx; if(!canfade) { /* deactivated: also disable gadgets, because they don't have any impact */ IIntuition->SetGadgetAttrs(GAD(OBJ_SLIDER_1),window,NULL, GA_Disabled,TRUE,TAG_END); IIntuition->SetGadgetAttrs(GAD(OBJ_SLIDER_2),window,NULL, GA_Disabled,TRUE,TAG_END); } } while(!done) { if(IExec->Wait(sigmask | SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) { done = TRUE; } while((result = RA_HandleInput(win,&code))) { switch(result & WMHI_CLASSMASK) { case WMHI_CLOSEWINDOW: done = TRUE; break; case WMHI_GADGETUP: switch(result & WMHI_GADGETMASK) { case OBJ_SLIDER_1: if (canfade) { IIntuition->GetAttrs(GAD(OBJ_SLIDER_1), SLIDER_Level, &fade_time, TAG_END); IIntuition->SetWindowAttrs(window, WA_FadeTime, fade_time * 10000, TAG_END); } break; case OBJ_SLIDER_2: if (canfade) { IIntuition->GetAttrs(GAD(OBJ_SLIDER_2), SLIDER_Level, &opaqueness, TAG_END); IIntuition->SetWindowAttrs(window, WA_Opaqueness, opaqueness, TAG_END); /* SetWindowAttr() returns immediately, hence we wait briefly */ /* until the windows has finished fading, before starting the next change */ IDOS->Delay(fade_time/2); } break; } break; } } } } IIntuition->DisposeObject(win); IDOS->FreeArgs(rda); } else IDOS->PrintFault(IDOS->IoErr(),"OpaquenessExample"); } else IDOS->Printf("program requires AmigaOS 4.1\n"); return 0; }
Non-square windows
The so far mentioned information always relates to complete square windows. In this second block we will see how you can 'remove' parts of a window or see through a hole in the window. These options are more complex, but AmigaOS supports programmers with its new functions. Since the Autodocs in this field are still quite incomplete we tried to compile all the important information.
Loading graphics
First of all, we need a graphic that will set the form of the window. The intuition.library supports us here with matching new functions that we haven't had a chance to present you yet. The image data is loaded in the memory with IIntuition->ObtainBitMapSource().The tag BMS_DoShade is important here in order to create/provide the alpha-channel data as well. At the moment this is only possible with PNG graphics. Other graphic formats do not provide this information.
IIntuition->ObtainBitMapInstance() declares a pointer to the image management. To get the actual image data you must request the desired values with IIntuition->BitMapInstanceControl(). Some important tags are here BMICTRL_GetWidth and BMICTRL_GetHeight for the size as well as BMICTRL_GetBitMap and BMICTRL_GetAlphaMap for the image data, i.e., the alpha mask. There is IIntuition->ReleaseBitMapInstance() and Iintuition->ReleaseBitMapSource() for release. The functions that you will need are compiled in the following list. The image data can be drawn with the usual functions such as IGraphics->BltBitMapTags().
struct Screen *scr; APTR source; APTR instance; struct BitMap *bitmap; struct BitMap *alpha; ULONG width; ULONG height; /* load the graphic in the memory, if necessary (internal via Datatype) */ if((source = IIntuition->ObtainBitMapSource((STRPTR)args[ARG_filename],BMS_DoShade,TRUE,TAG_END))) { /* request a bitmap instance for the screen */ if((instance = IIntuition->ObtainBitMapInstance(source,scr,TAG_END))) { /* Request image information and bitmap*/ IIntuition->BitMapInstanceControl(instance, BMICTRL_GetBitMap, &bitmap, BMICTRL_GetAlphaMap, &alpha, BMICTRL_GetWidth, &width, BMICTRL_GetHeight, &height, TAG_DONE ); if(bitmap && alpha) { /* Image data can be processed /blitted now */ } else IDOS->Printf("cannot get (alpha) bitmaps\n"); /* r BitMap Instance */ IIntuition->ReleaseBitMapInstance(instance); } else IDOS->Printf("cannot get instance of picture\n"); /* release graphic file */ IIntuition->ReleaseBitMapSource(source); } else IDOS->Printf("loading picture file failed\n");
Define window areas
The existing clipping system has been expanded in order to be able to set the areas in a window that display contents, that is, that let the background show through. With ILayers->InstallClipRegion() you could set even earlier rectangular areas in a window, in which a blit operation can draw data. This way, you can, for example, protect separate areas of a window too, if graphic data is larger than the window. However, in order to apply the ClipRect structure to an alpha channel you must use a private function of the layers. library:
ILayers->AllocClipRect()
This is provided exclusively for this task. The counterpart to it is ILayers->FreeClipRect(), that releases again the structure. The structure is already pre-initialised, so that only a few fields must be adjusted to your needs. On the one hand, you must store the graphic size in "bounds" and enter it in "BitMap". The required memory area for it can be created with IGraphics->AllocBitMap(). Then you must also blit the alpha-channel bitmap with IGraphics->BltBitMapTags(). The only thing you need to keep in mind in this whole procedure is that you automatically de-allocate the entered BitMap when you allocate the ClipRect structure! In case you do not want to do it, you must first set the field on NULL and de-allocate the BitMap with IGraphics->FreeBitMap(). These are the matching code rows for this part of the workshop:
struct ClipRect *ac; /* allocate and initialise ClipRect structure */ if((ac = ILayers->AllocClipRect(&(scr->LayerInfo)))) { struct TagItem bmtags[3] = { { BMATags_RGBFormat, RGBFB_ALPHA8 }, { BMATags_Friend, (ULONG)scr->RastPort.BitMap }, { TAG_END, 0 } }; /* allocate Alpha-ClipRect and request memory for Bitmap */ ac->bounds.MinX = 0; ac->bounds.MinY = 0; ac->bounds.MaxX = width-1, ac->bounds.MaxY = height-1; ac->Next = NULL; if((ac->BitMap = IGraphics->AllocBitMap(width,height,8,BMF_DISPLAYABLE|BMF_CLEAR|BMF_CHECKVALUE,(struct BitMap *)bmtags))) { /* Fill Alpha-ClipRect with Alpha-Bitmap */ IGraphics->BltBitMapTags(BLITA_Source, alpha, BLITA_SrcType, BLITT_CHUNKY, BLITA_SrcBytesPerRow, width, BLITA_Dest, ac->BitMap, BLITA_Width, width, BLITA_Height, height, TAG_DONE ); /* ... */ } else IDOS->Printf("cannot allocate alpha mask\n"); /* ClipRect deallocate; it also deallocates ac->BitMap! */ ILayers->FreeClipRect(&(scr->LayerInfo),ac); } else IDOS->Printf("cannot allocate cliprect\n");
Window show yourself
What is left is opening the window and blitting the graphic bitmap with IGraphics->BltBitMapRastPort(). You will need for it the following code rows:
struct Window *win; /* Open window with the Alpha mask */ if((win = IIntuition->OpenWindowTags(NULL, WA_Width, width, WA_Height, height, WA_Borderless, TRUE, WA_AlphaClips, ac, ... TAG_DONE))) { /* blit the graphic file into the window */ IGraphics->BltBitMapRastPort(bitmap, 0, 0, win->RPort, 0, 0, width, height, MINTERM_ABC|MINTERM_ABNC); }
When you open the window, you must also enter the tag WA_AlphaClips. The window will be opened without a frame and without system symbols. They are not shown, but as soon as you activate the window, the frame elements are drawn with Intuition, at least as far as the alpha mask allows it. You can also target the alpha mask display. Just do not copy any image data into the Rastport after you have opened the window. The alpha mask does not only set the opaque and transparent points, but it can also be created gradually. Thus, even image parts can appear as half-transparent and let the background shine through. The 'partly transparent ball' graphic shows the result.
You should also keep in mind that the window may not be of the GimmeZeroZero type. In this case there are already internal clipping masks, which cannot be combined with the alpha-clipping.
The program "BorderlessWindow" serves as a complete example for this topic showing all the mentioned functions in a practical interplay. A matching graphic is also included. Have fun experimenting with the source-code and looking for new possibilities!
/* Michael Christoph * BorderlessWindow.c * * gcc BorderlessWindow.c -o BorderlessWindow -l auto */ /******************************* INCLUDES *************************************/ #include <exec/libraries.h> #include <graphics/blitattr.h> #include <graphics/composite.h> #include <intuition/intuition.h> #include <intuition/bitmapshare.h> #include <intuition/gui.h> #include <proto/exec.h> #include <proto/dos.h> #include <proto/graphics.h> #include <proto/intuition.h> #include <proto/layers.h> /******************************************************************************/ static const char *version USED = "\0$VER: BorderlessWindow 1.1 (22.07.2013) - (c) Jan.2009 by Meicky-Soft\n"; /******************************************************************************/ int main(int argc, char *argv[]) { int res = RETURN_FAIL; struct RDArgs *rda; struct Screen *scr; struct Window *win; struct ClipRect *ac; struct DrawInfo *dri; struct BitMap *bitmap; struct BitMap *alpha; APTR source; APTR instance; ULONG width; ULONG height; ULONG specialfx; enum { ARG_filename, ARG_pubscreen, ARG_MAX }; ULONG args[ARG_MAX]={ (ULONG)"testpic.png",0 }; /* AmigaOS 4.1 is required */ if(SysBase->lib_Version >= 53) { /* Process program arguments */ if((rda = IDOS->ReadArgs("FILENAME,PUBSCREEN/K",(LONG*)args,NULL))) { /* Lock screen for display */ if((scr = IIntuition->LockPubScreen((STRPTR)args[ARG_pubscreen]))) { /* Ensure the screen supports compositing */ if(( dri = IIntuition->GetScreenDrawInfo(scr))) { IIntuition->GetGUIAttrs(NULL,dri,GUIA_SpecialEffects,&specialfx,TAG_END); if(!specialfx) IDOS->Printf("WARNING: screen does not support alpha composing\n"); IIntuition->FreeScreenDrawInfo(scr,dri); } /* If needed, load the image into memory (internal via Datatype) */ if((source = IIntuition->ObtainBitMapSource((STRPTR)args[ARG_filename], BMS_DoShade, TRUE, BMS_DoMask, TRUE, TAG_END))) { /* Request a BitMap for our screen */ if((instance = IIntuition->ObtainBitMapInstance(source,scr,TAG_END))) { /* Get BitMap and information about the image */ IIntuition->BitMapInstanceControl(instance, BMICTRL_GetBitMap, &bitmap, BMICTRL_GetAlphaMap, &alpha, BMICTRL_GetWidth, &width, BMICTRL_GetHeight, &height, TAG_END ); if(bitmap && alpha) { /* Create and init ClipRect structure */ if((ac = ILayers->AllocClipRect(&(scr->LayerInfo)))) { /* Fill alpha-ClipRect and allocated memory for the BitMap */ ac->bounds.MinX = 0; ac->bounds.MinY = 0; ac->bounds.MaxX = width-1, ac->bounds.MaxY = height-1; ac->Next = NULL; if((ac->BitMap = IGraphics->AllocBitMapTags(width, height, 8, BMATags_Displayable, TRUE, BMATags_Clear, TRUE, BMATags_PixelFormat, PIXF_ALPHA8, BMATags_Friend, scr->RastPort.BitMap, TAG_END))) { /* Fill alpha-ClipRect with alpha-BitMap */ IGraphics->BltBitMapTags(BLITA_Source, alpha, BLITA_SrcType, BLITT_CHUNKY, BLITA_SrcBytesPerRow, width, BLITA_Dest, ac->BitMap, BLITA_Width, width, BLITA_Height, height, TAG_END ); /* Open borderless window */ if((win = IIntuition->OpenWindowTags(NULL, WA_Left, 1580, WA_Top, 120, WA_Width, width, WA_Height, height, WA_CloseGadget, FALSE, WA_DragBar, FALSE, WA_DepthGadget, FALSE, WA_SizeGadget, FALSE, WA_Borderless, TRUE, WA_ToolBox, TRUE, WA_AlphaClips, ac, WA_CustomScreen,scr, WA_Title, "Borderless window", WA_IDCMP, IDCMP_MOUSEBUTTONS, TAG_END ))) { /* Define the full windows as draggable */ struct Gadget draggad = { NULL, 0,0, width,height, GFLG_GADGHNONE, 0, GTYP_WDRAGGING, NULL,NULL,NULL,0,0,0,NULL }; win->FirstGadget = &draggad; /* Blit the image into the window */ IGraphics->BltBitMapRastPort(bitmap, 0, 0, win->RPort, 0, 0, width, height, MINTERM_ABC | MINTERM_ABNC); /* Wait for CTRL-C to end the program */ IExec->Wait(SIGBREAKF_CTRL_C); res = RETURN_OK; /* Close window*/ IIntuition->CloseWindow(win); } else IDOS->Printf("can not open window\n"); } else IDOS->Printf("can not allocate alpha mask\n"); /* Free ClipRect; also frees ac->BitMap! */ ILayers->FreeClipRect(&(scr->LayerInfo),ac); } else IDOS->Printf("can not allocate cliprect\n"); } else IDOS->Printf("can not get (alpha) bitmaps\n"); /* Free BitMap instance */ IIntuition->ReleaseBitMapInstance(instance); } else IDOS->Printf("can not get instance of picture\n"); /* Free image source */ IIntuition->ReleaseBitMapSource(source); } else IDOS->Printf("loading picture file failed\n"); /* Unlock screen */ IIntuition->UnlockPubScreen(NULL,scr); } else IDOS->Printf("can not lock pubscreen\n"); IDOS->FreeArgs(rda); } else { IDOS->PrintFault(IDOS->IoErr(),"BorderlessWindow"); res = RETURN_ERROR; } } else IDOS->Printf("Program requires AmigaOS 4.1\n"); return res; }
Grab the test graphic here: testpic.png.
Outlook
You can look forward to the next issue, in which we will deal with datatypes. Do not expect any novelties regarding the program, but for the sake of completeness we should mention these options as well.
With a hole in a window you can create funny effects, especially if you move the window around.
Authors
Written by Michael Christoph and Aleksandra Schmidt-Pendarovska
Copyright (c) 2013 Michael Christoph