Logo Search packages:      
Sourcecode: freecraft version File versions

deco.c

//   ___________             _________                _____  __
//   \_       _____/______     ____ ____ \_       ___ \____________ _/ ____\/  |_
//    |        __) \_  __ \_/ __ \_/ __ \/        \  \/\_  __ \__  \\   __\\   __|
//    |         \ |  | \/\  ___/\    ___/\         \____|  | \// __ \|  |   |  |
//    \___  /     |__|  \___  >\___  >\______  /|__|  (____  /__|   |__|
//      \/            \/         \/      \/              \/
//  ______________________                     ______________________
//                  T H E       W A R       B E G I N S
//       FreeCraft - A free fantasy real time strategy game engine
//
/**@name deco.c   -     Mechanism for all 2D decorations on screen. */
//
//    (c) Copyright 2002 by Lutz Sammer and Stephan Rasenberg
//
//    FreeCraft is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published
//    by the Free Software Foundation; only version 2 of the License.
//
//    FreeCraft is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    $Id: deco.c,v 1.7 2002/12/17 07:39:11 nehalmistry Exp $

//@{


/**
**    page Decorations Decorations - a mechanism for all what is on screen
**    
**    This pages describes the intend to use a special mechanism that
**    functions as an intermediate between the freecraft engine and the
**    video routines.
**    
**    @note It won't take away the possibility to directly draw something on
**    the screen without using this mechanism, so you have to be careful when
**    you decide to do that, as you should take care that no other object is
**    overlapping yours or you should be able to handle those overlapping
**    objects yourself.
**    
**    
**    @section DecoWhy Why we want 'a decoration mechanism'
**
**    This mechanism was though off, as we got some basic problems when
**    dealing with drawing everything separately from different parts of the
**    source code, details follow:
**
**    @subsection DecoP1 Problem 1: higher screen resolution support
**    
**    Screen resolutions beyond 640x480 requires much memory, which also
**    costs speed performance to draw it. Not only the drawing needs to
**    write to this memory. Some what is drawn, needs to read first to
**    combine certain colors (like Fog of War).
**
**    When drawing you also need to invalidate the area you have drawn.
**    Meaning (for some video platforms, like X11) that what you draw is
**    drawn in a background buffer (a videopage) and the invalidate action
**    puts it on the screen.
**
**    Which is nice, as you can do multiple draws and invaldiate the total
**    area covered by those in one go, delivering a smoother update (as it
**    does not flicker).  But to invalidate the total buffer needs a very
**    large amount of memory to be transferred for higher screen resolutions.
**
**    So we want to minimize the access to this memory, to only update those
**    parts that have been changed, increasing the performance.
**
**    Example: before using this mechanism, we simply handled the entire main
**    map, when an unit moves from one tile to another. While we only
**    wanted the few tiles it crosses to be updated (for that unit).
**
**    @subsection DecoP2 Problem 2: unit decorations and lines
**
**    To redraw only what is needed, we need to split these areas into
**    separate rectangles. This is much faster and simpler then any other
**    variant known to me. And is also nicer, as in the end we also need to
**    invalidate rectangles areas (so we don't need to determine these twice).
**
**    Units a drawn as a sprite and so if we want to redraw the unit, we
**    need to know the rectangle region where the sprite it located and
**    redraw everythin in that.
**
**    But we also draw extra decorations near the unit (like a health bar,
**    mana and such..), these really look nice in the game. But makes it
**    harder to denote the area that needs to be redrawn, as this is no
**    longer the rectangle as is determined by the sprite. And it can
**    even change, as some decorations will only appear when the unit is
**    selected.
**
**    Currently we also support lines showing the destination/path the units
**    will follow, we want to have it divided in a lot of small rectangles,
**    as a single rectangle bounding box will cover too much of the screen
**    (so a redraw of all what is inside) and again makes the invalidate
**    slower.
**
**    @subsection DecoP3 Problem 3: overlapping units/graphics
**
**    For an update to draw some graphic, we need to know which graphics
**    are overlapping it as they also need to be redrawn. There is no link
**    available of what is located near eachother. There is a link denotihg
**    the location of the unit on the map, but what we see doesn't have to
**    be at the same location on the map.
**
**      Example: think of a flying unit, which is not shown on the same tile
**    as a ground unit underneath, but slightly shifted.. possible one/two
**    tiles further.
**
**    So we need to denote some linkage in way that is fast and accurate in
**    determining what is overlapping and needs to be redrawn.
**
**    @subsection DecoP4 Problem 4: depth level (z-axis)
**
**    Before this mechanism we first draw the ground, then the units, then
**    the missiles and as last the fog-of-war. We also want more depth-levels
**    to be supported and let so for overlapping units/etc.. we need to
**    automaticly determine which needs to be drawn first (on given z-level).
**
**    @subsection DecoP5 Problem 5: future decorations
**
**    As we want to support all kinds of new decorations to make the
**    interface look nicer, we want a type-independent mechanism. Not as done
**    before: units/missiles/gui-buttons/.. but all of one type 'Decoration'.
**    Making the mechanism unware of what the Decoration needs to represent,
**    se we cann attach different kinds of decoratons, without having to
**    update this file and/or add new functionaility..
**    
**    
**    @section DecoIdeas Decoration mechanism project ideas
**
**    Ok, now to handle all those problems and most of all, be efficient
**    and fast, I have a proposal which I will describe according to the
**    following storyline delivering us the implementation issues, so you
**    can understand the reasoning behind it.
**
**    @note if you gonna change this implementation, please update this
**    storyline part and denote why things have been changed.
**
**    @subsection DecoI1 Idea 1: screentiles
**    We first need some structure of denoting what is where on the screen.
**
**    We could do that with quad-trees or some kind of mathematical way of
**    using line-equations to denote the location of each decoration, but I
**    considered these to be too slow. Also looking at the invalidate part,
**    we need to split a line up in rectangles and as each graphic is a
**    rectangle I came to the conclusion to split the screen up in multiple
**    tiles and let its decoration denote the tile(s) it is in.
**
**    @subsection DecoI2 Idea 2: 4x4 matrix in 16bit
**
**    Now we don't really need to have a direct linkage from tile to
**    decoration, as we only need to know which tiles are 'dirty', meaning
**    some graphic is updated, it marks the tiles it is covering and the
**    other Decoratons can look at their tiles to see if they need to be
**    redrawn.
**
**    As we only have 'clean' and 'dirty' tiles, I made the conclusion we can
**    use a single bit for that and save lots of memory. To enable us to
**    cover as much tiles in one integer, I use a 16bit word to denote 4x4=16
**    tiles. So to check a 4x4 tile area, only one 16bit integer is read from
**    memory.
**
**    Example: a unit covering 2x2 tiles, with the left-top tile at (5,6)
**    tiles away from the top-left of the screen. This will be at entry
**    [5/4,6/4]  in an array of 16bit integers denoting all tiles on the
**    screen. And at bit-index [5%4,6%4] in such a 16bit integer:
**    with bit-indexes:  3  2  1  0  delivers: 0000  or as hecadecimal:
**                   7  6  5  4            0000       0x6600
**                  11 10  9  8            0110
**                  15 14 13 12            0110
**
**    @subsection DecoI3 Idea 3: Minimize decorations to max 4x4 tiles
**
**    Now we still need to determine the tiles-size, but no matter what we
**    choose later on. There will always be decorations covering multiple
**    tiles. One 16bit integer denoted 4x4 tiles, but a decoration of 4x4
**    tiles will it only in a single 16bit integer when its top-left tile is
**    exactly at a 4-tile boundary. As wordt-case scenario, where the
**    top-left ia at the bottom-right of the 4x4 bitmap, it can only be 1x1
**    tile big. So we need to support decorations covering multiple 16bit
**    integers.
**
**    However I want to restrict each decoration to 2x2 16bits, as these
**    can be simply marked and checked with 4 16bit bitmasks. As the support
**    of dynamic decoration sizes requires extra while-loops and
**    if-statements to determine which 16bit entries in the array are we
**    to mark/check. I think by not using any if- or while-loop will speed
**    up performance a bit. So each decoration has 4 16bit bitmasks, the
**    left-top, right-top, left-bottom and right-bottom 4x4 bitmask.
**
**    Now in the worst-case the decoration with the lef-top tile at the
**    right-bottom bit of the left-top bitmask, will still be able to able
**    have a 5x5 tile size. And in the best-case, with the left-top tile at
**    the left-top bit of the left-top bitmaks, can handle 8x8 tiles.
**
**    But I'm not using this total freedom of beyond 4x4 tiles, as it gets
**    very inefficient to determine bits covering more then 4x4 tiles over
**    different 16bit bitmask. And altough a 4x4 tile area can also cover
**    all 4 16bit integers, I can conver this to an area within a 32bit
**    integer. The trick for this is very simple:
**
**    In example: a 3x2 tile area, with only some bits marked 'dirty', is
**    determined by the following bits in 4 16bit bitmasks.
**
**    tile-area: 00000000  delivering bitmasks: A 0000 0000 B
**             00000000                 0000 0000
**             00000000                 0000 0000
**             00101000 A = top right         0001 0100
**             00011000 B = left top
**             00000000 C = right bottom  C 0001 1000 D
**             00000000 D = left bottom       0000 0000
**             00000000                 0000 0000
**                                      0000 0000
**
**    Now if we combine A and C in a 32bit integer and right bit-shift with
**    the y-bit-offset of the decoration in the left-top 16bit. We can
**    put A and C in one 16bit:
**
**    (A and C) 0000 -right-shift-> 0001
**            0000                  0001
**            0000                  0000
**            0001                  0000
**            0001
**            0000
**            0000
**            0000
**
**    We can do the same for B and D and combine the two resulting 16bit in
**    one 32bit, representing the 8x4 tile area in which the 'dirty' bits are:
**
**    (A and C) 0001  (B and D) 0100  as one 32bit: 00010100
**            0001              1000            00011000
**            0000              0000            00000000
**            0000              0000            00000000
**
**    Now is reasonable fast to find the bits marked 'dirty'.
**
**    @note The mechanism still supports larger Decorations though, from
**    caller point-of-view. But these are simply split up into multiple
**    ones, linking them with eachother, so the 'caller' does not have to
**    know about them.
**
**    @subsection DecoI4 Idea 4: DIRTYSCREEN_BITSIZE=16 (tile=16x16 pixels)
**
**    To denote tiles as pixels will costs too much memory and too much
**    time to maintain them. So we use tiles with a larger size to prevent
**    that, but care must be taken that we don't make them too large or we
**    let each tile overlap too many decorations and that costs unneeded
**    redraw. 
**
**    Looking at this, the largest decoration as unit is currently
**    128x128 (dark portal). So if we want it to be represented as a
**    single decoration and with a maximum of 2x2 tiles, we get
**    64x64 pixels for each tile.  This is too big though, as a normal
**    ground graphic of 32x32, will already be split up in 2x2 decorations.
**
**    But if we would use a tile of 32x32, a unit crossing from one tile to
**    another, would have an overlap with four tiles at once. And as it
**    happens a lot that units go that near to eachother on a ground graphic
**    of 32x32, this would lead to a redraw of the entire ground graphic and
**    the other units overlapping this. Which would cause a chain-reaction of
**    unneeded redraws.
**
**    This overlap happens a lot, also when an unit stand stationary, its
**    decorations (healthbar,manabar,..) is drawn below it.. on a lower
**    ground tile graphic.
**
**    So I suggest to do another step back and let one tile represent 16x16
**    pixels, this will make it possible to update only on of four corners of
**    a ground graphic, so overlapping decorations on the other three corners
**    don't have to be updated.
**
**    For screen resolution 1600x1280, this will cost 100x80 tiles in
**    25x20 16bits = 1000 bytes. Which will be accessed a lot, but as its
**    that small it would be accesable from cache and not influence speed
**    that much.
**
**    @note for the assumption that a ground graphic of 32x32 pixels can be
**    put in 2x2 tiles, the main screenmap should start exactly at a tile
**    boundary.
**/

