From: winter@cs.uni-sb.de (Stefan Winterstein)
Newsgroups: comp.sys.amiga.programmer
Subject: Upside-down graphics revisited
Keywords: Kopfstand, hack, upside-down, negative modulo
Date: 17 Aug 92 08:39:40 GMT
Reply-To: winter@cs.uni-sb.de
Followup-To: Upside down graphics??
Organization: Universitaet des Saarlandes


In article <1992Aug4.171235.19277@doug.cae.wisc.edu>
pochanay@cae.wisc.edu (Adisak Pochanayon) asked:
>
>   Can anyone tell me if the hardware BitPlane Mod values are signed or
>unsigned?  If they are signed, it should be extremely easy to display
>a graphic screen upside down.  Has anyone tried this?  If so, is it
>possible to do under 1.3 and 2.0 using the graphics.library functions
>on a viewport?

    Yes - there really IS such a thing as a negative modulo.
    Yes - this can be used to turn the display upside down - without
          blitting tons of BitPlane data.
And Yes - this can be done in a completely system-friendly (well, sort
          of OS-supported) manner: Ever used a *negative* Bitplane width?

    Appended is the commented SAS/C source of my program 'Kopfstand'
("head-stand"?), which simply turns the frontmost screen upside-down.
This can be Workbench or any other screen you like. Everything continues
to work just fine, except the screen is turned.
    Mouse pointer and movement are not mirrored, you have to guess the
position on the original screen.  You exit the program by holding down
the mouse button for about one second.  (You can use LALT-LAMIGA to
simulate a mouse click without exiting Kopfstand.)

    Here is a quick explanation of how it works:  First, 'Kopfstand' makes
a copy of the BitMap structure belonging to the frontmost screen, which it
modifies in two ways:
    - by calling InitBitMap with *negative* Screen width
    - by making the plane pointers point to *last* row of their plane

    It then attaches this BitMap to the current ViewPort's BitMap pointer
