/*
 * MandelVroom 2.0
 *
 * (c) Copyright 1987,1989  Kevin L. Clague, San Jose, CA
 *
 * All rights reserved.
 *
 * Permission is hereby granted to distribute this program's source
 * executable, and documentation for non-comercial purposes, so long as the
 * copyright notices are not removed from the sources, executable or
 * documentation.  This program may not be distributed for a profit without
 * the express written consent of the author Kevin L. Clague.
 *
 * This program is not in the public domain.
 *
 * Fred Fish is expressly granted permission to distribute this program's
 * source and executable as part of the "Fred Fish freely redistributable
 * Amiga software library."
 *
 * Permission is expressly granted for this program and it's source to be
 * distributed as part of the Amicus Amiga software disks, and the
 * First Amiga User Group's Hot Mix disks.
 *
 * contents: this file contains the code that implements the lens effect
 * (pixelized expansion) when zooming in.
 *
 */

#include "mandp.h"

/* Lens algorithm:
 *
 *   Initialization - Create RastPort and BitMap as a work area
 *
 *   Lens Expansion -
 *     Phase 1 (Horizontal Expansion)
 *       1.  Working from left side of NavBox to Right
 *       2.  expand one verticle line from NavBox into temp rastport.
 *       3.  duplicate the line in temp rastport to expansion width
 *       4.  Continue for enough lines to fill the temp rastport
 *
 *     Phase 2 (Verticle Expansion)
 *       1.  Working from top of temp rastport to bottom
 *       2.  expand one horizontal line from temp rastport into Lens
 *           window rastport.
 *       3.  duplicate the line in Lens window rastport to expansion height
 *       4.  Continue for enough lines in the temp rastport
 */

/* These structures are used to hold horizontally expanded view of lenz
   expansion */

static struct RastPort LensRp;
static struct BitMap   LensBitMap;
static LONG   LensWidth, LensHeight;

UBYTE LensOK;
UBYTE LensOn = 1;

#ifdef TEMPWINDOW
#define SAFEBLIT

static struct NewWindow NewTemp = {
   0,12,                     /* start position           */
   80,80,                    /* width, height            */
   (UBYTE) 0, (UBYTE) NORMALPEN,
   MOUSEBUTTONS,             /* IDCMP flags              */
                             /* MandWind flags           */
   WINDOWDRAG | NOCAREREFRESH | SMART_REFRESH,
   (struct Gadget *) NULL,   /* first gadget             */
   (struct Image *) NULL,    /* user checkmark           */
   (UBYTE *) NULL,           /* Title                    */
   (struct Screen *) NULL,   /* pointer to screen        */
   (struct BitMap *) NULL,   /* pointer to superbitmap   */
   20,20,-1,-1,              /* sizing                   */
   CUSTOMSCREEN              /* type of screen           */
   };

static struct Window *TempWind;
#endif

AllocLensTemp( Pict )
  register struct Picture *Pict;
{
  LONG Plane;
  PLANEPTR t;

  extern struct MenuItem LensSub[];

  static LensInited;

  extern struct NewScreen NewScreen;

  if ( !LensOn )
    return;

  if (LensOK == 1)
    return;

  if (Pict == Pict->DrawPict)
    return;

#ifdef TEMPWINDOW

  NewTemp.Width    = Pict->Window->Width;
  NewTemp.Height   = Pict->DrawPict->Window->Height;
  NewTemp.LeftEdge = NewScreen.Width - NewTemp.Width-2;
  NewTemp.TopEdge  = 0;
  NewTemp.Screen   = screen;
  TempWind         = OpenWindow( &NewTemp );
/*WindowToBack( TempWind );*/
  LensOK = TempWind != NULL;

#else

  if ( ! LensInited ) {

    InitRastPort( &LensRp );
    LensRp.BitMap = &LensBitMap;

    LensInited = 1;
  }

  LensWidth  = Pict->Window->Width;
  LensHeight = Pict->DrawPict->Window->Height;

  InitBitMap( &LensBitMap, (long) NewScreen.Depth, LensWidth, LensHeight);

  for (Plane = 0; Plane < NewScreen.Depth; Plane++) {

    t = (PLANEPTR) AllocRaster(LensWidth,LensHeight);
    LensBitMap.Planes[Plane] = (PLANEPTR) t;

    if (t == NULL) {
      FreeLensTemp( );
      return;
    }
  }
  LensOK = 1;

#endif
}