/*----------------------------------------------------------------------------
--    Includes
----------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freecraft.h"

#ifdef NEW_DECODRAW

#include "video.h"
#include "sweepline.h"
#include "deco.h"

/*----------------------------------------------------------------------------
--    Declarations
----------------------------------------------------------------------------*/

/**
**  The accuracy of decoration boundary box in screentiles:
**    The entire screen is divided into screentiles of the same size.
**    With n==1 we have highest accurate representation and prevent unneeded
**    redraw of decorations which aren't overlapping, but cost much memory and
**    CPU speed.
**    With high n we have less accurate representation and may redraw
**    decorations which aren't overlapping as pixels, but are as tiles.
**    With practice an ideal accuracy should be used, which is defined by
**    DIRTYSCREEN_BITDETAIL as a bit-shift (>=0) of getting from pixel to tile
**/
#define DIRTYSCREEN_BITDETAIL 4 // 4bit-->16x16 pixels for each screen-tile
#define DIRTYSCREEN_DETAILSIZE (1 << DIRTYSCREEN_BITDETAIL)

/**
**  The restricted number of tiles as both width and height for
**  DecorationSingle which only can handle a 2x2 of 16bit matrix.
**  So we could support a DECOSINGLE_TILES==8 (2 16bit matrixes with each a
**  width of 4 tiles) when the decoration start at the upper-left corner of the
**  tile-area represented. But in the worst-case, the decoration start at the
**  lower-right corner of the upper-left tile, making only a maximum of 5 tiles
**  possible in that case.. Even more so, the current code of CheckRedraw will
**  restrict this further concerning the height, as it will translate the
**  'dirty' bits of all 4 16bit matrixes into a single 32bit for 8x4 tiles,
**  making it much eassier to check anbd find the 'dirty' bits..
**  FIXME: investigate if the use of a selection to get the best possible
**         representation is worth the trouble and so doesn't result in
**         a performance loss.
**/
#define DECOSINGLE_TILES 4
#define DECOSINGLE_PIXELS (DECOSINGLE_TILES*DIRTYSCREEN_DETAILSIZE)


/*----------------------------------------------------------------------------
--    Externals
----------------------------------------------------------------------------*/

