/****************************************************************************
 SKEW.c

 The SKEW module handles rotating a rectangular object.

****************************************************************************/

#include "stdafx.h"
#include "global.h"
#include "pbrush.h"
#include "pbrusdoc.h"
#include "imgwnd.h"
#include "imgbrush.h"
#include "imgsuprt.h"
#include "bmobject.h"
#include "undo.h"
#include "props.h"

#ifdef _DEBUG
#undef THIS_FILE
static CHAR BASED_CODE THIS_FILE[] = __FILE__;
#endif

#include "memtrace.h"

static int miAngle;

/***************************************************************************/

static int CalcOffset( int iValue )
    {
    // tan[angle] is the 100000/tangent of angles 1 to 89deg.
    static int invtan[90] = {  -1, 5728996, 2863625, 1908114, 1430067,
                          1143005,  951436,  814436,  711537,  631375,
                           567128,  514455,  470463,  433148,  401078,
                           373205,  348741,  327085,  307768,  290421,
                           274748,  260509,  247509,  235585,  224604,
                           214451,  205030,  196261,  188073,  180405,
                           173205,  166428,  160034,  153987,  148256,
                           142815,  137638,  132705,  127994,  123490,
                           119175,  115037,  111061,  107237,  103553,
                           100000,   96569,   93252,   90040,   86929,
                            83910,   80978,   78129,   75355,   72654,
                            70021,   67451,   64941,   62487,   60086,
                            57735,   55431,   53171,   50953,   48773,
                            46631,   44523,   42448,   40403,   38386,
                            36397,   34433,   32492,   30573,   28675,
                            26795,   24933,   23087,   21256,   19438,
                            17633,   15838,   14054,   12279,   10510,
                             8749,    6993,    5241,    3492,    1746 };
    return (((iValue * 1000000 / invtan[miAngle]) + 5) / 10);
    }

/***************************************************************************/

