/*
** $VER: ImageBackFill.c 1.231 (19.3.95)
**
** Tests backfill hooks in screens
**
** Make>> smake
**
** (W) 1992-95 by Pierre Carrette & Walter Dörwald
*/

//#define DEBUG
#include <debug.h>
#include "mp.h"
#include "ImageBackFill.h"
#include "backfill.h"

struct Hook *Image_AllocBackfillHook(struct Screen *Scr, struct DefaultNode *DN)
{
  struct BackFillOptions bfo;
  struct BackFillInfo *b;
  
  if(DN->BackdropFile && DataTypesBase)
  {
    if(b=AllocVec(sizeof(*b),MEMF_CLEAR|MEMF_PUBLIC))
    {
      bfo.MaxCopyWidth=
      bfo.MaxCopyWidth=320;
      bfo.CenterX=
      bfo.CenterY=(DN->BackdropFlags & BF_CENTER);
      bfo.OffsetX=0;
      bfo.OffsetY=0;
      bfo.OffsetTitleY=0;
               
      if(LoadBackgroundImage(b,DN->BackdropFile,Scr,&bfo))
      {
        return((struct Hook *)b);
      }
      FreeVec(b);
    }
  }
  return(0);
}

void Image_FreeBackfillHook(struct Hook *Hook)
{
  struct BackFillInfo *b;

  b=(struct BackFillInfo *)Hook;

//  if(b->bd_Loaded)
  {
    UnloadBackgroundImage(b);
  }
  FreeVec(b);
}
      
// this seems to be a bug (or feature) in SAS/C: -6%5 gives -1 instead of 4
#define MOD(x,y) ((x)<0 ? (y)-((-(x))%(y)) : (x)%(y))

#define RECTSIZEX(r) ((r)->MaxX-(r)->MinX+1)
#define RECTSIZEY(r) ((r)->MaxY-(r)->MinY+1)

/****** CopyTiledBitMap ****************************************************************************
*
*   NAME
*	CopyTiledBitMap() -- fill bitmap with bitmap pattern
*
*   SYNOPSIS
*	CopyTiledBitMap(Src,SrcOffsetX,SrcOffsetY,SrcSizeX,SrcSizeY,Dst,DstBounds)

*	void CopyTiledBitMap(struct BitMap *,WORD,WORD,WORD,WORD,struct BitMap *,struct Rectangle *)
*
*   FUNCTION
*	fills the destination bitmap with repeated (tiled) copies of the source bitmap
*
*   INPUTS
*	Src = source bitmap.
*	SrcOffsetX = offset (in x direction) to use in the blit.
*	SrcOffsetY = offset (in y direction) to use in the blit.
*	             The pixel at position (SrcOffsetX,SrcOffsetY) in the source bitmap
*	             will be the first visible one in the upper left corner of DstBounds.
*	SrcSizeX = width of the source bitmap Src.
*	SrcSizeY = height of the source bitmap Src.
*	Dst = destination bitmap.
*	DstBounds = this rectangle in the destination bitmap will be filled.
*
*   RESULT
*	None.
*
*   EXAMPLE
*	This function is most useful in backfill hooks.
*
*   NOTES
*	The following relations must hold:
*		0 <= SrcOffsetX < SrcSizeX
*		0 <= SrcOffsetY < SrcSizeY
*	I.e. if calculating the offset involves some kind of modulo-operations
*	they have to be done before calling this function
*
*   BUGS
*
*   AUTHOR
*	Copyright © 1995 by Walter Dörwald
*
*   SEE ALSO
*
****************************************************************************************************
*
*	This function uses an "exponential" blit to fill the dest rect.
*	Suppose we have a 64×64 source bitmap and want to use offset 0,0
*	(i.e. the upper left corner of the source bitmap is copied to the
*	upper left corner of the dest rect. Let the dest rect be 320×320
*	with upper left corner (0,0). Thus we'll blit 5×5 copies of the
*	source bitmap to the destination.
*	Doing this with two nested loops would thus require 25 blits, or
*	x·y blits for x horizontal tiles and y vertical tiles.
*	First one copy of the source bitmap is blitted to the upper left
*	corner of the dest rect:
*
*		#....
*		.....
*		.....
*		.....
*		.....
*
*	This copy is then blitted to the right:
*
*		##...
*		.....
*		.....
*		.....
*		.....
*
*	*Both* copies are once again blitted to the right
*
*		####.
*		.....
*		.....
*		.....
*		.....
*
*	Finally the first row is filled with one blit:
*
*		#####
*		.....
*		.....
*		.....
*		.....
*
*	Now the same is done vertically to fill the complete rect:
*
*		#####   #####   #####
*		#####   #####   #####
*		.....   #####   #####
*		.....   #####   #####
*		.....   .....   #####
*
*	This requires 7 blit, or generally 1+ceil(log_2(x))+ceil(log_2(y))
*
*	The blits are done first in x direction and then in y direction
*	to minimize potential masking effects at the left and right border.
*
*	If the offsets into the source bitmap are not 0, part of a second tile
*	is blitted to the destination bitmap to once again get a tile of the
*	required size.
*/