FreeLensTemp( )
{
  LONG Plane;

  if (LensOK) {

#ifdef TEMPWINDOW
    CloseWindow( TempWind );
#else

    for (Plane = 0; Plane < LensBitMap.Depth; Plane++) {

      if (LensBitMap.Planes[Plane]) {

        FreeRaster( (char *) LensBitMap.Planes[Plane],
                    LensWidth, LensHeight);
        LensBitMap.Planes[Plane] = NULL;
      }
    }
#endif
    LensOK = 0;
  }
}

#ifdef SAFEBLIT

SafeClipBlit( src_Wind, src_x, src_y,
              dst_Wind, dst_x, dst_y,
              width, height, min_terms,
              line_no )

  struct Window *src_Wind, *dst_Wind;
  LONG   src_x, src_y, dst_x, dst_y, width, height, min_terms;
  LONG   line_no;
{

  if (src_x < 0 || src_x > src_Wind->Width) {
    printf("Line %d Bad blit src_x %d Width %d\n",
            line_no,src_x,src_Wind->Width);
    return;
  }

  if (src_y < 0 || src_y > src_Wind->Height) {
    printf("Line %d Bad blit src_y %d Height %d\n",
            line_no, src_y,src_Wind->Height);
    return;
  }

  if (width < 1 || width > src_Wind->Width) {
    printf("Line %d Bad blit width %d\n",line_no, width);
    return;
  }

  if (src_x + width > src_Wind->Width) {
    printf("Line %d Blit outside %d Width %d \n",
            line_no,src_x+width,src_Wind->Width);
    return;
  }

  if (height < 1 || height > src_Wind->Height) {
    printf("Line %d Bad blit height %d\n",line_no,height);
    return;
  }

  if (src_y + height > src_Wind->Height) {
    printf("Line %d Blit outside %d Height %d \n",
            line_no,src_y+height,src_Wind->Height);
    return;
  }

  ClipBlit( src_Wind->RPort, src_x, src_y,
            dst_Wind->RPort, dst_x, dst_y,
            width, height, min_terms);
}
#endif

  /* Do Lens effect using temporary rastort, ClipBlit() with exponential
     expansion */