void CImgWnd::CmdSkewBrush( int iAngle, BOOL bHorz )
    {
    BOOL  bFullImage = FALSE;
    CDC*  pdcImg = CDC::FromHandle( m_pImg->hDC );
    CDC*  pdcSrc = &theImgBrush.m_dc;
    CRect rectSlct( 0, 0, theImgBrush.m_size.cx, theImgBrush.m_size.cy );

    if (theImgBrush.m_pImg == NULL && ! g_bCustomBrush)
        {
        bFullImage = TRUE;
        pdcSrc     = pdcImg;
        rectSlct.SetRect( 0, 0, m_pImg->cxWidth, m_pImg->cyHeight );
        }

    CRect rectSkew = rectSlct;

    if (!iAngle || rectSlct.IsRectEmpty() || abs(iAngle) > 89 || abs(iAngle) < 1 )
        return;

    if (! bFullImage)
        {
        PrepareForBrushChange( TRUE, TRUE );
        HideBrush();
        }
    // angle is the angle of skew 0 to 90 degrees
    // axis is the direction to skew TRUE:  horizontal
    //                                      the y axis moves by angle
    //                               FALSE: verticle
    //                                      the x axis moves by angle
    int iWidthSlct  = rectSlct.Width();
    int iHeightSlct = rectSlct.Height();

    miAngle = abs(iAngle);

    if (bHorz) // horizontal - create a new bitmap of size = x + y * tan (angle)
        rectSkew.right += CalcOffset( iHeightSlct );
    else       // verticale - create a new bitmap of size = y + x * tan (angle)
        rectSkew.bottom += CalcOffset( iWidthSlct );

    int iWidthSkew  = rectSkew.Width();
    int iHeightSkew = rectSkew.Height();

    // angle is the angle of skew 0 to 90 degrees
    // axis is the direction to skew TRUE:  horizontal
    //                                      the y axis moves by angle
    //                               FALSE: verticle
    //                                      the x axis moves by angle
    CDC     dcWork;
    CBitmap bmWork;
    CBrush  brBackground;

    if (! dcWork.CreateCompatibleDC( pdcImg )
    ||  ! bmWork.CreateCompatibleBitmap( pdcImg, iWidthSkew, iHeightSkew )
    ||  ! brBackground.CreateSolidBrush( crRight ))
        {
        theApp.SetGdiEmergency( TRUE );
        return;
        }

    CBitmap*  pbmOld = dcWork.SelectObject( &bmWork );
    CPalette* ppalOld = SetImgPalette( &dcWork, FALSE );

    dcWork.FillRect( rectSkew, &brBackground );

    CPalette* ppalOldSrc = SetImgPalette( pdcSrc, FALSE );

    // Skew the bitmap
    int mx = 1;
    int my = 1;  // local variables for the skew
    int x = rectSlct.left;
    int y = rectSlct.top;

    BeginWaitCursor();

    if (bHorz) // horizontal
        {
        if (iAngle > 0)
            {
            mx = x + CalcOffset( iHeightSlct - y );
            // Start at Bottom
            for (y = rectSlct.bottom; y >= rectSlct.top; )
                {
                dcWork.BitBlt( mx, y, iWidthSlct, 1, pdcSrc, x, y, SRCCOPY );
                mx = x + CalcOffset( iHeightSlct - y-- );
                }
            }
        else
            {
            x = rectSkew.right - iWidthSlct;
            mx = x - CalcOffset( iHeightSlct - y );
            for (y = rectSlct.bottom; y >= rectSlct.top; )
                {
                dcWork.BitBlt( mx, y, iWidthSlct, 1, pdcSrc, rectSlct.left, y, SRCCOPY );
                mx = x - CalcOffset( iHeightSlct - y-- );
                }
            }
        }
    else
        { // vertical
        if (iAngle > 0)
            {
            my = y + CalcOffset( iWidthSlct - x );
            // Start at left
            for (x = rectSlct.left - 1; x <= rectSlct.right; )
                {
                dcWork.BitBlt( x, my, 1, iHeightSlct, pdcSrc, x, y, SRCCOPY );
                my = y + CalcOffset( iWidthSlct - x++ );
                }
            }
        else
            {
            y = rectSkew.bottom - iHeightSlct;
            my = y - CalcOffset( iWidthSlct - x );
            for (x = rectSlct.left - 1; x <= rectSlct.right; )
                {
                dcWork.BitBlt( x, my, 1, iHeightSlct, pdcSrc, x, rectSlct.top, SRCCOPY );
                my = y - CalcOffset( iWidthSlct - x++ );
                }
            }
        }

    if (ppalOldSrc)
        pdcSrc->SelectPalette( ppalOldSrc, FALSE );

    if (bFullImage)
        {
        theUndo.BeginUndo( TEXT("Skew Bitmap") );

        m_pImg->m_pBitmapObj->SetSizeProp( P_Size, CSize( iWidthSkew, iHeightSkew ) );

        m_pImg->cxWidth  = iWidthSkew;
        m_pImg->cyHeight = iHeightSkew;

        SetUndo( m_pImg );

        pdcImg->BitBlt( 0, 0, m_pImg->cxWidth, m_pImg->cyHeight, &dcWork, 0, 0, SRCCOPY );

        InvalImgRect ( m_pImg, &rectSkew );
        CommitImgRect( m_pImg, &rectSkew );

        FinishUndo( rectSkew );

        theUndo.EndUndo();

        DirtyImg( m_pImg );

        InvalidateRect( NULL );
        UpdateWindow();

        dcWork.SelectObject( pbmOld );
        bmWork.DeleteObject();
        }
    else
        {
        CBitmap bmMask;

        if (! bmMask.CreateBitmap( iWidthSkew, iHeightSkew, 1, 1, NULL ))
            {
            if (ppalOld)
                dcWork.SelectPalette( ppalOld, FALSE );

            theApp.SetMemoryEmergency( TRUE );
            return;
            }

        dcWork.SelectObject( pbmOld );

        theImgBrush.m_dc.SelectObject( &bmWork );
        theImgBrush.m_bitmap.DeleteObject();
        theImgBrush.m_bitmap.Attach( bmWork.Detach() );

        theImgBrush.m_size.cx = iWidthSkew;
        theImgBrush.m_size.cy = iHeightSkew;

        VERIFY( theImgBrush.m_maskDC.SelectObject( &bmMask ) ==
               &theImgBrush.m_maskBitmap );

        theImgBrush.m_maskBitmap.DeleteObject();
        theImgBrush.m_maskBitmap.Attach( bmMask.Detach() );
        theImgBrush.RecalcMask( crRight );

        rectSkew = theImgBrush.m_rcSelection;

        mx = 0;
        my = 0;

        if (bHorz)
            mx = CalcOffset( iHeightSlct ) / 2;
        else
            my = CalcOffset( iWidthSlct ) / 2;

        rectSkew.InflateRect( mx, my );

        MoveBrush( rectSkew );
        }

    if (ppalOld)
        dcWork.SelectPalette( ppalOld, FALSE );

    dcWork.DeleteDC();

    EndWaitCursor();
    }

/***************************************************************************/