(while leaving the Screen's BitMap alone) and lets graphics.library
recalculate the appropriate copperlist.  Et voila` - we have our negative
modulo, which equals -2*ScreenWidth.
    Since we changed the plane pointers, the last line of the plane is the
first line on the screen.  Add the negative modulo and the next line
displayed is the last-but-first line of the plane and so on (prove of
correctness by induction on BitMap->Rows...).

    Warning:  This program does one nasty thing or two.  Especially
changing plane pointers in the current ViewPort's BitMap is not a
very safe thing to do (but since the usual way to get plane pointers is
from the Screen's BitMap, it usually works).  Also 'Kopfstand' relies on
the fact that the screen turned upside-down will not close before turned
back again.
    After all, this is a screen hack in the truest of sense - use it at
your own risk.  Don't blame me if your Amiga gurus, formats your
hard-drive or blows up your house (although I didn't encounter anything of
the above ;-))

                'Kopfstand' is dedicated to Leo L. Schwab.

Any reaction on this article is welcome. Enjoy!

-Stefan

P.S.: I posted it in c.s.a.programmer since c.s.a.sources seems to be
      rather unreliable.
---                                                                 ///
  Stefan Winterstein, Schwarzbachstr. 7, W-6650 Homburg 6, Germany ///
  Usenet: winter@cs.uni-sb.de                                __   ///
  SGNTR VRS VRSN 3.1B:                                       \\\ ///
  Strppd nd cmprssd fr qckr trnsmssn. nfct yrs tdy!           \\X//


-------- cut here -------- Kopfstand.c -------- cut here --------
;/*	Execute me to compile !
    lc -cfist -j73 -ms -M -O -rr -v -w Kopfstand.c
    blink Kopfstand.o SC SD ND
    quit
  */
/*********************************************************************/
/*                                                                   */
/*                            Kopfstand.c                            */
/*                            ~~~~~~~~~~~                            */
/*                       by Stefan Winterstein                       */
/*                       (winter@cs.uni-sb.de)                       */
/*                                                                   */
/*              Makes topmost Screen stand upside-down.              */
/*                                                                   */
/*                 Exit by pressing LMB for a while.                 */
/*                                                                   */
/*     Neither uses Blitter nor moves BitPlanes about in memory.     */
/*                                                                   */
/*  Ever heard of negative plane width and modulo??? Look and see!   */
/*                                                                   */
/*                    Dedicated to Leo L. Schwab                     */
/*                                                                   */
/*  Written 16-Aug-92 using SAS/C 5.10b                              */
/*                                                                   */
/*********************************************************************/

#include <exec/types.h>
#include <exec/memory.h>
#include <hardware/cia.h>
#include <graphics/view.h>
#include <intuition/intuitionbase.h>

#define _USEOLDEXEC_
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>

#define CHECKDELAY	50L
#define ciaa (*((struct CIA *) 0xbfe001))

void __stdargs __saveds
main(void)		     /* entry point */
{
struct DosLibrary    *DOSBase;
struct GfxBase	     *GfxBase;
struct IntuitionBase *IntuitionBase;

struct View	*v;
struct ViewPort *vp;
struct Screen	*s;
struct BitMap	*realbm, *mybm;

UWORD height, bpr, planedelta, width, depth, i;


 if (IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 33L)) {
  if (GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 33L)) {
    if (DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 33L)) {
      if (mybm = AllocMem(sizeof(*mybm), MEMF_PUBLIC | MEMF_CLEAR)) {

	Forbid();   /* Nobody should disturb us. */

	    /* First we need to know a few addresses:		   */
	    v = ViewAddress();		    /* Intuition's View,   */
	    s = IntuitionBase->FirstScreen; /* the top-most Screen */
	    vp = &(s->ViewPort);	    /* and its ViewPort.   */

	    /*
	     * Now look for the BitMap structure
	     */
	    realbm = &s->BitMap;

	    /* and make a copy of it. */
	    *mybm = *realbm;

	    /* The following values are needed later */
	    depth = mybm->Depth;
	    height = mybm->Rows;
	    bpr = mybm->BytesPerRow;
	    width = bpr << 3;

	    /* This is the byte offset for the last line of a plane. */
	    planedelta = bpr * (height-1);

	    /*
	     * Here comes the trick:
	     *
	     * We call InitBitMap() with a *negative* width!
	     * When we supply the ViewPort with our faked BitMap
	     * to MakeVPort(), graphics.library will
	     * construct a copperlist with the appropriate
	     * negative modulo values for BPLxMOD.
	     */
	    InitBitMap(mybm, depth, -width, height);

	    /*
	     * We want the last line of a plane to be the
	     * first line on the display, so we'll have to
	     * make eache plane pointer point to the last line
	     * of the plane, which is planedelta bytes away.
	     */
	    for (i=depth; i; )
		mybm->Planes[--i] += planedelta;

	    /*
	     * Now we patch in the faked BitMap into the ViewPort.
	     * The Screen's BitMap is not changed, so that all
	     * graphic output continues to go to the right place.
	     */
	    vp->RasInfo->BitMap = mybm;

	    /* let graphics.library build that crazy copperlist */
	    MakeVPort(v, vp);
	    MrgCop(v);

	    /* and load it. */
	    LoadView(v);

	    /* Voil`! */

	    /*
	     * We leave our faked BitMap in our ViewPort for
	     * a little longer so that every time Intuition builds up a
	     * new copperlist (e.g. ScreenToFront), it is fooled
	     * and displays our ViewPort upside-down.
	     * Lets hope nobody closes it in the meantime...
	     */

	Permit();

	/* Now check for a mouse click every once in a while. */
	while ((ciaa.ciapra) & (1 << CIAB_GAMEPORT0))
	    Delay(CHECKDELAY);
	/* (dirty, I know...) */

	/* We are done now, restore the original BitMap, */
	vp->RasInfo->BitMap = realbm;
	/* (even dirtier...) */

	/* and reach normality. */
	MakeVPort(v, vp);
	MrgCop(v);
	LoadView(v);

	FreeMem(mybm, sizeof(*mybm));
      }
      CloseLibrary ((struct Library *) DOSBase);
    }
    CloseLibrary ((struct Library *) GfxBase);
  }
  CloseLibrary ((struct Library *) IntuitionBase);
 }
}
-------- cut here -------- Kopfstand.c -------- cut here --------