Lens( Pict )
  register struct Picture *Pict;
{
  struct Picture *DrawPict;

  LONG ExpFactor;
  LONG MaxHeight,MaxWidth;
  LONG SrcPos, SrcMax;

  LONG DstPos, DstMax;

  LONG EOL;

  LONG NextPos, AccPos;
  LONG          AccExp;

  LONG FirstSrc,FirstDst,NumOnes;

  static LONG OldLeft,OldTop,OldExp;
  LONG Top,Left,Bot,t;

  struct Window   *src_Wind,*dst_Wind;
  struct RastPort *src_Rp,  *dst_Rp,  *tmp_Rp;

  DrawPict = Pict->DrawPict;

  if (DrawPict == NULL)
    return;

  CloseZoomBoxList( Pict );

  src_Wind = DrawPict->Window;
  dst_Wind = Pict->Window;

  src_Rp = src_Wind->RPort;
  dst_Rp = dst_Wind->RPort;

  if (src_Rp == dst_Rp) return;

#ifdef TEMPWINDOW
  tmp_Rp = TempWind->RPort;
#else
  tmp_Rp = &LensRp;
#endif

  if ( !LensOK )
    return;

#ifndef TEMPWINDOW
  SetRast( tmp_Rp, (long) NORMALPEN );
#endif

  /* turn the Nav Box upside down, so the bit map is not inverted */

  Top   = Pict->NavTop;
  Bot   = Pict->NavBot;
  Left  = Pict->NavLeft;

  if (Bot < Top) {
    t            = Bot;
    Bot = Top;
    Top = t;
  }
  if (Pict->NavRight < Left) {
    Left  = Pict->NavRight;
  }

  /* Do Horizontal Expansion into a temporary bitmap */
  /* src = DrawPict */
  /* dst = Pict */

  MaxHeight = Bot - Top + 1;

  if (MaxHeight == 0)
    return;

  DstPos = 0;

  DstMax = dst_Wind->Width - Pict->LeftMarg - Pict->RightMarg;
  SrcMax = src_Wind->Width - DrawPict->RightMarg;

#define SHIFT_FACTOR 8

  AccExp = ((dst_Wind->Height - Pict->TopMarg - Pict->BotMarg)
            << SHIFT_FACTOR) / MaxHeight;

  if (OldLeft == Left && OldTop == Top && OldExp == AccExp)
    return;

  Pict->Flags |= LENS_DISPLAYED;

  OldLeft = Left;
  OldTop  = Top;
  OldExp  = AccExp;

  AccPos = DstPos << SHIFT_FACTOR;

  EOL = 0;
  NumOnes = 0;

  for (SrcPos = Left; SrcPos < SrcMax && ! EOL; SrcPos++) {

    AccPos += AccExp;
    NextPos = AccPos >> SHIFT_FACTOR;

    ExpFactor = NextPos - DstPos;

    /* Accumulate adjacent single line blits */

    if (ExpFactor == 1) {
      if (NumOnes == 0) {
        FirstSrc = SrcPos;
        FirstDst = DstPos;
      }
      NumOnes++;
    } else {

      /* Copy accumulated adjcent single line copies */

      if (NumOnes > 0) {

#ifndef SAFEBLIT
        ClipBlit( src_Rp,  FirstSrc,  (long) Top,
                  tmp_Rp,  FirstDst,  0,
                  NumOnes, MaxHeight, (long) 0xc0 );
#else
        SafeClipBlit( src_Wind, FirstSrc,  Top,
                      TempWind, FirstDst,  0,
                      NumOnes,  MaxHeight, 0xc0, __LINE__ );
#endif
        NumOnes = 0;
      }

      if (ExpFactor > 1) {

        register LONG WrkPos, WrkOff, Last, DstExp;

        /* Expand Pattern exponentially */

        WrkPos = DstPos + 1;
        WrkOff = 1;
        Last = 0;
        DstExp = 1;

        /* Copy First line */

#ifndef SAFEBLIT
        ClipBlit( src_Rp, SrcPos,    (long) Top,
                  tmp_Rp, DstPos,    0,
                  (long) 1, MaxHeight, (long) 0xc0 );
#else
        SafeClipBlit( src_Wind,  SrcPos,  Top,
                      TempWind,  DstPos,  0,
                      1,         MaxHeight, 0xc0, __LINE__);
#endif

        /* Now expand it exponentially */

        do {

          if ( WrkOff + DstExp >= ExpFactor ) {
            Last = 1;
            DstExp = ExpFactor - WrkOff;
          }

          if ( WrkPos + DstExp >= DstMax ) {
            Last = 1;
            EOL = 1;
            DstExp = DstMax - WrkPos;
          }

          if (DstExp) {
#ifndef SAFEBLIT
            ClipBlit( tmp_Rp, DstPos,    0,
                      tmp_Rp, WrkPos,    0,
                      DstExp, MaxHeight, (long) 0xc0 );
#else
            SafeClipBlit( TempWind,  DstPos,  0,
                          TempWind,  WrkPos,  0,
                          DstExp,    MaxHeight, 0xc0, __LINE__);
#endif
            WrkPos += DstExp;
            WrkOff += DstExp;

            DstExp <<= 1;
          }
        } while ( ! Last );
      }
    }
    if ((DstPos = NextPos) >= DstMax) EOL = 1;
  }

  if (NumOnes > 0) {

#ifndef SAFEBLIT
    ClipBlit( src_Rp,  FirstSrc,  (long) Top,
              tmp_Rp,  FirstDst,  0,
              NumOnes, MaxHeight, (long) 0xc0 );
#else
    SafeClipBlit( src_Wind,  FirstSrc,  Top,
                  TempWind,  FirstDst,  0,
                  NumOnes,   MaxHeight, 0xc0, __LINE__);
#endif
  }
  NumOnes = 0;

  ZoomBox( Pict );

  /* src = DrawPict */
  /* dst = Pict */

  /* Do Verticle Expansion */

  MaxWidth = dst_Wind->Width - Pict->LeftMarg - Pict->RightMarg;

  DstPos = Pict->TopMarg;
  DstMax = dst_Wind->Height - Pict->BotMarg;
  SrcMax = src_Wind->Height;

  EOL = 0;

  AccPos = DstPos << SHIFT_FACTOR;

  for (SrcPos = 0; SrcPos < SrcMax && !EOL; SrcPos++) {

    AccPos += AccExp;
    NextPos = AccPos >> SHIFT_FACTOR;

    ExpFactor = NextPos - DstPos;

    /* Accumulate adjacent single line blits */

    if (ExpFactor == 1) {
      if (NumOnes == 0) {
        FirstSrc = SrcPos;
        FirstDst = DstPos;
      }
      NumOnes++;
    } else {

      /* Copy accumulated adjacent single line copies */

      if (NumOnes > 0) {

#ifndef SAFEBLIT
        ClipBlit( tmp_Rp,   0,  FirstSrc,
                  dst_Rp,   Pict->LeftMarg,  FirstDst,
                  MaxWidth, NumOnes,   (long) 0xc0 );
#else
        SafeClipBlit( TempWind,  0,  FirstSrc,
                      dst_Wind,  Pict->LeftMarg,  FirstDst,
                      MaxWidth,  NumOnes,  0xc0, __LINE__);
#endif
        NumOnes = 0;
      }

      if ( ExpFactor > 1 ) {

        register LONG WrkPos, WrkOff, Last, DstExp;

        /* Expand Pattern exponentially */

        WrkPos = DstPos + 1;
        WrkOff = 1;
        Last = 0;
        DstExp = 1;

        /* Copy First Horizontal Line */

#ifndef SAFEBLIT
        ClipBlit( tmp_Rp,   0, SrcPos,
                  dst_Rp,   Pict->LeftMarg, DstPos,
                  MaxWidth, (long) 1, (long) 0xc0 );
#else
        SafeClipBlit( TempWind, 0, SrcPos,
                      dst_Wind, Pict->LeftMarg, DstPos,
                      MaxWidth, 1,        0xc0, __LINE__ );
#endif
        do {

          if ( WrkOff + DstExp >= ExpFactor ) {
            Last = 1;
            DstExp = ExpFactor - WrkOff;
          }

          if ( WrkPos + DstExp >= DstMax ) {
            Last = 1;
            EOL = 1;
            DstExp = DstMax - WrkPos;
          }
          if (DstExp) {
#ifndef SAFEBLIT
            ClipBlit( dst_Rp,   Pict->LeftMarg, DstPos,
                      dst_Rp,   Pict->LeftMarg, WrkPos,
                      MaxWidth, DstExp,   (long) 0xc0 );
#else
            SafeClipBlit( dst_Wind, Pict->LeftMarg, DstPos,
                          dst_Wind, Pict->LeftMarg, WrkPos,
                          MaxWidth, DstExp,   0xc0, __LINE__ );
#endif
            WrkPos += DstExp;
            WrkOff += DstExp;

            DstExp <<= 1;
          }
        } while ( ! Last );
      }
    }
    if ((DstPos = NextPos) >= DstMax) EOL = 1;
  }

  /* Copy accumulated adjacent single line copies */

  if (NumOnes > 0) {
#ifndef SAFEBLIT
    ClipBlit( tmp_Rp,  0,  FirstSrc,
              dst_Rp,  Pict->LeftMarg,  FirstDst,
              MaxWidth,NumOnes,   (long) 0xc0 );
#else
    SafeClipBlit( TempWind, 0,  FirstSrc,
                  dst_Wind, Pict->LeftMarg,  FirstDst,
                  MaxWidth, NumOnes,   0xc0, __LINE__ );
#endif
  }

  ZoomBox( Pict );
}

CloseZoomBoxList( Pict )
  register struct Picture *Pict;
{
  register struct Node    *zNode;
  register struct Picture *ClosePict;
           struct Picture *PictAddr();

  zNode = Pict->zList.lh_Head;

  while ( zNode->ln_Succ ) {

    ClosePict = PictAddr( zNode );

    if (Pict != ClosePict) {
      CloseZoomBox(ClosePict);
    }

    zNode = zNode->ln_Succ;
  }
}