void CopyTiledBitMap(struct BitMap *Src,WORD SrcOffsetX,WORD SrcOffsetY,WORD SrcSizeX,WORD SrcSizeY,struct BitMap *Dst,struct Rectangle *DstBounds)
{
	WORD FirstSizeX;  // the width of the rectangle to blit as the first column
	WORD FirstSizeY;  // the height of the rectangle to blit as the first row
	WORD SecondMinX;  // the left edge of the second column
	WORD SecondMinY;  // the top edge of the second column
	WORD SecondSizeX; // the width of the second column
	WORD SecondSizeY; // the height of the second column
	WORD Pos;         // used as starting position in the "exponential" blit
	WORD Size;        // used as bitmap size in the "exponential" blit

	FirstSizeX = MIN(SrcSizeX-SrcOffsetX,RECTSIZEX(DstBounds)); // the width of the first tile, this is either the rest of the tile right to SrcOffsetX or the width of the dest rect, if the rect is narrow
	SecondMinX = DstBounds->MinX+FirstSizeX; // the start for the second tile (if used)
	SecondSizeX = MIN(SrcOffsetX,DstBounds->MaxX-SecondMinX+1); // the width of the second tile (we want the whole tile to be SrcSizeX pixels wide, if we use SrcSizeX-SrcOffsetX pixels for the left part we'll use SrcOffsetX for the right part)

	FirstSizeY = MIN(SrcSizeY-SrcOffsetY,RECTSIZEY(DstBounds)); // the same values are calculated for y direction
	SecondMinY = DstBounds->MinY+FirstSizeY;
	SecondSizeY = MIN(SrcOffsetY,DstBounds->MaxY-SecondMinY+1);

	BltBitMap(Src,SrcOffsetX,SrcOffsetY,Dst,DstBounds->MinX,DstBounds->MinY,FirstSizeX,FirstSizeY,0xC0,-1,NULL); // blit the first piece of the tile
	if (SecondSizeX>0) // if SrcOffset was 0 or the dest rect was to narrow, we won't need a second column
		BltBitMap(Src,0,SrcOffsetY,Dst,SecondMinX,DstBounds->MinY,SecondSizeX,FirstSizeY,0xC0,-1,NULL);
	if (SecondSizeY>0) // is a second row necessary?
	{
		BltBitMap(Src,SrcOffsetX,0,Dst,DstBounds->MinX,SecondMinY,FirstSizeX,SecondSizeY,0xC0,-1,NULL);
		if (SecondSizeX>0)
			BltBitMap(Src,0,0,Dst,SecondMinX,SecondMinY,SecondSizeX,SecondSizeY,0xC0,-1,NULL);
	}

	// this loop generates the first row of the tiles
	for (Pos = DstBounds->MinX+SrcSizeX,Size = MIN(SrcSizeX,DstBounds->MaxX-Pos+1);Pos<=DstBounds->MaxX;)
	{
		BltBitMap(Dst,DstBounds->MinX,DstBounds->MinY,Dst,Pos,DstBounds->MinY,Size,MIN(SrcSizeY,RECTSIZEY(DstBounds)),0xC0,-1,NULL);
		Pos += Size;
		Size = MIN(Size<<1,DstBounds->MaxX-Pos+1);
	}

	// this loop blit the first row down several times to fill the whole dest rect
	for (Pos = DstBounds->MinY+SrcSizeY,Size = MIN(SrcSizeY,DstBounds->MaxY-Pos+1);Pos<=DstBounds->MaxY;)
	{
		BltBitMap(Dst,DstBounds->MinX,DstBounds->MinY,Dst,DstBounds->MinX,Pos,RECTSIZEX(DstBounds),Size,0xC0,-1,NULL);
		Pos += Size;
		Size = MIN(Size<<1,DstBounds->MaxY-Pos+1);
	}
}