extern void DecorationInit(void);

extern Deco *DecorationAdd( void *data,
                      void (*drawclip)(void *data),
                      DecorationLevel l, 
                      unsigned x, unsigned y,
                      unsigned w, unsigned h );
extern void DecorationRemove( Deco *d );
extern void DecorationRemoveLevels( DecorationLevel min, DecorationLevel max );


extern void DecorationMark( Deco *d );

extern void DecorationRefreshDisplay(void);
extern void DecorationUpdateDisplay(void);


/*----------------------------------------------------------------------------
--    Variables
----------------------------------------------------------------------------*/

/**
**    The array dirtysceen is the representation of the screen in screentiles.
**    Each tile is normally 'clean' and can only be marked 'dirty', so we only
**    need 1 bit for each tile. By using a 4x4 tile area, we can store 16
**    tiles in 2 bytes and still be able to get the proper bit with
**    bit-operations fast enough:   <--------x
**                                  3  2  1  0 y
**                                  7  6  5  4 |
**                                 11 10  9  8 |
**                                 15 14 13 12 v
**
**      As this should be platform independent, we can't use type 'int' (which
**    is either 16, 32 bit or more), but use 2 of type 'char' which size is
**    always 8 bit.
**
**      dirtyscreen           = the array itself
**      dirtyscreen_xtiles    = no. 2-byte tiles horizontally (screen width)
**      dirtyscreen_ytiles    = no. 2-byte tiles vertically (screen height)
**      dirtyscreen_size      = the total size in bytes of the array
**/
static unsigned char *dirtyscreen = NULL;
static unsigned dirtyscreen_xtiles, dirtyscreen_ytiles, dirtyscreen_size,
                dirtyscreen_xbitmaps, dirtyscreen_ybitmaps;

/**
**    To denote an entire row/colum of tiles in one 4x4 segment, these
**    bitmasks help. They are based upon an bit-index 0..3, for which 0 is
**    exactly at a 4x4 matrix boundary (for which we don't need a mask).
**
**    xbitmaskhead      = mask for a bit-index denoting a starting x-pos 
**    ybitmaskhead      = mask for a bit-index denoting an ending x-pos 
**    xbitmasktail      = mask for a bit-index denoting a starting y-pos 
**    ybitmasktail      = mask for a bit-index denoting an ending y-pos 
**
**    @note: use an and-operation upon these bitmasks to get a combination
**    for any row/column inside a single 4x4 matrix.
**/
static unsigned xbitmaskhead[4] = {
  0xFFFF /*1111*/, 0xEEEE /*1110*/, 0xCCCC /*1100*/, 0x8888 /*1000*/,
         /*1111*/         /*1110*/         /*1100*/         /*1000*/
         /*1111*/         /*1110*/         /*1100*/         /*1000*/
         /*1111*/         /*1110*/         /*1100*/         /*1000*/
};
static unsigned xbitmasktail[4] = {
  0xFFFF /*1111*/, 0x1111 /*0001*/, 0x3333 /*0011*/, 0x7777 /*0111*/,
         /*1111*/         /*0001*/         /*0011*/         /*0111*/
         /*1111*/         /*0001*/         /*0011*/         /*0111*/
         /*1111*/         /*0001*/         /*0011*/         /*0111*/
};
static unsigned ybitmaskhead[4] = {
  0xFFFF /*1111*/, 0xFFF0 /*0000*/, 0xFF00 /*0000*/, 0xF000 /*0000*/,
         /*1111*/         /*1111*/         /*0000*/         /*0000*/
         /*1111*/         /*1111*/         /*1111*/         /*0000*/
         /*1111*/         /*1111*/         /*1111*/         /*1111*/
};
static unsigned ybitmasktail[4] = {
  0xFFFF /*1111*/, 0x000F /*1111*/, 0x00FF /*1111*/, 0x0FFF /*1111*/,
         /*1111*/         /*0000*/         /*1111*/         /*1111*/
         /*1111*/         /*0000*/         /*0000*/         /*1111*/
         /*1111*/         /*0000*/         /*0000*/         /*0000*/
};

/**
**    All decoration structs ordered by their depth level are stored in
**    this array as a link-list for every depth-level.
**/
static Deco *dhead[LevCount] = { NULL };

/**
**    We have a separate link-list to store decorations which are deleted,
**    doing so we can re-use such a deocration again without a new
**    allocation.
**    FIXME: this will result into a memory-leak when this mechanism is no
**    longer used. But I expect this to be needed througout the program and
**    so no memory is given back.
**/
static Deco *dgarbage = NULL;
static DecorationSingle *dsgarbage = NULL;


/*----------------------------------------------------------------------------
--    Functions
----------------------------------------------------------------------------*/

/**
**    Given decoration-single-tile is put in the garbage-list, so it can be
**    re-used another time.
**
**    @note it might not be referenced as the old given type, as it should
**    be considered de-allocated (access can result in undefined behavior).
**/
static void DecorationSingleDelete( DecorationSingle *t )
{
  t->nxt    = dsgarbage;
  dsgarbage = t;
}

/**
**    Allocate (or re-use from garage list) a DecorationSingle struct
**/
static DecorationSingle *DecorationSingleAllocate(void)
{
  DecorationSingle *t;

  // Get memory for garbage link-list
  if ( !dsgarbage )
  {
    int size = 1024 / sizeof( DecorationSingle ); // about 1KB at a time
    dsgarbage = t = malloc( size * sizeof( DecorationSingle ) );
    if ( !t )
    {
      printf( "Out of memory (DecorationTypeAllocate,video/deco.c)\n" );
      exit( 1 );
    }
    while ( --size )
    {
      t->nxt = t + 1;
      t++;
    }
    t->nxt = NULL;
  }

  // Get new Decoration from garbage link-list
  t = dsgarbage;
  dsgarbage = (DecorationSingle *) t->nxt;

  return t;
}

/**
**    Given decorationis put in the garbage-list, so it can be
**    re-used another time.
**
**    @note it might not be referenced as the old given type, as it should
**    be considered de-allocated (access can result in undefined behavior).
**/
static void DecorationDelete( Deco *d )
{
  d->nxt   = dgarbage;
  dgarbage = d;
}

/**
**    Allocate (or re-use from garage list) a Decoration struct
**/
static Deco *DecorationAllocate(void)
{
  Deco *d;

  // Get memory for garbage link-list
  if ( !dgarbage )
  {
    int size = 1024 / sizeof( Deco ); // about 1KB at a time
    dgarbage = d = malloc( size * sizeof( Deco ) );
    if ( !d )
    {
      printf( "Out of memory (DecorationTypeAllocate,video/deco.c)\n" );
      exit( 1 );
    }
    while ( --size )
    {
      d->nxt = d + 1;
      d++;
    }
    d->nxt = NULL;
  }

  // Get new Decoration from garbage link-list
  d = dgarbage;
  dgarbage = d->nxt;

  return d;
}

