/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"

#include <vcl/salbtype.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/bmpacc.hxx>

#include <impbmp.hxx>

#include <string.h>

// --------------------
// - BitmapReadAccess -
// --------------------

BitmapReadAccess::BitmapReadAccess( Bitmap& rBitmap, sal_Bool bModify ) :
			mpBuffer		( NULL ),
			mpScanBuf		( NULL ),
			mFncGetPixel	( NULL ),
			mFncSetPixel	( NULL ),
			mbModify		( bModify )
{
	ImplCreate( rBitmap );
}

// ------------------------------------------------------------------

BitmapReadAccess::BitmapReadAccess( Bitmap& rBitmap ) :
			mpBuffer		( NULL ),
			mpScanBuf		( NULL ),
			mFncGetPixel	( NULL ),
			mFncSetPixel	( NULL ),
			mbModify		( sal_False )
{
	ImplCreate( rBitmap );
}

// ------------------------------------------------------------------

BitmapReadAccess::~BitmapReadAccess()
{
	ImplDestroy();
}

// ------------------------------------------------------------------

void BitmapReadAccess::ImplCreate( Bitmap& rBitmap )
{
	ImpBitmap* pImpBmp = rBitmap.ImplGetImpBitmap();

	DBG_ASSERT( pImpBmp, "Forbidden Access to empty bitmap!" );

	if( pImpBmp )
	{
		if( mbModify && !maBitmap.ImplGetImpBitmap() )
		{
			rBitmap.ImplMakeUnique();
			pImpBmp = rBitmap.ImplGetImpBitmap();
		}
		else
		{
			DBG_ASSERT( !mbModify || pImpBmp->ImplGetRefCount() == 2,
						"Unpredictable results: bitmap is referenced more than once!" );
		}

		mpBuffer = pImpBmp->ImplAcquireBuffer( !mbModify );

		if( !mpBuffer )
		{
			ImpBitmap* pNewImpBmp = new ImpBitmap;

			if( pNewImpBmp->ImplCreate( *pImpBmp, rBitmap.GetBitCount()  ) )
			{
				pImpBmp = pNewImpBmp;
				rBitmap.ImplSetImpBitmap( pImpBmp );
				mpBuffer = pImpBmp->ImplAcquireBuffer( !mbModify );
			}
			else
				delete pNewImpBmp;
		}

		if( mpBuffer )
		{
			const long	nHeight = mpBuffer->mnHeight;
			Scanline	pTmpLine = mpBuffer->mpBits;

			mpScanBuf = new Scanline[ nHeight ];
			maColorMask = mpBuffer->maColorMask;

			if( BMP_SCANLINE_ADJUSTMENT( mpBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN )
			{
				for( long nY = 0L; nY < nHeight; nY++, pTmpLine += mpBuffer->mnScanlineSize )
					mpScanBuf[ nY ] = pTmpLine;
			}
			else
			{
				for( long nY = nHeight - 1; nY >= 0; nY--, pTmpLine += mpBuffer->mnScanlineSize )
					mpScanBuf[ nY ] = pTmpLine;
			}

			if( !ImplSetAccessPointers( BMP_SCANLINE_FORMAT( mpBuffer->mnFormat ) ) )
			{
				delete[] mpScanBuf;
				mpScanBuf = NULL;

				pImpBmp->ImplReleaseBuffer( mpBuffer, !mbModify );
				mpBuffer = NULL;
			}
			else
				maBitmap = rBitmap;
		}
	}
}

// ------------------------------------------------------------------

void BitmapReadAccess::ImplDestroy()
{
	ImpBitmap* pImpBmp = maBitmap.ImplGetImpBitmap();

	delete[] mpScanBuf;
	mpScanBuf = NULL;

	if( mpBuffer && pImpBmp )
	{
		pImpBmp->ImplReleaseBuffer( mpBuffer, !mbModify );
		mpBuffer = NULL;
	}
}

// ------------------------------------------------------------------

sal_Bool BitmapReadAccess::ImplSetAccessPointers( sal_uLong nFormat )
{
	sal_Bool bRet = sal_True;

	switch( nFormat )
	{
		CASE_FORMAT( _1BIT_MSB_PAL )
		CASE_FORMAT( _1BIT_LSB_PAL )
		CASE_FORMAT( _4BIT_MSN_PAL )
		CASE_FORMAT( _4BIT_LSN_PAL )
		CASE_FORMAT( _8BIT_PAL )
		CASE_FORMAT( _8BIT_TC_MASK )
		CASE_FORMAT( _16BIT_TC_MSB_MASK )
		CASE_FORMAT( _16BIT_TC_LSB_MASK )
		CASE_FORMAT( _24BIT_TC_BGR )
		CASE_FORMAT( _24BIT_TC_RGB )
		CASE_FORMAT( _24BIT_TC_MASK )
		CASE_FORMAT( _32BIT_TC_ABGR )
		CASE_FORMAT( _32BIT_TC_ARGB )
		CASE_FORMAT( _32BIT_TC_BGRA )
		CASE_FORMAT( _32BIT_TC_RGBA )
		CASE_FORMAT( _32BIT_TC_MASK )

		default:
			bRet = sal_False;
		break;
	}

	return bRet;
}

// ------------------------------------------------------------------

void BitmapReadAccess::ImplZeroInitUnusedBits()
{
	const sal_uInt32 nWidth = Width(), nHeight = Height(), nScanSize = GetScanlineSize();

	if( nWidth && nHeight && nScanSize && GetBuffer() )
	{
		sal_uInt32 nBits;
        bool       bMsb;

        const sal_uLong nScanlineFormat = GetScanlineFormat();
		switch( nScanlineFormat )
		{
            case( BMP_FORMAT_1BIT_MSB_PAL ):
				nBits = 1;
				bMsb = true;
                break;

            case( BMP_FORMAT_1BIT_LSB_PAL ):
				nBits = 1;
				bMsb = false;
                break;

			case( BMP_FORMAT_4BIT_MSN_PAL ):
				nBits = 4;
				bMsb = true;
                break;

            case( BMP_FORMAT_4BIT_LSN_PAL ):
				nBits = 4;
				bMsb = false;
                break;

			case( BMP_FORMAT_8BIT_PAL ):
			case( BMP_FORMAT_8BIT_TC_MASK ):
				bMsb = true;
				nBits = 8;
			break;

			case( BMP_FORMAT_16BIT_TC_MSB_MASK ):
			case( BMP_FORMAT_16BIT_TC_LSB_MASK ):
				bMsb = true;
				nBits = 16;
			break;

			case( BMP_FORMAT_24BIT_TC_BGR ):
			case( BMP_FORMAT_24BIT_TC_RGB ):
			case( BMP_FORMAT_24BIT_TC_MASK ):
				bMsb = true;
				nBits = 24;
			break;

			case( BMP_FORMAT_32BIT_TC_ABGR ):
			case( BMP_FORMAT_32BIT_TC_ARGB ):
			case( BMP_FORMAT_32BIT_TC_BGRA ):
			case( BMP_FORMAT_32BIT_TC_RGBA ):
			case( BMP_FORMAT_32BIT_TC_MASK ):
				bMsb = true;
				nBits = 32;
			break;

			default:
			{
				DBG_ERROR( "BitmapWriteAccess::ZeroInitUnusedBits: Unsupported pixel format");
				nBits = 0;
				bMsb = true;
			}
			break;
		}

        nBits *= nWidth;
		if( nScanSize % 4 || !bMsb )
        {
            DBG_ASSERT( 8*nScanSize >= nBits,
                        "BitmapWriteAccess::ZeroInitUnusedBits: span size smaller than width?!");
			const sal_uInt32 nLeftOverBits = 8*sizeof(sal_uInt8)*nScanSize - nBits;
            if( nLeftOverBits != 0 ) // else there is really nothing to do
            {
                const sal_uInt32 nBytes = (nLeftOverBits + 7U) >> 3U;
                sal_uInt8	     nMask;
    
                if( bMsb )
                    nMask = static_cast<sal_uInt8>(0xffU << (nLeftOverBits & 3UL));
                else
                    nMask = static_cast<sal_uInt8>(0xffU >> (nLeftOverBits & 3UL));
    
                sal_uInt8* pLastBytes = (sal_uInt8*)GetBuffer() + ( nScanSize - nBytes );
                for( sal_uInt32 i = 0; i < nHeight; i++, pLastBytes += nScanSize )
                {
                    *pLastBytes &= nMask;
                    for( sal_uInt32 j = 1; j < nBytes; j++ )
                        pLastBytes[j] = 0;
                }
            }
        }
        else if( nBits & 0x1f )
		{
			sal_uInt32	nMask = 0xffffffff << ( ( nScanSize << 3 ) - nBits );
			sal_uInt8*		pLast4Bytes = (sal_uInt8*) GetBuffer() + ( nScanSize - 4 );

#ifdef OSL_LITENDIAN
			nMask = SWAPLONG( nMask );
#endif
			for( sal_uInt32 i = 0; i < nHeight; i++, pLast4Bytes += nScanSize )
				( *(sal_uInt32*) pLast4Bytes ) &= nMask;
		}
	}
}

// ------------------------------------------------------------------

void BitmapReadAccess::Flush()
{
	ImplDestroy();
}

// ------------------------------------------------------------------

void BitmapReadAccess::ReAccess( sal_Bool bModify )
{
	const ImpBitmap* pImpBmp = maBitmap.ImplGetImpBitmap();

	DBG_ASSERT( !mpBuffer, "No ReAccess possible while bitmap is being accessed!" );
	DBG_ASSERT( pImpBmp && ( pImpBmp->ImplGetRefCount() > 1UL ), "Accessed bitmap does not exist anymore!" );

	if( !mpBuffer && pImpBmp && ( pImpBmp->ImplGetRefCount() > 1UL ) )
	{
		mbModify = bModify;
		ImplCreate( maBitmap );
	}
}

// ------------------------------------------------------------------

sal_uInt16 BitmapReadAccess::GetBestPaletteIndex( const BitmapColor& rBitmapColor ) const
{
	return( HasPalette() ? mpBuffer->maPalette.GetBestIndex( rBitmapColor ) : 0 );
}

// ---------------------
// - BitmapWriteAccess -
// ---------------------

BitmapWriteAccess::BitmapWriteAccess( Bitmap& rBitmap ) :
            BitmapReadAccess( rBitmap, sal_True ),
            mpLineColor     ( NULL ),
            mpFillColor     ( NULL )
{
}

// ------------------------------------------------------------------

BitmapWriteAccess::~BitmapWriteAccess()
{
	delete mpLineColor;
	delete mpFillColor;
}

// ------------------------------------------------------------------

void BitmapWriteAccess::CopyScanline( long nY, const BitmapReadAccess& rReadAcc )
{
	DBG_ASSERT( ( nY >= 0 ) && ( nY < mpBuffer->mnHeight ), "y-coordinate in destination out of range!" );
	DBG_ASSERT( nY < rReadAcc.Height(), "y-coordinate in source out of range!" );
	DBG_ASSERT( ( HasPalette() && rReadAcc.HasPalette() ) || ( !HasPalette() && !rReadAcc.HasPalette() ), "No copying possible between palette bitmap and TC bitmap!" );

	if( ( GetScanlineFormat() == rReadAcc.GetScanlineFormat() ) &&
		( GetScanlineSize() >= rReadAcc.GetScanlineSize() ) )
	{
		memcpy( mpScanBuf[ nY ], rReadAcc.GetScanline( nY ), rReadAcc.GetScanlineSize() );
	}
	else
        // TODO: use fastbmp infrastructure
		for( long nX = 0L, nWidth = Min( mpBuffer->mnWidth, rReadAcc.Width() ); nX < nWidth; nX++ )
			SetPixel( nY, nX, rReadAcc.GetPixel( nY, nX ) );
}

// ------------------------------------------------------------------

void BitmapWriteAccess::CopyScanline( long nY, ConstScanline aSrcScanline,
									  sal_uLong nSrcScanlineFormat, sal_uLong nSrcScanlineSize )
{
	const sal_uLong nFormat = BMP_SCANLINE_FORMAT( nSrcScanlineFormat );

	DBG_ASSERT( ( nY >= 0 ) && ( nY < mpBuffer->mnHeight ), "y-coordinate in destination out of range!" );
	DBG_ASSERT( ( HasPalette() && nFormat <= BMP_FORMAT_8BIT_PAL ) ||
				( !HasPalette() && nFormat > BMP_FORMAT_8BIT_PAL ),
				"No copying possible between palette and non palette scanlines!" );

	const sal_uLong nCount = Min( GetScanlineSize(), nSrcScanlineSize );

	if( nCount )
	{
		if( GetScanlineFormat() == BMP_SCANLINE_FORMAT( nSrcScanlineFormat ) )
			memcpy( mpScanBuf[ nY ], aSrcScanline, nCount );
		else
		{
			DBG_ASSERT( nFormat != BMP_FORMAT_8BIT_TC_MASK &&
                        nFormat != BMP_FORMAT_16BIT_TC_MSB_MASK && nFormat != BMP_FORMAT_16BIT_TC_LSB_MASK &&
						nFormat != BMP_FORMAT_24BIT_TC_MASK && nFormat != BMP_FORMAT_32BIT_TC_MASK,
						"No support for pixel formats with color masks yet!" );

            // TODO: use fastbmp infrastructure
            FncGetPixel pFncGetPixel;

			switch( nFormat )
			{
				case( BMP_FORMAT_1BIT_MSB_PAL ):	pFncGetPixel = GetPixelFor_1BIT_MSB_PAL; break;
				case( BMP_FORMAT_1BIT_LSB_PAL ):	pFncGetPixel = GetPixelFor_1BIT_LSB_PAL; break;
				case( BMP_FORMAT_4BIT_MSN_PAL ):	pFncGetPixel = GetPixelFor_4BIT_MSN_PAL; break;
				case( BMP_FORMAT_4BIT_LSN_PAL ):	pFncGetPixel = GetPixelFor_4BIT_LSN_PAL; break;
				case( BMP_FORMAT_8BIT_PAL ):		pFncGetPixel = GetPixelFor_8BIT_PAL; break;
				case( BMP_FORMAT_8BIT_TC_MASK ):	pFncGetPixel = GetPixelFor_8BIT_TC_MASK; break;
				case( BMP_FORMAT_16BIT_TC_MSB_MASK ):	pFncGetPixel = GetPixelFor_16BIT_TC_MSB_MASK; break;
				case( BMP_FORMAT_16BIT_TC_LSB_MASK ):	pFncGetPixel = GetPixelFor_16BIT_TC_LSB_MASK; break;
				case( BMP_FORMAT_24BIT_TC_BGR ):	pFncGetPixel = GetPixelFor_24BIT_TC_BGR; break;
				case( BMP_FORMAT_24BIT_TC_RGB ):	pFncGetPixel = GetPixelFor_24BIT_TC_RGB; break;
				case( BMP_FORMAT_24BIT_TC_MASK ):	pFncGetPixel = GetPixelFor_24BIT_TC_MASK; break;
				case( BMP_FORMAT_32BIT_TC_ABGR ):	pFncGetPixel = GetPixelFor_32BIT_TC_ABGR; break;
				case( BMP_FORMAT_32BIT_TC_ARGB ):	pFncGetPixel = GetPixelFor_32BIT_TC_ARGB; break;
				case( BMP_FORMAT_32BIT_TC_BGRA ):	pFncGetPixel = GetPixelFor_32BIT_TC_BGRA; break;
				case( BMP_FORMAT_32BIT_TC_RGBA ):	pFncGetPixel = GetPixelFor_32BIT_TC_RGBA; break;
				case( BMP_FORMAT_32BIT_TC_MASK ):	pFncGetPixel = GetPixelFor_32BIT_TC_MASK; break;

				default:
					pFncGetPixel = NULL;
				break;
			}

			if( pFncGetPixel )
			{
				const ColorMask aDummyMask;

				for( long nX = 0L, nWidth = mpBuffer->mnWidth; nX < nWidth; nX++ )
					SetPixel( nY, nX, pFncGetPixel( aSrcScanline, nX, aDummyMask ) );
			}
		}
	}
}


// ------------------------------------------------------------------

void BitmapWriteAccess::CopyBuffer( const BitmapReadAccess& rReadAcc )
{
	DBG_ASSERT( ( HasPalette() && rReadAcc.HasPalette() ) || ( !HasPalette() && !rReadAcc.HasPalette() ), "No copying possible between palette bitmap and TC bitmap!" );

	if( ( GetScanlineFormat() == rReadAcc.GetScanlineFormat() ) &&
		( GetScanlineSize() == rReadAcc.GetScanlineSize() ) )
	{
		const long	nHeight = Min( mpBuffer->mnHeight, rReadAcc.Height() );
		const sal_uLong nCount = nHeight * mpBuffer->mnScanlineSize;

		memcpy( mpBuffer->mpBits, rReadAcc.GetBuffer(), nCount );
	}
	else
		for( long nY = 0L, nHeight = Min( mpBuffer->mnHeight, rReadAcc.Height() ); nY < nHeight; nY++ )
			CopyScanline( nY, rReadAcc );
}