// This function must be compiled without stack checking
static void __saveds __asm WindowPatternBackFillFunc(register __a0 struct Hook *Hook,register __a2 struct RastPort *RP,register __a1 struct BackFillMsg *BFM)
{
	WORD OffsetX; // the offset within the tile in x direction
	WORD OffsetY; // the offset within the tile in y direction

	struct BackFillInfo *BFI = (struct BackFillInfo *)Hook; // get the data for our backfillhook (but __saveds is nonetheless required because this function needs GfxBase)


	OffsetX = BFM->Bounds.MinX-BFI->Options.OffsetX; // The first tile normally isn't totally visible => calculate the offset (offset 0 would mean that the left edge of the damage rectangle coincides with the left edge of a tile)
	if (BFI->Options.CenterX) // horizontal centering?
		OffsetX -= (BFI->Screen->Width-BFI->BitMapHeader->bmh_Width)/2;

	OffsetY = BFM->Bounds.MinY-BFI->Options.OffsetY; // The same values are calculated for y direction
	if (BFI->Options.OffsetTitleY) // shift the tiles down?
		OffsetY -= BFI->Screen->BarHeight+1;
	if (BFI->Options.CenterY) // horizontal centering?
		OffsetY -= (BFI->Screen->Height-BFI->BitMapHeader->bmh_Height)/2;

	CopyTiledBitMap(BFI->BitMap,MOD(OffsetX,BFI->BitMapHeader->bmh_Width),MOD(OffsetY,BFI->BitMapHeader->bmh_Height),BFI->CopyWidth,BFI->CopyHeight,RP->BitMap,&BFM->Bounds);
}

// this function calculates the sizes to be used for the copy of the bitmap
static void CalculateCopySizes(struct BackFillInfo *BFI)
{
	BFI->CopyWidth  = (BFI->BitMapHeader->bmh_Width>BFI->Options.MaxCopyWidth)   ? BFI->BitMapHeader->bmh_Width  : BFI->Options.MaxCopyWidth -BFI->Options.MaxCopyWidth%BFI->BitMapHeader->bmh_Width;
	BFI->CopyHeight = (BFI->BitMapHeader->bmh_Height>BFI->Options.MaxCopyHeight) ? BFI->BitMapHeader->bmh_Height : BFI->Options.MaxCopyHeight-BFI->Options.MaxCopyHeight%BFI->BitMapHeader->bmh_Height;
}