/**
**    Given decoration is removed from this mechanism and will no longer
**    affect other ones.
**
**    @param d    = decoration as been created with DecorationAdd
**
**    @note: it should be prevented that Decorations are added and directly
**    removed, as they still have parts of the screen marked 'dirty' and
**    will influence other decorations to be re-drawn unneededly.
**/
void DecorationRemove( Deco *d )
{
  DecorationSingle *t, *tmp;

// Mark is needed.. as we possibly need to redraw what was overlapping it
  DecorationMark( d );

// remove from linklists and let its memory be re-used again
  if ( d->prv )
    d->prv->nxt = d->nxt;
  else dhead[ d->l ] = NULL;

  if ( d->nxt )
    d->nxt->prv = d->prv;

  t = d->singles;
  DecorationDelete( d );
  do
  {
    tmp = t;
    t = t->nxt;
    DecorationSingleDelete( tmp );
  }
  while ( t );
}

/**
**    Remove all decorations belonging to a range of levels at once..
**
**    @param min  = first level to remove
**    @param max  = last level to remove (higher or equal to min)
**/
void DecorationRemoveLevels( DecorationLevel min, DecorationLevel max )
{
  DebugCheck( min > max || max == LevCount );

  do
  {
    Deco *d;

    while ( (d=dhead[ min ]) )
    {
      DecorationSingle *t, *tmp;

      DecorationMark( d );
      t = d->singles;
      DecorationDelete( d );
      do
      {
        tmp = t;
        t = t->nxt;
        DecorationSingleDelete( tmp );
      }
      while ( t );

      dhead[ min ] = d->nxt;
    }
  }
  while ( ++min < max );
}

/**
**    Clears the dirtyscreen array, making all tiles 'unmarked'
**    FIXME: is there some way to get this fast?
**           (skipping parts that are already '0' ?)
*/
static void ClearDirtyscreen(void)
{
  memset( dirtyscreen, 0, dirtyscreen_size );
}

/**
**    Initialize and allocate memory for this mechanism for a fixed screen
**    resolution (may not alter, unless DecorationInit is called again).
**    pre : VideoWidth and VideoHeight need to have the first
**          DIRTYSCREEN_BITDETAIL bits on zero.
**/
void DecorationInit(void)
{
  // Some platform dependent assumpions of the entire mechanism.
  // If failure, please fix..
  DebugCheck( sizeof(unsigned long) < 4 ); // atleast 32bit 
  DebugCheck( sizeof(unsigned int) < 2 );  // atleast 16bit 

  // The number of screen-tiles we support, a lower DIRTYSCREEN_BITDETAIL
  // means detailed smaller tiles, but also costs more memory.
  dirtyscreen_xtiles = (VideoWidth >> DIRTYSCREEN_BITDETAIL);
  dirtyscreen_ytiles = (VideoHeight >> DIRTYSCREEN_BITDETAIL);
  DebugCheck( (dirtyscreen_xtiles<<DIRTYSCREEN_BITDETAIL)!=VideoWidth ||
              (dirtyscreen_ytiles<<DIRTYSCREEN_BITDETAIL)!=VideoHeight );

  // Get memory for array dirtyscreen as 4x4bit matrixes each denoting 16
  // tiles. So (dirtyscreen_xtiles*dirtyscreen_ytiles)/16 matrixes of 2 bytes
  dirtyscreen_xbitmaps = 1 + (dirtyscreen_xtiles-1) / 4;
  dirtyscreen_ybitmaps = 1 + (dirtyscreen_ytiles-1) / 4;
  dirtyscreen_size     = dirtyscreen_xbitmaps * dirtyscreen_ybitmaps * 2;
  if ( dirtyscreen )
    dirtyscreen = realloc( dirtyscreen, dirtyscreen_size );
  else dirtyscreen = malloc( dirtyscreen_size );

  if ( !dirtyscreen )
  {
    printf( "Out of memory (InitDirtyscreen,video/deco.c)\n" );
    exit( 1 );
  }

  ClearDirtyscreen();

  { int i;
    for ( i = 0; i < LevCount; i++ )
      while ( dhead[i] != NULL )
      {
        Deco *d = dhead[i];
        dhead[i] = d->nxt;
        DecorationDelete( d );
      }
  }
}

/*
**      dirtyscreen           = the array itself
**      dirtyscreen_xtiles    = no. 2-byte tiles horizontally (screen width)
**      dirtyscreen_ytiles    = no. 2-byte tiles vertically (screen height)
*/
global void debugdirtyscreen(void)
{
  char *p;
  unsigned int x, y, xbit, ybit, mask;

  printf( "   " ); 
  for ( x=y=0; x<dirtyscreen_xtiles; y++ )
    if ( y % 5 == 0 )
      printf( " " );
    else printf("%d", x++ % 10 );
  printf( "\n" );

  for ( y=0; y<dirtyscreen_ybitmaps; y++ )
  {
    for ( ybit=0; ybit<4; ybit++ )
    {
      printf( "%3d", y*4+ybit );
      p = dirtyscreen + y * dirtyscreen_xbitmaps * 2;
      for ( x=0; x<dirtyscreen_xbitmaps; x++, p+=2 )
      {
        mask = ((p[1] << 8) | p[0]) >> (ybit*4);
        printf(" ");
        for ( xbit=1; xbit<=8; xbit <<= 1 )
          printf( "%c", (mask & xbit) ? '1' : '0' );
      }
      printf("\n");
    }
  }
}

/**
**    Marks given position on screen as 'dirty',  which can later be checked
**    to determine if something is overlapping it and to denote what needs to
**    be invalidated.
**    @param x    = x-position in pixels on screen
**    @param y    = y-position in pixels on screen
**    pre : given (x,y) should be inside screen resolution as given through
**          DecorationInit
**/
static void MarkPos( unsigned x, unsigned y )
{
  char *p;
  unsigned bits;

  // Scale (x,y) down to the tile-index as it is stored in array dirtyscreen
  x >>= DIRTYSCREEN_BITDETAIL;
  y >>= DIRTYSCREEN_BITDETAIL;

  // As the array dirtyscreen denotes each tile as an individual bit (to reduce
  // memory), we use an easy to use 4x4 bit format that fits exactly into a
  // 16bit element:    <--------x
  //                   3  2  1  0 y
  //                   7  6  5  4 |
  //                  11 10  9  8 |
  //                  15 14 13 12 v
  // As the type of the dirtyscreen elements is char, we are also sure we
  // don't waste memory as it's element size is exactly twice (16bit) the size
  // of char (8bit), where as sizeof(unsigned) can be bigger.
  // But to perform bit-operation on the 16bit element, we need to get the
  // 4x4bit matrix as type unsigned at tile-index y*dirtyscreen_xtiles+x,
  // which is translated into 16bit bitmap-index as:
  // p = dirtyscreen + ((y/4)*dirtyscreen_xbitmaps+(x/4))*2
  p    = dirtyscreen + (((y>>2)*dirtyscreen_xbitmaps+(x>>2))<<1);

  // Mark the one bit refering to the tile (x,y) in least sig. 4x4 bits
  bits = (p[1] << 8) | p[0];
  bits |= ( 1 << ((x & 0x000F) + ((y & 0x000F)<<2)) );
  p[0] = bits & 0x00FF;
  p[1] = (bits >> 8);
}

/**
**      Marks given area on screen as 'dirty',  which can later be checked
**      to determine if something is overlapping it and to denote what needs to
**      be invalidated.
**      @param x        = x-position in pixels on screen
**      @param y        = y-position in pixels on screen
**      @param width    = width in pixels on screen (> 0)
**      @param height   = height in pixels on screen (> 0)
**      pre : given (x,y;x+width-1,y+height-1) should be inside screen
**            resolution as determined by DecorationInit 
**/
void MarkArea( int x, int y, int w, int h )
{
  char *tiles;
  unsigned int xmaskhead, xmasktail, ymaskhead, ymasktail, bits;
  int w2, nextline, bitindex;

  DebugCheck( x <= 0 || y <= 0 || w <= 0 || h <= 0 ||
              (x+w) >= VideoWidth || (y+h) >= VideoHeight );

  // First scale (x,y) down to the tile-index as it is stored in array
  // dirtyscreen and let w,h denote the width/height in tiles iso pixels
  x >>= DIRTYSCREEN_BITDETAIL;
  y >>= DIRTYSCREEN_BITDETAIL;
  w = ((w - 1) >> DIRTYSCREEN_BITDETAIL) + 1;
  h = ((h - 1) >> DIRTYSCREEN_BITDETAIL) + 1;
  DebugCheck( w > dirtyscreen_xtiles || h > dirtyscreen_ytiles );

  // Reference to top-left 4x4bit matrix containing tile (x,y) in dirtyscreen
  tiles = dirtyscreen + (((y>>2)*dirtyscreen_xbitmaps+(x>>2))<<1);

  // Now scale (w,h) down to the number of 16bit elements (in a 4x4 bit matrix)
  // to check and denote when we need to check the four sides with a bitmask
  // or just check wether the 4x4 matrix(es) should be entirely zero.
  bitindex = (x & 0x3);
  xmaskhead = xbitmaskhead[ bitindex ];
  xmasktail = xbitmasktail[ (x+w) & 0x3 ];
  if ( w < 4 && w <= 4 - bitindex )
  { // xmaskhead and xmasktail in same 4x4 matrix column  --> combine to one
    if ( x >= dirtyscreen_xtiles - 4 ) // at rightmost side of screen
    { // move one 4x4 matrix to the left to prevent acces outside 2D dimension
      tiles  -= 4 * 2;
      xmasktail &= xmaskhead;
      xmaskhead = 0;
    }
    else
    {
      xmaskhead &= xmasktail;
      xmasktail = 0;
    }
  }
  bitindex  = (y & 0x3);
  ymaskhead = ybitmaskhead[ bitindex ];
  ymasktail = ybitmasktail[ (y+h) & 0x3 ];
  if ( h < 4 && h <= 4 - bitindex )
  { // ymaskhead and ymasktail in same 4x4 matrix row  --> combine to one
    if ( y >= dirtyscreen_ytiles - 4 ) // at bottom side of screen
    { // move one 4x4 matrix upwards to prevent acces outside 2D dimension
      tiles  -= 2 * dirtyscreen_xbitmaps;
      ymasktail &= ymaskhead;
      ymaskhead = 0;
    }
    else
    {
      ymaskhead &= ymasktail;
      ymasktail  = 0;
    }
  }

  //
  // Mark the tiles with above bitmasks..
  //
  nextline=(dirtyscreen_xbitmaps-(w>>2))*2;
  w-=2;
  h-=2;

  // upper-left 4x4 matrixes
  bits = (tiles[1] << 8) | tiles[0];
  bits |= (ymaskhead&xmaskhead);
  tiles[0] = bits & 0x00FF;
  tiles[1] = bits & 0xFF00;

  // upper-middle 4x4 matrixes
  w2=2;
  while (  tiles+=2, w2-- > 0 )
  {
    bits = (tiles[1] << 8) | tiles[0];
    bits |= ymaskhead;
    tiles[0] = bits & 0x00FF;
    tiles[1] = bits & 0xFF00;
  }

  // upper-right 4x4 matrix
  bits = (tiles[1] << 8) | tiles[0];
  bits |= (ymaskhead&xmasktail);
  tiles[0] = bits & 0x00FF;
  tiles[1] = bits & 0xFF00;

  h--;

  // middle 4x4 matrixes
  while (  tiles+=nextline, h-- > 0 )
  {
    // left-middle 4x4 matrix
    bits = (tiles[1] << 8) | tiles[0];
    bits |= xmaskhead;
    tiles[0] = bits & 0x00FF;
    tiles[1] = bits & 0xFF00;

    // middle 4x4 matrixes
    w2 = w;
    while (  tiles+=2, w2-- > 0 )
    {
      tiles[0] |= 0xFF;
      tiles[1] |= 0xFF;
    }

    // right-middle 4x4 matrix
    bits = (tiles[1] << 8) | tiles[0];
    bits |= xmasktail;
    tiles[0] = bits & 0x00FF;
    tiles[1] = bits & 0xFF00;
  }

  // lower-left 4x4 matrix
  bits = (tiles[1] << 8) | tiles[0];
  bits |= (ymasktail&xmaskhead);
  tiles[0] = bits & 0x00FF;
  tiles[1] = bits & 0xFF00;

  // lower-middle 4x4 matrixes
  w2 = w;
  while (  tiles+=2, w2-- > 0 )
  {
    bits = (tiles[1] << 8) | tiles[0];
    bits |= ymasktail;
    tiles[0] = bits & 0x00FF;
    tiles[1] = bits & 0xFF00;
  }

  // lower-right 4x4 matrix
  bits = (tiles[1] << 8) | tiles[0];
  bits |= (ymasktail&xmasktail);
  tiles[0] = bits & 0x00FF;
  tiles[1] = bits & 0xFF00;
}

/**
**    Draws within a given area by setting up a proper clip rectangle and
**    calling the user-defined drawclip routine with its data.
**
**    @param x    = x pixel position on screen of left-top
**    @param y    = y pixel position on screen of left-top
**    @param data = any type of object that needs to be drawn, this
**                  file will not need to know what it is.
**    @param drawclip   = function that can draw above data using draw rountines
**                  that can restrict drawing to a clip rectangle.
**/
static void DrawArea( int x, int y, int w, int h,
                      void *data, void (*drawclip)(void *data) )
{
  SetClipping( x, y, x+w-1, y+h-1 );
  drawclip( data );
}