BOOL LoadBackgroundImage(struct BackFillInfo *BFI,STRPTR PicName,struct Screen *Scr,struct BackFillOptions *BFO)
{
  LONG stc,depth; // screen true color
  
	if (DataTypesBase) // safety check, if not used with V39++
	{
		memset(BFI,0,sizeof(*BFI));
		BFI->Hook.h_Entry = (ULONG (*)())WindowPatternBackFillFunc;
		BFI->Screen = Scr;
		BFI->Options = *BFO;

    depth=GetBitMapAttr(Scr->RastPort.BitMap,BMA_DEPTH);

    stc=(depth>8 ? 1 : 0);

//    DKP("Depth=%ld  %ld\n",depth,stc);

		if (BFI->PictureObject = NewDTObject(PicName,
		                                     DTA_SourceType       ,DTST_FILE,
		                                     DTA_GroupID          ,GID_PICTURE,
		                                     PDTA_Remap           ,(stc ? FALSE : TRUE),
		                                     PDTA_Screen          ,Scr,
		                                     PDTA_FreeSourceBitMap,TRUE,
                                         
                                         PDTA_DestMode,        (stc ? PMODE_V43 : PMODE_V42),
                                         
                                         PDTA_UseFriendBitMap ,1,
		                                     OBP_Precision        ,PRECISION_EXACT,
		                                     TAG_DONE))
		{
			if (DoMethod(BFI->PictureObject,DTM_PROCLAYOUT,NULL,1))
			{
				struct BitMap *BitMap;

        BitMap=0;

				GetDTAttrs(BFI->PictureObject,PDTA_BitMapHeader,        &BFI->BitMapHeader,
                                      (stc ? PDTA_BitMap : PDTA_DestBitMap),          &BitMap,
//                                    PDTA_DestMode,            &destmode,
                                      TAG_DONE);
        
				if (!BitMap)
					GetDTAttrs(BFI->PictureObject,PDTA_BitMap,&BitMap,TAG_DONE);
          
				if(BitMap)
				{
					LONG Depth = GetBitMapAttr(BitMap,BMA_DEPTH);

					CalculateCopySizes(BFI);

					// the bitmap is copied to a friend bitmap of the screen to speed up rendering with gfxboards (no c2p/p2c!); this additionally allows to use this copy as a "picture cache", i.e. if the picture is 2x2 the copy can be made 16x16 (with 64 tiles) or whatever to speed up rendering
					if (BFI->BitMap = AllocBitMap(BFI->CopyWidth,BFI->CopyHeight,Depth, BMF_MINPLANES ,(stc ? BitMap: Scr->RastPort.BitMap)))
					{
						struct Rectangle CopyBounds;
						CopyBounds.MinX = 0;
						CopyBounds.MinY = 0;
						CopyBounds.MaxX = BFI->CopyWidth-1;
						CopyBounds.MaxY = BFI->CopyHeight-1;


/*            
            if(CyberGfxBase && destmode==PMODE_V43)
            {
              struct pdtBlitPixelArray bpa;
              
              bpa.MethodID=PDTM_READPIXELARRAY;
              bpa.pbpa_PixelArray=;      /* Source PixelArray                                         */
              bpa.pbpa_PixelArrayMode=;  /* Format Mode of the Source PixelArray; see cybergraphics.h */
              bpa.pbpa_PixelArrayMod=;   /* Bytes to add to the next line in the Source PixelArray    */
              bpa.pbpa_LeftEdge=;        /* XStart of the Dest                                        */
              bpa.pbpa_TopEdge=;         /* YStart of the Dest                                        */
              bpa.pbpa_Width=;           /* Width of the Source PixelArray                            */
              bpa.pbpa_Height=;          /* Height of the Source PixelArray                           */
              
              
              DoMethod(
                            
              CopyTiledBitMap(BFI->BitMap,0,0,BFI->BitMapHeader->bmh_Width,BFI->BitMapHeader->bmh_Height,BFI->BitMap,&CopyBounds); // blit the tiles to the bitmap
            }
            else*/
            {
						  CopyTiledBitMap(BitMap,0,0,BFI->BitMapHeader->bmh_Width,BFI->BitMapHeader->bmh_Height,BFI->BitMap,&CopyBounds); // blit the tiles to the bitmap
            }
						return (TRUE);
					}
				}
			}
		}
    UnloadBackgroundImage(BFI);
		//memset(BFI,0,sizeof(*BFI));
	}
	return (FALSE);
}

void UnloadBackgroundImage(struct BackFillInfo *BFI)
{
	WaitBlit();
	// both frees work with NULL
	FreeBitMap(BFI->BitMap);
	DisposeDTObject(BFI->PictureObject); // datatype object is freed here, because - although a different bitmap is used for blitting to the screen - the pens have to stay allocated
	memset(BFI,0,sizeof(*BFI));
}