/**
**      Checks if any pixel in area on screen overlapped by this Decoration is
**    marked 'dirty' and if so.. will redraw only those tiles again.
**
**      @param d   = valid decoration
**      @param t   = one of the single-tile decorations of above
**/
static void CheckRedraw( Deco *d, DecorationSingle *t )
{
  char *top, *bottom;
  unsigned long topbits, bottombits, leftbits, rightbits, bits;

  top    = t->tiles;
  bottom = top + dirtyscreen_xbitmaps * 2;

  // Get left-top and -bottom 16bit 4x4 matrixes, masked to get the 'dirty'
  // area overlapped by this decoration in a single 32bit back to 16bit
  topbits     = (top[1] << 8) | top[0];
  topbits    &= t->lefttopmask;
  bottombits  = (bottom[1] << 8) | bottom[0];
  bottombits &= t->leftbottommask;
  leftbits    = (bottombits << 16) | topbits;
  leftbits  >>= t->bity4;
  leftbits = ((leftbits & 0xF000) << 12)
           | ((leftbits & 0x0F00) <<  8)
           | ((leftbits & 0x00F0) <<  4)
           |  (leftbits & 0x000F);

  // Get right-top and -bottom 16bit 4x4 matrixes, masked to get the 'dirty'
  // area overlapped by this decoration in a single 32bit back to 16bit
  topbits     = (top[3] << 8) | top[2];
  topbits    &= t->righttopmask;
  bottombits  = (bottom[3] << 8) | bottom[2];
  bottombits &= t->rightbottommask;
  rightbits   = (bottombits << 16) | topbits;
  rightbits >>= t->bity4;
  rightbits = ((rightbits & 0xF000) << 16)
            | ((rightbits & 0x0F00) << 12)
            | ((rightbits & 0x00F0) <<  8)
            | ((rightbits & 0x000F) <<  4);

  // Now combine both left+right 16bit into one 32bit
  bits = rightbits | leftbits;

  // Check this 32bit as a 8x4 tile area
  // FIXME: try merging neighbouring 'dirty' bits, to minimize DrawIt calls
  if ( bits )
  {
    int x, y;
    x = t->topleftx;
    y = t->toplefty;
    bits >>= t->bitx;
    do
    {
      if ( bits & 0x1 ) // bit 0 is 'dirty'
        DrawArea( x, y, DIRTYSCREEN_DETAILSIZE, DIRTYSCREEN_DETAILSIZE,
                  d->data, d->drawclip );

      if ( bits & 0x2 ) // bit 1 is 'dirty'
        DrawArea( x+DIRTYSCREEN_DETAILSIZE, y,
                  DIRTYSCREEN_DETAILSIZE, DIRTYSCREEN_DETAILSIZE,
                  d->data, d->drawclip );

      if ( bits & 0x4 ) // bit 2 is 'dirty'
        DrawArea( x+2*DIRTYSCREEN_DETAILSIZE, y,
                  DIRTYSCREEN_DETAILSIZE, DIRTYSCREEN_DETAILSIZE,
                  d->data, d->drawclip );

      if ( bits & 0x8 ) // bit 3 is 'dirty'
        DrawArea( x+3*DIRTYSCREEN_DETAILSIZE, y,
                  DIRTYSCREEN_DETAILSIZE, DIRTYSCREEN_DETAILSIZE,
                  d->data, d->drawclip );

      y += DIRTYSCREEN_DETAILSIZE;
      bits >>= 8; // next line of 8 bits
    } while ( bits );
  }
}

/*
**    FOR DEBUG PURPOSE ONLY
**    Will print the given 16bit as 4x4 tiles.
*/
global debugdecobits( unsigned int bits )
{
  int y;
  printf( "16bits as 4x4: 3210\n" );
  for ( y=0; y<=3; y++ )
  {
    printf( "             %d %c%c%c%c\n", y,
            bits&8?'1':'0', bits&4?'1':'0',
            bits&2?'1':'0', bits&1?'1':'0' );
    bits >>= 4;
  }
}

/*
**    Convert 8x8 tiles from 4 16bit masks into 8x4 tiles in one 32bit,
**    moving the set bits up with given bitshift in y-direction.
**    For this to work only a 4x4 area within above 8x8 tiles might be set.
**
**    @param ybitshift       = a (full) bitshift to move bits to upper tile
**    @param topleftmask     = 16bit bitmask for top-left     4x4 tiles
**    @param toprightmask    = 16bit bitmask for top-right    4x4 tiles
**    @param bottomleftmas k = 16bit bitmask for bottom-left  4x4 tiles
**    @param bottomrightmask = 16bit bitmask for bottom-right 4x4 tiles
*/
local unsigned long convert8x8to8x4(
  int ybitshift,
  unsigned int topleftmask,    unsigned int toprightmask,
  unsigned int bottomleftmask, unsigned int bottomrightmask )
{
  unsigned long leftmask, rightmask;

  leftmask   = (unsigned long)bottomleftmask << 16;
  leftmask  |= topleftmask;
  leftmask >>= ybitshift;

  leftmask = ((leftmask & 0xF000) << 12)
           | ((leftmask & 0x0F00) <<  8)
           | ((leftmask & 0x00F0) <<  4)
           |  (leftmask & 0x000F);

  rightmask   = (unsigned long)bottomrightmask << 16;
  rightmask  |= toprightmask;
  rightmask >>= ybitshift;

  rightmask = ((rightmask & 0xF000) << 16)
            | ((rightmask & 0x0F00) << 12)
            | ((rightmask & 0x00F0) <<  8)
            | ((rightmask & 0x000F) <<  4);

  return leftmask | rightmask;
}

/*
**    FOR DEBUG PURPOSE ONLY
**    Will print the given t as separate bitvalues.
*/
global debugsinglebits( DecorationSingle *t )
{
  char *p;
  unsigned long topleftscreen, toprightscreen, bottomleftscreen, 
                bottomrightscreen, leftbits, rightbits, bits32, mark32;
  int y, x;

// 2x2 16bit matrixes for 8x8 tiles translated to 8x4 tiles in one 32bit
  bits32 = convert8x8to8x4( t->bity4, t->lefttopmask,    t->righttopmask,
                                      t->leftbottommask, t->rightbottommask );

// now the same for marked bits
  p = t->tiles;
  topleftscreen  = (p[1] << 8) | p[0];
  toprightscreen = (p[3] << 8) | p[2];
  p = t->tiles + dirtyscreen_xbitmaps * 2;
  bottomleftscreen  = (p[1] << 8) | p[0];
  bottomrightscreen = (p[3] << 8) | p[2];
  mark32 = convert8x8to8x4( t->bity4, topleftscreen     & t->lefttopmask,
                                      toprightscreen    & t->righttopmask,
                                      bottomleftscreen  & t->leftbottommask,
                                      bottomrightscreen & t->rightbottommask );

// print 8x8 and 9x4 tiles representations
  printf( "DecorationSingle as 8x8 tiles\n" );
  printf( "     76543210 76543210 76543210  76543210\n     ");
  for ( x=7; t->bitx<x; x-- )
    printf( " " );
  printf( "^\n" );
  leftbits  = t->lefttopmask;
  rightbits = t->righttopmask;
  for ( y=0; y<=7; y++ )
  {
    printf( "%s%d ", (y * 4 == t->bity4) ? "-->" : "   ", y );

    for ( x=8; x; x>>=1 )
      printf( "%c", toprightscreen & x ? '1' : '0' );
    for ( x=8; x; x>>=1 )
      printf( "%c", topleftscreen & x ? '1' : '0' );

    printf( " " );
    for ( x=8; x; x>>=1 )
      printf( "%c", rightbits & x ? '1' : '0' );
    for ( x=8; x; x>>=1 )
      printf( "%c", leftbits & x ? '1' : '0' );

    printf( " " );
    for ( x=8; x; x>>=1 )
      printf( "%c", rightbits & toprightscreen & x ? '1' : '0' );
    for ( x=8; x; x>>=1 )
      printf( "%c", leftbits & topleftscreen & x ? '1' : '0' );

    topleftscreen  >>= 4;
    toprightscreen >>= 4;
    leftbits       >>= 4;
    rightbits      >>= 4;

    if ( y < 4 )
    {
      printf( "  " );
      for ( x=128; x; x>>=1 )
        printf( "%c", bits32 & x ? '1' : '0' );
      bits32  >>= 8;
      printf( "  " );
      for ( x=128; x; x>>=1 )
        printf( "%c", mark32 & x ? '1' : '0' );
      mark32  >>= 8;
    }

    printf( "\n" );
    if ( y == 3 )
    {
      leftbits  = t->leftbottommask;
      rightbits = t->rightbottommask;
      topleftscreen  = bottomleftscreen;
      toprightscreen = bottomrightscreen;
    }
  }
  printf( "     (screen)&( deco )=(redraw)  ( deco )  (redraw)\n");
}

/**
**    Will mark a DecorationSingle (restricted to 4x4 tiles)
**
**    @param t = one DecorationSingle (will not follow its filed nxt!)
**/
static void DecorationSingleMark( DecorationSingle *t )
{
  char *p;
  unsigned long bits;

  // Mark left-top 16bit 4x4 matrix with area overlapped by this decoration
  p = t->tiles;
  bits  = (p[1] << 8) | p[0];
  bits |= t->lefttopmask;
  p[0] = bits & 0x00FF;
  p[1] = (bits >> 8);

  // Mark right-top 16bit 4x4 matrix with area overlapped by this decoration
  bits  = (p[3] << 8) | p[2];
  bits |= t->righttopmask;
  p[2] = bits & 0x00FF;
  p[3] = (bits >> 8);

  // Mark left-bottom 16bit 4x4 matrix with area overlapped by this decoration
  p += dirtyscreen_xbitmaps * 2;
  bits  = (p[1] << 8) | p[0];
  bits |= t->leftbottommask;
  p[0] = bits & 0x00FF;
  p[1] = (bits >> 8);

  // Mark right-bottom 16bit 4x4 matrix with area overlapped by this decoration
  bits  = (p[3] << 8) | p[2];
  bits |= t->rightbottommask;
  p[2] = bits & 0x00FF;
  p[3] = (bits >> 8);
}

/**
**    Will mark a complete Decoration (maybe multiple DecorationSingle)
**
**    @param d = Deco to completely mark
**/
void DecorationMark( Deco *d )
{
  DecorationSingle *t;
  for ( t = d->singles; t; t = t->nxt )
    DecorationSingleMark( t );
}

/**
**    Add a single decoration restricted by size so it covers max 4x4 tiles.
**
**    @param x    = x pixel position on screen of left-top
**    @param y    = y pixel position on screen of left-top
**    @param w    = width in pixels of area to be drawn from (x,y)
**    @param h    = height in pixels of area to be drawn from (x,y)
**/
static DecorationSingle *DecorationSingleNew( unsigned x, unsigned y,
                                              unsigned w, unsigned h )
{
  DecorationSingle *t;
  unsigned bitindex, xmaskhead, xmasktail, ymaskhead, ymasktail;

  DebugCheck( x < 0 || y < 0 || w <= 0 || h <= 0 ||
              (x+w) >= VideoWidth || (y+h) >= VideoHeight );

  // Fill in this new Decoration so it can be used
  t = DecorationSingleAllocate();
  t->topleftx = x;
  t->toplefty = y;

  // Instead of storing given (x,y,w,h), we use prepared pointer and bitmasks
  // to the dirtyscreen array, as these are fast and can be used again..
  //
  // First scale (x,y) down to the tile-index as it is stored in array
  // dirtyscreen and let w,h denote the width/height in tiles iso pixels
  x >>= DIRTYSCREEN_BITDETAIL;
  y >>= DIRTYSCREEN_BITDETAIL;
  w = ((w - 1) >> DIRTYSCREEN_BITDETAIL) + 1;
  h = ((h - 1) >> DIRTYSCREEN_BITDETAIL) + 1;
  DebugCheck( w > DECOSINGLE_TILES || h > DECOSINGLE_TILES );

  // Reference to top-left 4x4bit matrix containing tile (x,y) in dirtyscreen
  t->tiles = dirtyscreen + (((y>>2)*dirtyscreen_xbitmaps+(x>>2))<<1);

  // Now scale (w,h) down to the number of 16bit elements (in a 4x4 bit matrix)
  // to check and denote when we need to check the four sides with a bitmask
  // or just check wether the 4x4 matrix(es) should be entirely zero.
  t->bitx  = bitindex = (x & 0x3);
  xmaskhead = xbitmaskhead[ bitindex ];
  xmasktail = xbitmasktail[ (x+w) & 0x3 ];
  if ( w < 4 && w <= 4 - bitindex )
  { // xmaskhead and xmasktail in same 4x4 matrix column  --> combine to one
    if ( x >= dirtyscreen_xtiles - 4 ) // at rightmost side of screen
    { // move one 4x4 matrix to the left to prevent acces outside 2D dimension
      t->tiles  -= 4 * 2;
      xmasktail &= xmaskhead;
      xmaskhead = 0;
    }
    else
    {
      xmaskhead &= xmasktail;
      xmasktail = 0;
    }
  }
  bitindex  = (y & 0x3);
  t->bity4  = (bitindex * 4);
  ymaskhead = ybitmaskhead[ bitindex ];
  ymasktail = ybitmasktail[ (y+h) & 0x3 ];
  if ( h < 4 && h <= 4 - bitindex )
  { // ymaskhead and ymasktail in same 4x4 matrix row  --> combine to one
    if ( y >= dirtyscreen_ytiles - 4 ) // at bottom side of screen
    { // move one 4x4 matrix upwards to prevent acces outside 2D dimension
      t->tiles  -= 2 * dirtyscreen_xbitmaps;
      ymasktail &= ymaskhead;
      ymaskhead = 0;
    }
    else
    {
      ymaskhead &= ymasktail;
      ymasktail  = 0;
    }
  }

  // Check is this 'single' really is restricted to a 2x2 16bit area
  DebugCheck( (((w-1)>>2) + 1) > 2 || (((h-1)>>2) + 1) > 2 );

  // now using above head+tail masks, combine them to get 2x2 16bit masks
  t->lefttopmask     = xmaskhead & ymaskhead;
  t->righttopmask    = xmasktail & ymaskhead;
  t->leftbottommask  = xmaskhead & ymasktail;
  t->rightbottommask = xmasktail & ymasktail;

  return t;
}

/**
**    Add a new decoration for something that needs to be drawn on the
**    screen. You only need to supply the given information, after which the
**    mechanism itself determines (on the moment of a video refresh) what is
**    to be drawn.
**    When the object that the decoration represents changes in time, you
**    need to call the proepr mark directly function to denote this.
**    When the decoration is no longer needed, simply call DecorationDelete..
**
**    @param data = any type of object that needs to be drawn, this
**                  mechanism will not need to know what it is.
**    @param drawclip   = function that can draw above data using draw rountines
**                  that can restrict drawing to a clip rectangle.
**    @param l    = depth-level; order in which de decoration is drawn
**    @param x    = x pixel position on screen of left-top
**    @param y    = y pixel position on screen of left-top
**    @param w    = width in pixels of area to be drawn from (x,y)
**    @param h    = height in pixels of area to be drawn from (x,y)
**/
Deco *DecorationAdd( void *data,
                     void (*drawclip)(void *data),
                     DecorationLevel l, 
                     unsigned x, unsigned y,
                     unsigned w, unsigned h )
{
  DecorationSingle **prevt;
  Deco *list, *d, *prv, **pprv;

  // Allocate and fill in this new DecorationType so it can be used
  d = DecorationAllocate();
  d->drawclip  = drawclip;
  d->data      = data;
  d->l         = l;
  d->x         = x;
  d->y         = y;
  d->w         = w;
  d->h         = h;

  // Restrict to screen (keeping original total location in d for check later)
  if( x<0 ) {
    int ofs=-x;
    if( w<=ofs ) {
      DecorationDelete( d );
      return NULL;
    }
    x=0;
    w-=ofs;
  }
  if( (x+w)>VideoWidth ) {
    if( x>=VideoWidth ) {
      DecorationDelete( d );
      return NULL;
    }
    w=VideoWidth-x;
  }
  if( y<0 ) {
    int ofs=-y;
    if( h<=ofs ) {
      DecorationDelete( d );
      return NULL;
    }
    y=0;
    h-=ofs;
  }
  if( (y+h)>VideoHeight ) {
    if( y>=VideoHeight ) {
      DecorationDelete( d );
      return NULL;
    }
    h=VideoHeight-y;
  }
  DebugCheck( x < 0 || y < 0 || w <= 0 || h <= 0 ||
              (x+w) > VideoWidth || (y+h) > VideoHeight );
  

  // Find entry for this decoration ordered on z(l):y:x and add it
  // @note we only need z-level really, but also do y:x to be able to draw
  // decorations of same z-levle on top of eachother as compatible with original
  // FIXME: use a smarter more faster method
  prv  = NULL;
  pprv = dhead + l;
  while ( (list = *pprv) && (list->y < y || (list->y == y && list->x < x) ) )
  {
    prv  = list;
    pprv = &list->nxt;
  }
  *pprv = d;
  d->prv = prv;
  d->nxt = list;
  if ( list )
    list->prv = d;

  // Split given area up into multiple Decorations of DIRTYSCREEN_DETAILSIZE
  // FIXME: can be done faster, or maybe we should do without?
  prevt = &d->singles;
  while ( h > DECOSINGLE_PIXELS )
  {
    int x2 = x;
    int w2 = w;
    while ( w2 > DECOSINGLE_PIXELS )
    {
      *prevt = DecorationSingleNew( x2, y,
                                    DECOSINGLE_PIXELS, DECOSINGLE_PIXELS );
      prevt = &(*prevt)->nxt;
      x2 += DECOSINGLE_PIXELS;
      w2 -= DECOSINGLE_PIXELS;
    }
    *prevt = DecorationSingleNew( x2, y, w2, DECOSINGLE_PIXELS );
    prevt = &(*prevt)->nxt;
    y += DECOSINGLE_PIXELS;
    h -= DECOSINGLE_PIXELS;
  }
  while ( w > DECOSINGLE_PIXELS )
  {
    *prevt = DecorationSingleNew( x, y, DECOSINGLE_PIXELS, h );
    prevt = &(*prevt)->nxt;
    x += DECOSINGLE_PIXELS;
    w -= DECOSINGLE_PIXELS;
  }
  *prevt = DecorationSingleNew( x, y, w, h );
  (*prevt)->nxt = NULL;

  // As this is a new Decoration to be put onto the screen, we mark its area
  DecorationMark(d);

  return d;
}

/**
**    Invalidate atleast that part of the screen that is marked dirty.
**    (note that not the minimum area, but a somehwat bigger area can be
**     invalidated for efficiency reasons).
**/
static void InvalidateDirtyscreen(void)
{
  char *p;
  unsigned bits, pixelx, pixely, pixelstep, dirtylinesize, xcount, ycount;

  dirtylinesize = dirtyscreen_xbitmaps*2;
  ycount        = dirtyscreen_ybitmaps*2;
  pixelstep     = 4 << DIRTYSCREEN_BITDETAIL;
  p             = dirtyscreen;
  pixely        = 0;
  do
  {
    pixelx = 0;
    xcount = dirtylinesize;

  // First add all horizontal segments found on current dirtyline
    do
    {
      bits    = (p[1] << 8) | p[0];
      p      += 2;
      xcount--;

      if ( bits ) // one or more bits in 4x4 matrix set
      {
        int leftx = pixelx;
      // skip non-zero bits
        while ( xcount > 0 && *((unsigned *)p) )
        {
          p      += 2;
          pixelx += pixelstep;
          xcount--;
        }
        SweeplineAdd( leftx, pixelx+pixelstep-1, pixely );
      }
      pixelx += pixelstep;
    }
    while ( xcount > 0 );

    pixely += pixelstep;

  // Now check if some segments exist too long and needs invalidate
    SweeplineInvalidate( pixely );
  }
  while ( --ycount > 0 );

// Invalidate remaining rectangles, which have their shadow beyond VideoHeight
  SweeplineInvalidateAll();
}

/**
**    Draw all decorations, independently if they cover dirty pixels or not.
**    To be called only to redraw entire screen (like in the beginning),
**    or when the DecorationUpdateDisplay operates to slow.
**    FIXME: (which might
**    automaticly detected by checking the function-duration ?)
**
**/
void DecorationRefreshDisplay(void)
{
  Deco *d;
  int i;

// save clip rectangle
  PushClipping();

// Handle each decoration (not the singles)
  for ( i = 0; i < LevCount; i++ )
    for ( d = dhead[i]; d; d = d->nxt )
      DrawArea( d->x, d->y, d->w, d->h, d->data, d->drawclip );

  Invalidate();

// reset dirty array, to remember new updates for next refresh
  ClearDirtyscreen();

// restore clip rectangle
  PopClipping();
}

/**
**    Draw only those decorations which cover dirty pixels.
**    Cleaning those pixels again for a next time.
**    FIXME: when this operates slower then a normal DecorationRefreshDisplay,
**           the DecorationRefreshDisplay needs to be called. This can be
**           by measuring both function durations..
**/
void DecorationUpdateDisplay(void)
{
  DecorationSingle *t;
  Deco *d;
  int i;

// save clip rectangle
  PushClipping();

// Handle each decoration-single separately
  for ( i = 0; i < LevCount; i++ )
    for ( d = dhead[i]; d; d = d->nxt )
      for ( t = d->singles; t; t = t->nxt )
        CheckRedraw( d, t );

// FIXME: use followin function instead for speed.. never tried out though
//  InvalidateDirtyscreen();
  Invalidate();

// reset dirty array, to remember new updates for next refresh
  ClearDirtyscreen();

// restore clip rectangle
  PopClipping();
}

#endif

//@}

Generated by  Doxygen 1.6.0   Back to index