/**************************************************************
 * 
 * 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 <com/sun/star/util/Endianness.hpp>
#include <com/sun/star/rendering/ColorComponentTag.hpp>
#include <com/sun/star/rendering/ColorSpaceType.hpp>
#include <com/sun/star/rendering/RenderingIntent.hpp>

#include <rtl/instance.hxx>
#include <vos/mutex.hxx>

#include <tools/diagnose_ex.h>
#include <canvasbitmap.hxx>
#include <vcl/canvastools.hxx>
#include <vcl/bmpacc.hxx>
#include <vcl/svapp.hxx>

#include <algorithm>


using namespace ::vcl::unotools;
using namespace ::com::sun::star;

namespace
{
    // TODO(Q3): move to o3tl bithacks or somesuch. A similar method is in canvas/canvastools.hxx
    
    // Good ole HAKMEM tradition. Calc number of 1 bits in 32bit word,
    // unrolled loop. See e.g. Hackers Delight, p. 66
    inline sal_Int32 bitcount( sal_uInt32 val )
    {
        val = val - ((val >> 1) & 0x55555555);
        val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
        val = (val + (val >> 4)) & 0x0F0F0F0F;
        val = val + (val >> 8);
        val = val + (val >> 16);
        return sal_Int32(val & 0x0000003F);
    }
}

void VclCanvasBitmap::setComponentInfo( sal_uLong redShift, sal_uLong greenShift, sal_uLong blueShift )
{
    // sort channels in increasing order of appearance in the pixel
    // (starting with the least significant bits)
    sal_Int8 redPos(0);
    sal_Int8 greenPos(1);
    sal_Int8 bluePos(2);

    if( redShift > greenShift )
    {
        std::swap(redPos,greenPos);
        if( redShift > blueShift )
        {
            std::swap(redPos,bluePos);
            if( greenShift > blueShift )
                std::swap(greenPos,bluePos);
        }
    }
    else
    {
        if( greenShift > blueShift )
        {
            std::swap(greenPos,bluePos);
            if( redShift > blueShift )
                std::swap(redPos,bluePos);
        }
    }

    m_aComponentTags.realloc(3);
    sal_Int8* pTags = m_aComponentTags.getArray();
    pTags[redPos]   = rendering::ColorComponentTag::RGB_RED;
    pTags[greenPos] = rendering::ColorComponentTag::RGB_GREEN;
    pTags[bluePos]  = rendering::ColorComponentTag::RGB_BLUE;

    m_aComponentBitCounts.realloc(3);
    sal_Int32* pCounts = m_aComponentBitCounts.getArray();
    pCounts[redPos]    = bitcount(sal::static_int_cast<sal_uInt32>(redShift));
    pCounts[greenPos]  = bitcount(sal::static_int_cast<sal_uInt32>(greenShift));
    pCounts[bluePos]   = bitcount(sal::static_int_cast<sal_uInt32>(blueShift));
}

VclCanvasBitmap::VclCanvasBitmap( const BitmapEx& rBitmap ) :
    m_aBmpEx( rBitmap ),
    m_aBitmap( rBitmap.GetBitmap() ),
    m_aAlpha(),
    m_pBmpAcc( m_aBitmap.AcquireReadAccess() ),
    m_pAlphaAcc( NULL ),
    m_aComponentTags(),
    m_aComponentBitCounts(),
    m_aLayout(),
    m_nBitsPerInputPixel(0),
    m_nBitsPerOutputPixel(0),
    m_nRedIndex(-1),
    m_nGreenIndex(-1),
    m_nBlueIndex(-1),
    m_nAlphaIndex(-1),
    m_nIndexIndex(-1),
    m_nEndianness(0),
    m_bSwap(false),
    m_bPalette(false)
{
    if( m_aBmpEx.IsTransparent() )
    {
        m_aAlpha = m_aBmpEx.IsAlpha() ? m_aBmpEx.GetAlpha().GetBitmap() : m_aBmpEx.GetMask();
        m_pAlphaAcc = m_aAlpha.AcquireReadAccess();
    }

    m_aLayout.ScanLines      = 0;
    m_aLayout.ScanLineBytes  = 0;
    m_aLayout.ScanLineStride = 0;
    m_aLayout.PlaneStride    = 0;
    m_aLayout.ColorSpace.clear();
    m_aLayout.Palette.clear();
    m_aLayout.IsMsbFirst     = sal_False;

    if( m_pBmpAcc )
    {
        m_aLayout.ScanLines      = m_pBmpAcc->Height();
        m_aLayout.ScanLineBytes  = (m_pBmpAcc->GetBitCount()*m_pBmpAcc->Width() + 7) / 8;
        m_aLayout.ScanLineStride = m_pBmpAcc->GetScanlineSize();
        m_aLayout.PlaneStride    = 0;

        switch( m_pBmpAcc->GetScanlineFormat() )
        {
            case BMP_FORMAT_1BIT_MSB_PAL:
                m_bPalette           = true;
                m_nBitsPerInputPixel = 1;
                m_nEndianness        = util::Endianness::LITTLE; // doesn't matter
                m_aLayout.IsMsbFirst = sal_True;
                break;

            case BMP_FORMAT_1BIT_LSB_PAL:
                m_bPalette           = true;
                m_nBitsPerInputPixel = 1;
                m_nEndianness        = util::Endianness::LITTLE; // doesn't matter
                m_aLayout.IsMsbFirst = sal_False;
                break;

            case BMP_FORMAT_4BIT_MSN_PAL:
                m_bPalette           = true;
                m_nBitsPerInputPixel = 4;
                m_nEndianness        = util::Endianness::LITTLE; // doesn't matter
                m_aLayout.IsMsbFirst = sal_True;
                break;

            case BMP_FORMAT_4BIT_LSN_PAL:
                m_bPalette           = true;
                m_nBitsPerInputPixel = 4;
                m_nEndianness        = util::Endianness::LITTLE; // doesn't matter
                m_aLayout.IsMsbFirst = sal_False;
                break;

            case BMP_FORMAT_8BIT_PAL:
                m_bPalette           = true;
                m_nBitsPerInputPixel = 8;
                m_nEndianness        = util::Endianness::LITTLE; // doesn't matter
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter
                break;
                
            case BMP_FORMAT_8BIT_TC_MASK:
                m_bPalette           = false;
                m_nBitsPerInputPixel = 8;
                m_nEndianness        = util::Endianness::LITTLE; // doesn't matter
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter
                setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(),
                                  m_pBmpAcc->GetColorMask().GetGreenMask(),
                                  m_pBmpAcc->GetColorMask().GetBlueMask() );
                break;

            case BMP_FORMAT_16BIT_TC_MSB_MASK:
                m_bPalette           = false;
                m_nBitsPerInputPixel = 16;
                m_nEndianness        = util::Endianness::BIG;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter
                setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(),
                                  m_pBmpAcc->GetColorMask().GetGreenMask(),
                                  m_pBmpAcc->GetColorMask().GetBlueMask() );
                break;

            case BMP_FORMAT_16BIT_TC_LSB_MASK:
                m_bPalette           = false;
                m_nBitsPerInputPixel = 16;
                m_nEndianness        = util::Endianness::LITTLE;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter
                setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(),
                                  m_pBmpAcc->GetColorMask().GetGreenMask(),
                                  m_pBmpAcc->GetColorMask().GetBlueMask() );
                break;

            case BMP_FORMAT_24BIT_TC_BGR:
                m_bPalette           = false;
                m_nBitsPerInputPixel = 24;
                m_nEndianness        = util::Endianness::LITTLE;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter
                setComponentInfo( 0xff0000LL,
                                  0x00ff00LL,
                                  0x0000ffLL );
                break;

            case BMP_FORMAT_24BIT_TC_RGB:
                m_bPalette           = false;
                m_nBitsPerInputPixel = 24;
                m_nEndianness        = util::Endianness::LITTLE;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter
                setComponentInfo( 0x0000ffLL,
                                  0x00ff00LL,
                                  0xff0000LL );
                break;

            case BMP_FORMAT_24BIT_TC_MASK:
                m_bPalette           = false;
                m_nBitsPerInputPixel = 24;
                m_nEndianness        = util::Endianness::LITTLE;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter
                setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(),
                                  m_pBmpAcc->GetColorMask().GetGreenMask(),
                                  m_pBmpAcc->GetColorMask().GetBlueMask() );
                break;

            case BMP_FORMAT_32BIT_TC_ABGR:
            {
                m_bPalette           = false;
                m_nBitsPerInputPixel = 32;
                m_nEndianness        = util::Endianness::LITTLE;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter

                m_aComponentTags.realloc(4);
                sal_Int8* pTags = m_aComponentTags.getArray();
                pTags[0]        = rendering::ColorComponentTag::ALPHA;
                pTags[1]        = rendering::ColorComponentTag::RGB_BLUE;
                pTags[2]        = rendering::ColorComponentTag::RGB_GREEN;
                pTags[3]        = rendering::ColorComponentTag::RGB_RED;

                m_aComponentBitCounts.realloc(4);
                sal_Int32* pCounts = m_aComponentBitCounts.getArray();
                pCounts[0]         = 8;
                pCounts[1]         = 8;
                pCounts[2]         = 8;
                pCounts[3]         = 8;
                
                m_nRedIndex   = 3;
                m_nGreenIndex = 2;
                m_nBlueIndex  = 1;
                m_nAlphaIndex = 0;
            }
            break;

            case BMP_FORMAT_32BIT_TC_ARGB:
            {
                m_bPalette           = false;
                m_nBitsPerInputPixel = 32;
                m_nEndianness        = util::Endianness::LITTLE;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter

                m_aComponentTags.realloc(4);
                sal_Int8* pTags = m_aComponentTags.getArray();
                pTags[0]        = rendering::ColorComponentTag::ALPHA;
                pTags[1]        = rendering::ColorComponentTag::RGB_RED;
                pTags[2]        = rendering::ColorComponentTag::RGB_GREEN;
                pTags[3]        = rendering::ColorComponentTag::RGB_BLUE;

                m_aComponentBitCounts.realloc(4);
                sal_Int32* pCounts = m_aComponentBitCounts.getArray();
                pCounts[0]         = 8;
                pCounts[1]         = 8;
                pCounts[2]         = 8;
                pCounts[3]         = 8;

                m_nRedIndex   = 1;
                m_nGreenIndex = 2;
                m_nBlueIndex  = 3;
                m_nAlphaIndex = 0;
            }
            break;

            case BMP_FORMAT_32BIT_TC_BGRA:
            {
                m_bPalette           = false;
                m_nBitsPerInputPixel = 32;
                m_nEndianness        = util::Endianness::LITTLE;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter

                m_aComponentTags.realloc(4);
                sal_Int8* pTags = m_aComponentTags.getArray();
                pTags[0]        = rendering::ColorComponentTag::RGB_BLUE;
                pTags[1]        = rendering::ColorComponentTag::RGB_GREEN;
                pTags[2]        = rendering::ColorComponentTag::RGB_RED;
                pTags[3]        = rendering::ColorComponentTag::ALPHA;

                m_aComponentBitCounts.realloc(4);
                sal_Int32* pCounts = m_aComponentBitCounts.getArray();
                pCounts[0]         = 8;
                pCounts[1]         = 8;
                pCounts[2]         = 8;
                pCounts[3]         = 8;

                m_nRedIndex   = 2;
                m_nGreenIndex = 1;
                m_nBlueIndex  = 0;
                m_nAlphaIndex = 3;
            }
            break;

            case BMP_FORMAT_32BIT_TC_RGBA:
            {
                m_bPalette           = false;
                m_nBitsPerInputPixel = 32;
                m_nEndianness        = util::Endianness::LITTLE;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter

                m_aComponentTags.realloc(4);
                sal_Int8* pTags = m_aComponentTags.getArray();
                pTags[0]        = rendering::ColorComponentTag::RGB_RED;
                pTags[1]        = rendering::ColorComponentTag::RGB_GREEN;
                pTags[2]        = rendering::ColorComponentTag::RGB_BLUE;
                pTags[3]        = rendering::ColorComponentTag::ALPHA;

                m_aComponentBitCounts.realloc(4);
                sal_Int32* pCounts = m_aComponentBitCounts.getArray();
                pCounts[0]         = 8;
                pCounts[1]         = 8;
                pCounts[2]         = 8;
                pCounts[3]         = 8;

                m_nRedIndex   = 0;
                m_nGreenIndex = 1;
                m_nBlueIndex  = 2;
                m_nAlphaIndex = 3;
            }
            break;

            case BMP_FORMAT_32BIT_TC_MASK:
                m_bPalette           = false;
                m_nBitsPerInputPixel = 32;
                m_nEndianness        = util::Endianness::LITTLE;
                m_aLayout.IsMsbFirst = sal_False; // doesn't matter
                setComponentInfo( m_pBmpAcc->GetColorMask().GetRedMask(),
                                  m_pBmpAcc->GetColorMask().GetGreenMask(),
                                  m_pBmpAcc->GetColorMask().GetBlueMask() );
                break;

            default:
                DBG_ERROR( "unsupported bitmap format" );
                break;
        }

        if( m_bPalette )
        {
            m_aComponentTags.realloc(1);
            m_aComponentTags[0] = rendering::ColorComponentTag::INDEX;

            m_aComponentBitCounts.realloc(1);
            m_aComponentBitCounts[0] = m_nBitsPerInputPixel;

            m_nIndexIndex = 0;
        }

        m_nBitsPerOutputPixel = m_nBitsPerInputPixel;
        if( m_aBmpEx.IsTransparent() )
        {
            // TODO(P1): need to interleave alpha with bitmap data -
            // won't fuss with less-than-8 bit for now
            m_nBitsPerOutputPixel = std::max(sal_Int32(8),m_nBitsPerInputPixel);

            // check whether alpha goes in front or behind the
            // bitcount sequence. If pixel format is little endian,
            // put it behind all the other channels. If it's big
            // endian, put it in front (because later, the actual data
            // always gets written after the pixel data)

            // TODO(Q1): slight catch - in the case of the
            // BMP_FORMAT_32BIT_XX_ARGB formats, duplicate alpha
            // channels might happen!
            m_aComponentTags.realloc(m_aComponentTags.getLength()+1);
            m_aComponentTags[m_aComponentTags.getLength()-1] = rendering::ColorComponentTag::ALPHA;

            m_aComponentBitCounts.realloc(m_aComponentBitCounts.getLength()+1);
            m_aComponentBitCounts[m_aComponentBitCounts.getLength()-1] = m_aBmpEx.IsAlpha() ? 8 : 1;

            if( m_nEndianness == util::Endianness::BIG )
            {
                // put alpha in front of all the color channels
                sal_Int8*  pTags  =m_aComponentTags.getArray();
                sal_Int32* pCounts=m_aComponentBitCounts.getArray();
                std::rotate(pTags, 
                            pTags+m_aComponentTags.getLength()-1, 
                            pTags+m_aComponentTags.getLength());
                std::rotate(pCounts, 
                            pCounts+m_aComponentBitCounts.getLength()-1, 
                            pCounts+m_aComponentBitCounts.getLength());
                ++m_nRedIndex;
                ++m_nGreenIndex;
                ++m_nBlueIndex;
                ++m_nIndexIndex;
                m_nAlphaIndex=0;
            }

            // always add a full byte to the pixel size, otherwise
            // pixel packing hell breaks loose.
            m_nBitsPerOutputPixel += 8;

            // adapt scanline parameters
            const Size aSize = m_aBitmap.GetSizePixel();
            m_aLayout.ScanLineBytes  = 
            m_aLayout.ScanLineStride = (aSize.Width()*m_nBitsPerOutputPixel + 7)/8;
        }
    }
}

VclCanvasBitmap::~VclCanvasBitmap()
{
    if( m_pAlphaAcc )
        m_aAlpha.ReleaseAccess(m_pAlphaAcc);
    if( m_pBmpAcc )
        m_aBitmap.ReleaseAccess(m_pBmpAcc);
}

// XBitmap
geometry::IntegerSize2D SAL_CALL VclCanvasBitmap::getSize() throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    return integerSize2DFromSize( m_aBitmap.GetSizePixel() );
}

::sal_Bool SAL_CALL VclCanvasBitmap::hasAlpha() throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    return m_aBmpEx.IsTransparent();
}

uno::Reference< rendering::XBitmap > SAL_CALL VclCanvasBitmap::getScaledBitmap( const geometry::RealSize2D& newSize, 
                                                                                sal_Bool beFast ) throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    
    BitmapEx aNewBmp( m_aBitmap );
    aNewBmp.Scale( sizeFromRealSize2D( newSize ), beFast ? BMP_SCALE_FASTESTINTERPOLATE : BMP_SCALE_INTERPOLATE );
    return uno::Reference<rendering::XBitmap>( new VclCanvasBitmap( aNewBmp ) );
}

// XIntegerReadOnlyBitmap
uno::Sequence< sal_Int8 > SAL_CALL VclCanvasBitmap::getData( rendering::IntegerBitmapLayout& 	 bitmapLayout,
                                                             const geometry::IntegerRectangle2D& rect ) throw( lang::IndexOutOfBoundsException, 
                                                                                                               rendering::VolatileContentDestroyedException, 
                                                                                                               uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    bitmapLayout = getMemoryLayout();

    const ::Rectangle aRequestedArea( vcl::unotools::rectangleFromIntegerRectangle2D(rect) );
    if( aRequestedArea.IsEmpty() )
        return uno::Sequence< sal_Int8 >();
        
    // Invalid/empty bitmap: no data available
    if( !m_pBmpAcc )
        throw lang::IndexOutOfBoundsException();
    if( m_aBmpEx.IsTransparent() && !m_pAlphaAcc )
        throw lang::IndexOutOfBoundsException();

    if( aRequestedArea.Left() < 0 || aRequestedArea.Top() < 0 ||
        aRequestedArea.Right() > m_pBmpAcc->Width() ||
        aRequestedArea.Bottom() > m_pBmpAcc->Height() )
    {
        throw lang::IndexOutOfBoundsException();
    }

    uno::Sequence< sal_Int8 > aRet; 
    Rectangle aRequestedBytes( aRequestedArea );

    // adapt to byte boundaries
    aRequestedBytes.Left()  = aRequestedArea.Left()*m_nBitsPerOutputPixel/8;
    aRequestedBytes.Right() = (aRequestedArea.Right()*m_nBitsPerOutputPixel + 7)/8;
        
    // copy stuff to output sequence
    aRet.realloc(aRequestedBytes.getWidth()*aRequestedBytes.getHeight());
    sal_Int8* pOutBuf = aRet.getArray();

    bitmapLayout.ScanLines     = aRequestedBytes.getHeight();
    bitmapLayout.ScanLineBytes = 
    bitmapLayout.ScanLineStride= aRequestedBytes.getWidth();

    sal_Int32 nScanlineStride=bitmapLayout.ScanLineStride;
    if( !(m_pBmpAcc->GetScanlineFormat() & BMP_FORMAT_TOP_DOWN) )
    {
        pOutBuf += bitmapLayout.ScanLineStride*(aRequestedBytes.getHeight()-1);
        nScanlineStride *= -1;
    }

    if( !m_aBmpEx.IsTransparent() )
    {
        OSL_ENSURE(m_pBmpAcc,"Invalid bmp read access");

        // can return bitmap data as-is
        for( long y=aRequestedBytes.Top(); y<aRequestedBytes.Bottom(); ++y )
        {
            Scanline pScan = m_pBmpAcc->GetScanline(y);
            rtl_copyMemory(pOutBuf, pScan+aRequestedBytes.Left(), aRequestedBytes.getWidth());
            pOutBuf += nScanlineStride;
        }
    }
    else
    {
        OSL_ENSURE(m_pBmpAcc,"Invalid bmp read access");
        OSL_ENSURE(m_pAlphaAcc,"Invalid alpha read access");

        // interleave alpha with bitmap data - note, bitcount is
        // always integer multiple of 8
        OSL_ENSURE((m_nBitsPerOutputPixel & 0x07) == 0,
                   "Transparent bitmap bitcount not integer multiple of 8" );

        for( long y=aRequestedArea.Top(); y<aRequestedArea.Bottom(); ++y )
        {
            sal_Int8* pOutScan = pOutBuf;

            if( m_nBitsPerInputPixel < 8 )
            {
                // input less than a byte - copy via GetPixel()
                for( long x=aRequestedArea.Left(); x<aRequestedArea.Right(); ++x )
                {
                    *pOutScan++ = m_pBmpAcc->GetPixelIndex(y,x);
                    *pOutScan++ = m_pAlphaAcc->GetPixelIndex(y,x);
                }
            }
            else
            {
                const long nNonAlphaBytes( m_nBitsPerInputPixel/8 );
                const long nScanlineOffsetLeft(aRequestedArea.Left()*nNonAlphaBytes);
                Scanline  pScan = m_pBmpAcc->GetScanline(y) + nScanlineOffsetLeft;

                // input integer multiple of byte - copy directly
                for( long x=aRequestedArea.Left(); x<aRequestedArea.Right(); ++x )
                {
                    for( long i=0; i<nNonAlphaBytes; ++i )
                        *pOutScan++ = *pScan++;
                    *pOutScan++ = m_pAlphaAcc->GetPixelIndex( y, x );
                }
            }

            pOutBuf += nScanlineStride;
        }
    }

    return aRet;
}

uno::Sequence< sal_Int8 > SAL_CALL VclCanvasBitmap::getPixel( rendering::IntegerBitmapLayout&	bitmapLayout,
                                                              const geometry::IntegerPoint2D&	pos ) throw (lang::IndexOutOfBoundsException, 
                                                                                                             rendering::VolatileContentDestroyedException, 
                                                                                                             uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    bitmapLayout = getMemoryLayout();

    // Invalid/empty bitmap: no data available
    if( !m_pBmpAcc )
        throw lang::IndexOutOfBoundsException();
    if( m_aBmpEx.IsTransparent() && !m_pAlphaAcc )
        throw lang::IndexOutOfBoundsException();

    if( pos.X < 0 || pos.Y < 0 ||
        pos.X > m_pBmpAcc->Width() || pos.Y > m_pBmpAcc->Height() )
    {
        throw lang::IndexOutOfBoundsException();
    }

    uno::Sequence< sal_Int8 > aRet((m_nBitsPerOutputPixel + 7)/8);
    sal_Int8* pOutBuf = aRet.getArray();

    // copy stuff to output sequence
    bitmapLayout.ScanLines     = 1;
    bitmapLayout.ScanLineBytes = 
    bitmapLayout.ScanLineStride= aRet.getLength();

    const long nScanlineLeftOffset( pos.X*m_nBitsPerInputPixel/8 );
    if( !m_aBmpEx.IsTransparent() )
    {
        OSL_ENSURE(m_pBmpAcc,"Invalid bmp read access");

        // can return bitmap data as-is
        Scanline pScan = m_pBmpAcc->GetScanline(pos.Y);
        rtl_copyMemory(pOutBuf, pScan+nScanlineLeftOffset, aRet.getLength() );
    }
    else
    {
        OSL_ENSURE(m_pBmpAcc,"Invalid bmp read access");
        OSL_ENSURE(m_pAlphaAcc,"Invalid alpha read access");

        // interleave alpha with bitmap data - note, bitcount is
        // always integer multiple of 8
        OSL_ENSURE((m_nBitsPerOutputPixel & 0x07) == 0,
                   "Transparent bitmap bitcount not integer multiple of 8" );

        if( m_nBitsPerInputPixel < 8 )
        {
            // input less than a byte - copy via GetPixel()
            *pOutBuf++ = m_pBmpAcc->GetPixelIndex(pos.Y,pos.X);
            *pOutBuf   = m_pAlphaAcc->GetPixelIndex(pos.Y,pos.X);
        }
        else
        {
            const long nNonAlphaBytes( m_nBitsPerInputPixel/8 );
            Scanline  pScan = m_pBmpAcc->GetScanline(pos.Y);

            // input integer multiple of byte - copy directly
            rtl_copyMemory(pOutBuf, pScan+nScanlineLeftOffset, nNonAlphaBytes );
            pOutBuf += nNonAlphaBytes;
            *pOutBuf++ = m_pAlphaAcc->GetPixelIndex(pos.Y,pos.X);
        }
    }

    return aRet;
}

uno::Reference< rendering::XBitmapPalette > SAL_CALL VclCanvasBitmap::getPalette() throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    uno::Reference< XBitmapPalette > aRet;
    if( m_bPalette )
        aRet.set(this);

    return aRet;
}

rendering::IntegerBitmapLayout SAL_CALL VclCanvasBitmap::getMemoryLayout() throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    rendering::IntegerBitmapLayout aLayout( m_aLayout );

    // only set references to self on separate copy of
    // IntegerBitmapLayout - if we'd set that on m_aLayout, we'd have
    // a circular reference!
    if( m_bPalette )
        aLayout.Palette.set( this );

    aLayout.ColorSpace.set( this );

    return aLayout;
}

sal_Int32 SAL_CALL VclCanvasBitmap::getNumberOfEntries() throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    if( !m_pBmpAcc )
        return 0;

    return m_pBmpAcc->HasPalette() ? m_pBmpAcc->GetPaletteEntryCount() : 0 ;
}

sal_Bool SAL_CALL VclCanvasBitmap::getIndex( uno::Sequence< double >& o_entry, sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_uInt16 nCount( m_pBmpAcc ? 
                         (m_pBmpAcc->HasPalette() ? m_pBmpAcc->GetPaletteEntryCount() : 0 ) : 0 );
    OSL_ENSURE(nIndex >= 0 && nIndex < nCount,"Palette index out of range");
    if( nIndex < 0 || nIndex >= nCount )
        throw lang::IndexOutOfBoundsException(::rtl::OUString::createFromAscii("Palette index out of range"),
                                              static_cast<rendering::XBitmapPalette*>(this));

    const BitmapColor aCol = m_pBmpAcc->GetPaletteColor(sal::static_int_cast<sal_uInt16>(nIndex));
    o_entry.realloc(3);
    double* pColor=o_entry.getArray();
    pColor[0] = aCol.GetRed();
    pColor[1] = aCol.GetGreen();
    pColor[2] = aCol.GetBlue();

    return sal_True; // no palette transparency here.
}

sal_Bool SAL_CALL VclCanvasBitmap::setIndex( const uno::Sequence< double >&, sal_Bool, sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, lang::IllegalArgumentException, uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_uInt16 nCount( m_pBmpAcc ? 
                         (m_pBmpAcc->HasPalette() ? m_pBmpAcc->GetPaletteEntryCount() : 0 ) : 0 );

    OSL_ENSURE(nIndex >= 0 && nIndex < nCount,"Palette index out of range");
    if( nIndex < 0 || nIndex >= nCount )
        throw lang::IndexOutOfBoundsException(::rtl::OUString::createFromAscii("Palette index out of range"),
                                              static_cast<rendering::XBitmapPalette*>(this));

    return sal_False; // read-only implementation
}

namespace 
{ 
    struct PaletteColorSpaceHolder: public rtl::StaticWithInit<uno::Reference<rendering::XColorSpace>,
                                                               PaletteColorSpaceHolder> 
    {
        uno::Reference<rendering::XColorSpace> operator()()
        {
            return vcl::unotools::createStandardColorSpace();
        }
    }; 
}

uno::Reference< rendering::XColorSpace > SAL_CALL VclCanvasBitmap::getColorSpace(  ) throw (uno::RuntimeException)
{
    // this is the method from XBitmapPalette. Return palette color
    // space here
    return PaletteColorSpaceHolder::get();
}

sal_Int8 SAL_CALL VclCanvasBitmap::getType(  ) throw (uno::RuntimeException)
{
    return rendering::ColorSpaceType::RGB;
}

uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::getComponentTags(  ) throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );
    return m_aComponentTags;
}

sal_Int8 SAL_CALL VclCanvasBitmap::getRenderingIntent(  ) throw (uno::RuntimeException)
{
    return rendering::RenderingIntent::PERCEPTUAL;
}

uno::Sequence< ::beans::PropertyValue > SAL_CALL VclCanvasBitmap::getProperties(  ) throw (uno::RuntimeException)
{
    return uno::Sequence< ::beans::PropertyValue >();
}

uno::Sequence< double > SAL_CALL VclCanvasBitmap::convertColorSpace( const uno::Sequence< double >& deviceColor, 
                                                                     const uno::Reference< ::rendering::XColorSpace >& targetColorSpace ) throw (uno::RuntimeException)
{
    // TODO(P3): if we know anything about target
    // colorspace, this can be greatly sped up
    uno::Sequence<rendering::ARGBColor> aIntermediate(
        convertToARGB(deviceColor));
    return targetColorSpace->convertFromARGB(aIntermediate);
}

uno::Sequence<rendering::RGBColor> SAL_CALL VclCanvasBitmap::convertToRGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_Size  nLen( deviceColor.getLength() );
    const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength());
    ENSURE_ARG_OR_THROW2(nLen%nComponentsPerPixel==0, 
                         "number of channels no multiple of pixel element count",
                         static_cast<rendering::XBitmapPalette*>(this), 01);

    uno::Sequence< rendering::RGBColor > aRes(nLen/nComponentsPerPixel);
    rendering::RGBColor* pOut( aRes.getArray() );

    if( m_bPalette )
    {
        OSL_ENSURE(m_nIndexIndex != -1,
                   "Invalid color channel indices");
        ENSURE_OR_THROW(m_pBmpAcc,
                        "Unable to get BitmapAccess");

        for( sal_Size i=0; i<nLen; i+=nComponentsPerPixel )
        {
            const BitmapColor aCol = m_pBmpAcc->GetPaletteColor(
                sal::static_int_cast<sal_uInt16>(deviceColor[i+m_nIndexIndex]));

            // TODO(F3): Convert result to sRGB color space
            *pOut++ = rendering::RGBColor(toDoubleColor(aCol.GetRed()),
                                          toDoubleColor(aCol.GetGreen()),
                                          toDoubleColor(aCol.GetBlue()));
        }
    }
    else
    {
        OSL_ENSURE(m_nRedIndex != -1 && m_nGreenIndex != -1 && m_nBlueIndex != -1,
                   "Invalid color channel indices");

        for( sal_Size i=0; i<nLen; i+=nComponentsPerPixel )
        {
            // TODO(F3): Convert result to sRGB color space
            *pOut++ = rendering::RGBColor(
                deviceColor[i+m_nRedIndex],
                deviceColor[i+m_nGreenIndex],
                deviceColor[i+m_nBlueIndex]);
        }
    }

    return aRes;
}

uno::Sequence<rendering::ARGBColor> SAL_CALL VclCanvasBitmap::convertToARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_Size  nLen( deviceColor.getLength() );
    const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength());
    ENSURE_ARG_OR_THROW2(nLen%nComponentsPerPixel==0, 
                         "number of channels no multiple of pixel element count",
                         static_cast<rendering::XBitmapPalette*>(this), 01);

    uno::Sequence< rendering::ARGBColor > aRes(nLen/nComponentsPerPixel);
    rendering::ARGBColor* pOut( aRes.getArray() );

    if( m_bPalette )
    {
        OSL_ENSURE(m_nIndexIndex != -1,
                   "Invalid color channel indices");
        ENSURE_OR_THROW(m_pBmpAcc,
                        "Unable to get BitmapAccess");

        for( sal_Size i=0; i<nLen; i+=nComponentsPerPixel )
        {
            const BitmapColor aCol = m_pBmpAcc->GetPaletteColor(
                sal::static_int_cast<sal_uInt16>(deviceColor[i+m_nIndexIndex]));

            // TODO(F3): Convert result to sRGB color space
            const double nAlpha( m_nAlphaIndex != -1 ? 1.0 - deviceColor[i+m_nAlphaIndex] : 1.0 );
            *pOut++ = rendering::ARGBColor(nAlpha,
                                           toDoubleColor(aCol.GetRed()),
                                           toDoubleColor(aCol.GetGreen()),
                                           toDoubleColor(aCol.GetBlue()));
        }
    }
    else
    {
        OSL_ENSURE(m_nRedIndex != -1 && m_nGreenIndex != -1 && m_nBlueIndex != -1,
                   "Invalid color channel indices");

        for( sal_Size i=0; i<nLen; i+=nComponentsPerPixel )
        {
            // TODO(F3): Convert result to sRGB color space
            const double nAlpha( m_nAlphaIndex != -1 ? 1.0 - deviceColor[i+m_nAlphaIndex] : 1.0 );
            *pOut++ = rendering::ARGBColor(
                nAlpha,
                deviceColor[i+m_nRedIndex],
                deviceColor[i+m_nGreenIndex],
                deviceColor[i+m_nBlueIndex]);
        }
    }

    return aRes;
}

uno::Sequence<rendering::ARGBColor> SAL_CALL VclCanvasBitmap::convertToPARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_Size  nLen( deviceColor.getLength() );
    const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength());
    ENSURE_ARG_OR_THROW2(nLen%nComponentsPerPixel==0, 
                         "number of channels no multiple of pixel element count",
                         static_cast<rendering::XBitmapPalette*>(this), 01);

    uno::Sequence< rendering::ARGBColor > aRes(nLen/nComponentsPerPixel);
    rendering::ARGBColor* pOut( aRes.getArray() );

    if( m_bPalette )
    {
        OSL_ENSURE(m_nIndexIndex != -1,
                   "Invalid color channel indices");
        ENSURE_OR_THROW(m_pBmpAcc,
                        "Unable to get BitmapAccess");

        for( sal_Size i=0; i<nLen; i+=nComponentsPerPixel )
        {
            const BitmapColor aCol = m_pBmpAcc->GetPaletteColor(
                sal::static_int_cast<sal_uInt16>(deviceColor[i+m_nIndexIndex]));

            // TODO(F3): Convert result to sRGB color space
            const double nAlpha( m_nAlphaIndex != -1 ? 1.0 - deviceColor[i+m_nAlphaIndex] : 1.0 );
            *pOut++ = rendering::ARGBColor(nAlpha,
                                           nAlpha*toDoubleColor(aCol.GetRed()),
                                           nAlpha*toDoubleColor(aCol.GetGreen()),
                                           nAlpha*toDoubleColor(aCol.GetBlue()));
        }
    }
    else
    {
        OSL_ENSURE(m_nRedIndex != -1 && m_nGreenIndex != -1 && m_nBlueIndex != -1,
                   "Invalid color channel indices");

        for( sal_Size i=0; i<nLen; i+=nComponentsPerPixel )
        {
            // TODO(F3): Convert result to sRGB color space
            const double nAlpha( m_nAlphaIndex != -1 ? 1.0 - deviceColor[i+m_nAlphaIndex] : 1.0 );
            *pOut++ = rendering::ARGBColor(
                nAlpha,
                nAlpha*deviceColor[i+m_nRedIndex],
                nAlpha*deviceColor[i+m_nGreenIndex],
                nAlpha*deviceColor[i+m_nBlueIndex]);
        }
    }

    return aRes;
}

uno::Sequence< double > SAL_CALL VclCanvasBitmap::convertFromRGB( const uno::Sequence<rendering::RGBColor>& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_Size  nLen( rgbColor.getLength() );
    const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength());

    uno::Sequence< double > aRes(nLen*nComponentsPerPixel);
    double* pColors=aRes.getArray();
    
    if( m_bPalette )
    {
        for( sal_Size i=0; i<nLen; ++i )
        {
            pColors[m_nIndexIndex] = m_pBmpAcc->GetBestPaletteIndex( 
                    BitmapColor(toByteColor(rgbColor[i].Red),
                                toByteColor(rgbColor[i].Green),
                                toByteColor(rgbColor[i].Blue)));
            if( m_nAlphaIndex != -1 )
                pColors[m_nAlphaIndex] = 1.0;
                
            pColors += nComponentsPerPixel;
        }
    }
    else
    {
        for( sal_Size i=0; i<nLen; ++i )
        {
            pColors[m_nRedIndex]   = rgbColor[i].Red;
            pColors[m_nGreenIndex] = rgbColor[i].Green;
            pColors[m_nBlueIndex]  = rgbColor[i].Blue;
            if( m_nAlphaIndex != -1 )
                pColors[m_nAlphaIndex] = 1.0;
                
            pColors += nComponentsPerPixel;
        }
    }
    return aRes;
}

uno::Sequence< double > SAL_CALL VclCanvasBitmap::convertFromARGB( const uno::Sequence<rendering::ARGBColor>& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_Size  nLen( rgbColor.getLength() );
    const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength());

    uno::Sequence< double > aRes(nLen*nComponentsPerPixel);
    double* pColors=aRes.getArray();
    
    if( m_bPalette )
    {
        for( sal_Size i=0; i<nLen; ++i )
        {
            pColors[m_nIndexIndex] = m_pBmpAcc->GetBestPaletteIndex( 
                    BitmapColor(toByteColor(rgbColor[i].Red),
                                toByteColor(rgbColor[i].Green),
                                toByteColor(rgbColor[i].Blue)));
            if( m_nAlphaIndex != -1 )
                pColors[m_nAlphaIndex] = rgbColor[i].Alpha;
                
            pColors += nComponentsPerPixel;
        }
    }
    else
    {
        for( sal_Size i=0; i<nLen; ++i )
        {
            pColors[m_nRedIndex]   = rgbColor[i].Red;
            pColors[m_nGreenIndex] = rgbColor[i].Green;
            pColors[m_nBlueIndex]  = rgbColor[i].Blue;
            if( m_nAlphaIndex != -1 )
                pColors[m_nAlphaIndex] = rgbColor[i].Alpha;
                
            pColors += nComponentsPerPixel;
        }
    }
    return aRes;
}

uno::Sequence< double > SAL_CALL VclCanvasBitmap::convertFromPARGB( const uno::Sequence<rendering::ARGBColor>& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_Size  nLen( rgbColor.getLength() );
    const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength());

    uno::Sequence< double > aRes(nLen*nComponentsPerPixel);
    double* pColors=aRes.getArray();
    
    if( m_bPalette )
    {
        for( sal_Size i=0; i<nLen; ++i )
        {
            const double nAlpha( rgbColor[i].Alpha );
            pColors[m_nIndexIndex] = m_pBmpAcc->GetBestPaletteIndex( 
                    BitmapColor(toByteColor(rgbColor[i].Red / nAlpha),
                                toByteColor(rgbColor[i].Green / nAlpha),
                                toByteColor(rgbColor[i].Blue / nAlpha)));
            if( m_nAlphaIndex != -1 )
                pColors[m_nAlphaIndex] = nAlpha;
                
            pColors += nComponentsPerPixel;
        }
    }
    else
    {
        for( sal_Size i=0; i<nLen; ++i )
        {
            const double nAlpha( rgbColor[i].Alpha );
            pColors[m_nRedIndex]   = rgbColor[i].Red / nAlpha;
            pColors[m_nGreenIndex] = rgbColor[i].Green / nAlpha;
            pColors[m_nBlueIndex]  = rgbColor[i].Blue / nAlpha;
            if( m_nAlphaIndex != -1 )
                pColors[m_nAlphaIndex] = nAlpha;
                
            pColors += nComponentsPerPixel;
        }
    }
    return aRes;
}

sal_Int32 SAL_CALL VclCanvasBitmap::getBitsPerPixel(  ) throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );    
    return m_nBitsPerOutputPixel;
}

uno::Sequence< ::sal_Int32 > SAL_CALL VclCanvasBitmap::getComponentBitCounts(  ) throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );    
    return m_aComponentBitCounts;
}

sal_Int8 SAL_CALL VclCanvasBitmap::getEndianness(  ) throw (uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );    
    return m_nEndianness;
}

uno::Sequence<double> SAL_CALL VclCanvasBitmap::convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor, 
                                                                              const uno::Reference< ::rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    if( dynamic_cast<VclCanvasBitmap*>(targetColorSpace.get()) )
    {
        vos::OGuard aGuard( Application::GetSolarMutex() );

        const sal_Size  nLen( deviceColor.getLength() );
        const sal_Int32 nComponentsPerPixel(m_aComponentTags.getLength());
        ENSURE_ARG_OR_THROW2(nLen%nComponentsPerPixel==0, 
                             "number of channels no multiple of pixel element count",
                             static_cast<rendering::XBitmapPalette*>(this), 01);

        uno::Sequence<double> aRes(nLen);
        double* pOut( aRes.getArray() );

        if( m_bPalette )
        {
            OSL_ENSURE(m_nIndexIndex != -1,
                       "Invalid color channel indices");
            ENSURE_OR_THROW(m_pBmpAcc,
                            "Unable to get BitmapAccess");

            for( sal_Size i=0; i<nLen; i+=nComponentsPerPixel )
            {
                const BitmapColor aCol = m_pBmpAcc->GetPaletteColor(
                    sal::static_int_cast<sal_uInt16>(deviceColor[i+m_nIndexIndex]));

                // TODO(F3): Convert result to sRGB color space
                const double nAlpha( m_nAlphaIndex != -1 ? 1.0 - deviceColor[i+m_nAlphaIndex] : 1.0 );
                *pOut++ = toDoubleColor(aCol.GetRed());
                *pOut++ = toDoubleColor(aCol.GetGreen());
                *pOut++ = toDoubleColor(aCol.GetBlue());
                *pOut++ = nAlpha;
            }
        }
        else
        {
            OSL_ENSURE(m_nRedIndex != -1 && m_nGreenIndex != -1 && m_nBlueIndex != -1,
                       "Invalid color channel indices");

            for( sal_Size i=0; i<nLen; i+=nComponentsPerPixel )
            {
                // TODO(F3): Convert result to sRGB color space
                const double nAlpha( m_nAlphaIndex != -1 ? 1.0 - deviceColor[i+m_nAlphaIndex] : 1.0 );
                *pOut++ = deviceColor[i+m_nRedIndex];
                *pOut++ = deviceColor[i+m_nGreenIndex];
                *pOut++ = deviceColor[i+m_nBlueIndex];
                *pOut++ = nAlpha;
            }
        }

        return aRes;
    }
    else
    {
        // TODO(P3): if we know anything about target
        // colorspace, this can be greatly sped up
        uno::Sequence<rendering::ARGBColor> aIntermediate(
            convertIntegerToARGB(deviceColor));
        return targetColorSpace->convertFromARGB(aIntermediate);
    }
}

uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor, 
                                                                                  const uno::Reference< ::rendering::XIntegerBitmapColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    if( dynamic_cast<VclCanvasBitmap*>(targetColorSpace.get()) )
    {
        // it's us, so simply pass-through the data
        return deviceColor;
    }
    else
    {
        // TODO(P3): if we know anything about target
        // colorspace, this can be greatly sped up
        uno::Sequence<rendering::ARGBColor> aIntermediate(
            convertIntegerToARGB(deviceColor));
        return targetColorSpace->convertIntegerFromARGB(aIntermediate);
    }
}

uno::Sequence<rendering::RGBColor> SAL_CALL VclCanvasBitmap::convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_uInt8*     pIn( reinterpret_cast<const sal_uInt8*>(deviceColor.getConstArray()) );
    const sal_Size  nLen( deviceColor.getLength() );
    const sal_Int32 nNumColors((nLen*8 + m_nBitsPerOutputPixel-1)/m_nBitsPerOutputPixel);

    uno::Sequence< rendering::RGBColor > aRes(nNumColors);
    rendering::RGBColor* pOut( aRes.getArray() );

    ENSURE_OR_THROW(m_pBmpAcc,
                    "Unable to get BitmapAccess");

    if( m_aBmpEx.IsTransparent() )
    {
        const sal_Int32 nBytesPerPixel((m_nBitsPerOutputPixel+7)/8);
        for( sal_Size i=0; i<nLen; i+=nBytesPerPixel )
        {
            // if palette, index is guaranteed to be 8 bit
            const BitmapColor aCol = 
                m_bPalette ?
                m_pBmpAcc->GetPaletteColor(*pIn) : 
                m_pBmpAcc->GetPixelFromData(pIn,0);
            
            // TODO(F3): Convert result to sRGB color space 
            *pOut++ = rendering::RGBColor(toDoubleColor(aCol.GetRed()),
                                          toDoubleColor(aCol.GetGreen()),
                                          toDoubleColor(aCol.GetBlue()));
            // skips alpha  
            pIn += nBytesPerPixel;
        }
    }
    else
    {
        for( sal_Int32 i=0; i<nNumColors; ++i )
        {
            const BitmapColor aCol = 
                m_bPalette ?
                m_pBmpAcc->GetPaletteColor( m_pBmpAcc->GetPixelFromData( pIn, i ).GetIndex()) :
                m_pBmpAcc->GetPixelFromData(pIn, i);

            // TODO(F3): Convert result to sRGB color space
            *pOut++ = rendering::RGBColor(toDoubleColor(aCol.GetRed()),
                                          toDoubleColor(aCol.GetGreen()),
                                          toDoubleColor(aCol.GetBlue()));
        }
    }

    return aRes;
}

uno::Sequence<rendering::ARGBColor> SAL_CALL VclCanvasBitmap::convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_uInt8*     pIn( reinterpret_cast<const sal_uInt8*>(deviceColor.getConstArray()) );
    const sal_Size  nLen( deviceColor.getLength() );
    const sal_Int32 nNumColors((nLen*8 + m_nBitsPerOutputPixel-1)/m_nBitsPerOutputPixel);

    uno::Sequence< rendering::ARGBColor > aRes(nNumColors);
    rendering::ARGBColor* pOut( aRes.getArray() );

    ENSURE_OR_THROW(m_pBmpAcc,
                    "Unable to get BitmapAccess");

    if( m_aBmpEx.IsTransparent() )
    {
        const long      nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 );
        const sal_Int32 nBytesPerPixel((m_nBitsPerOutputPixel+7)/8);
        const sal_uInt8 nAlphaFactor( m_aBmpEx.IsAlpha() ? 1 : 255 );
        for( sal_Size i=0; i<nLen; i+=nBytesPerPixel )
        {
            // if palette, index is guaranteed to be 8 bit
            const BitmapColor aCol = 
                m_bPalette ?
                m_pBmpAcc->GetPaletteColor(*pIn) : 
                m_pBmpAcc->GetPixelFromData(pIn,0);
            
            // TODO(F3): Convert result to sRGB color space 
            *pOut++ = rendering::ARGBColor(1.0 - toDoubleColor(nAlphaFactor*pIn[nNonAlphaBytes]),
                                           toDoubleColor(aCol.GetRed()),
                                           toDoubleColor(aCol.GetGreen()),
                                           toDoubleColor(aCol.GetBlue()));
            pIn += nBytesPerPixel;
        }
    }
    else
    {
        for( sal_Int32 i=0; i<nNumColors; ++i )
        {
            const BitmapColor aCol = 
                m_bPalette ?
                m_pBmpAcc->GetPaletteColor( m_pBmpAcc->GetPixelFromData( pIn, i ).GetIndex() ) :
                m_pBmpAcc->GetPixelFromData(pIn, i);

            // TODO(F3): Convert result to sRGB color space
            *pOut++ = rendering::ARGBColor(1.0,
                                           toDoubleColor(aCol.GetRed()),
                                           toDoubleColor(aCol.GetGreen()),
                                           toDoubleColor(aCol.GetBlue()));
        }
    }

    return aRes;
}

uno::Sequence<rendering::ARGBColor> SAL_CALL VclCanvasBitmap::convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_uInt8*     pIn( reinterpret_cast<const sal_uInt8*>(deviceColor.getConstArray()) );
    const sal_Size  nLen( deviceColor.getLength() );
    const sal_Int32 nNumColors((nLen*8 + m_nBitsPerOutputPixel-1)/m_nBitsPerOutputPixel);

    uno::Sequence< rendering::ARGBColor > aRes(nNumColors);
    rendering::ARGBColor* pOut( aRes.getArray() );

    ENSURE_OR_THROW(m_pBmpAcc,
                    "Unable to get BitmapAccess");

    if( m_aBmpEx.IsTransparent() )
    {
        const long      nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 );
        const sal_Int32 nBytesPerPixel((m_nBitsPerOutputPixel+7)/8);
        const sal_uInt8 nAlphaFactor( m_aBmpEx.IsAlpha() ? 1 : 255 );
        for( sal_Size i=0; i<nLen; i+=nBytesPerPixel )
        {
            // if palette, index is guaranteed to be 8 bit
            const BitmapColor aCol = 
                m_bPalette ?
                m_pBmpAcc->GetPaletteColor(*pIn) : 
                m_pBmpAcc->GetPixelFromData(pIn,0);
            
            // TODO(F3): Convert result to sRGB color space 
            const double nAlpha( 1.0 - toDoubleColor(nAlphaFactor*pIn[nNonAlphaBytes]) );
            *pOut++ = rendering::ARGBColor(nAlpha,
                                           nAlpha*toDoubleColor(aCol.GetRed()),
                                           nAlpha*toDoubleColor(aCol.GetGreen()),
                                           nAlpha*toDoubleColor(aCol.GetBlue()));
            pIn += nBytesPerPixel;
        }
    }
    else
    {
        for( sal_Int32 i=0; i<nNumColors; ++i )
        {
            const BitmapColor aCol = 
                m_bPalette ?
                m_pBmpAcc->GetPaletteColor( m_pBmpAcc->GetPixelFromData( pIn, i ).GetIndex() ) :
                m_pBmpAcc->GetPixelFromData(pIn, i);

            // TODO(F3): Convert result to sRGB color space
            *pOut++ = rendering::ARGBColor(1.0,
                                           toDoubleColor(aCol.GetRed()),
                                           toDoubleColor(aCol.GetGreen()),
                                           toDoubleColor(aCol.GetBlue()));
        }
    }

    return aRes;
}

uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertIntegerFromRGB( const uno::Sequence<rendering::RGBColor>& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_Size  nLen( rgbColor.getLength() );
    const sal_Int32 nNumBytes((nLen*m_nBitsPerOutputPixel+7)/8);

    uno::Sequence< sal_Int8 > aRes(nNumBytes);
    sal_uInt8* pColors=reinterpret_cast<sal_uInt8*>(aRes.getArray());

    if( m_aBmpEx.IsTransparent() )
    {
        const long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 );
        for( sal_Size i=0; i<nLen; ++i )
        {
            const BitmapColor aCol(toByteColor(rgbColor[i].Red),
                                   toByteColor(rgbColor[i].Green),
                                   toByteColor(rgbColor[i].Blue));
            const BitmapColor aCol2 = 
                m_bPalette ?
                BitmapColor(
                    sal::static_int_cast<sal_uInt8>(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : 
                aCol;

            m_pBmpAcc->SetPixelOnData(pColors,0,aCol2);
            pColors   += nNonAlphaBytes;
            *pColors++ = sal_uInt8(255);
        }
    }
    else
    {
        for( sal_Size i=0; i<nLen; ++i )
        {
            const BitmapColor aCol(toByteColor(rgbColor[i].Red),
                                   toByteColor(rgbColor[i].Green),
                                   toByteColor(rgbColor[i].Blue));
            const BitmapColor aCol2 = 
                m_bPalette ?
                BitmapColor(
                    sal::static_int_cast<sal_uInt8>(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : 
                aCol;

            m_pBmpAcc->SetPixelOnData(pColors,i,aCol2);
        }
    }

    return aRes;
}

uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertIntegerFromARGB( const uno::Sequence<rendering::ARGBColor>& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_Size  nLen( rgbColor.getLength() );
    const sal_Int32 nNumBytes((nLen*m_nBitsPerOutputPixel+7)/8);

    uno::Sequence< sal_Int8 > aRes(nNumBytes);
    sal_uInt8* pColors=reinterpret_cast<sal_uInt8*>(aRes.getArray());

    if( m_aBmpEx.IsTransparent() )
    {
        const long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 );
        for( sal_Size i=0; i<nLen; ++i )
        {
            const BitmapColor aCol(toByteColor(rgbColor[i].Red),
                                   toByteColor(rgbColor[i].Green),
                                   toByteColor(rgbColor[i].Blue));
            const BitmapColor aCol2 = 
                m_bPalette ?
                BitmapColor(
                    sal::static_int_cast<sal_uInt8>(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : 
                aCol;

            m_pBmpAcc->SetPixelOnData(pColors,0,aCol2);
            pColors   += nNonAlphaBytes;
            *pColors++ = 255 - toByteColor(rgbColor[i].Alpha);
        }
    }
    else
    {
        for( sal_Size i=0; i<nLen; ++i )
        {
            const BitmapColor aCol(toByteColor(rgbColor[i].Red),
                                   toByteColor(rgbColor[i].Green),
                                   toByteColor(rgbColor[i].Blue));
            const BitmapColor aCol2 = 
                m_bPalette ?
                BitmapColor(
                    sal::static_int_cast<sal_uInt8>(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : 
                aCol;

            m_pBmpAcc->SetPixelOnData(pColors,i,aCol2);
        }
    }

    return aRes;
}

uno::Sequence< ::sal_Int8 > SAL_CALL VclCanvasBitmap::convertIntegerFromPARGB( const uno::Sequence<rendering::ARGBColor>& rgbColor ) throw (lang::IllegalArgumentException,uno::RuntimeException)
{
    vos::OGuard aGuard( Application::GetSolarMutex() );

    const sal_Size  nLen( rgbColor.getLength() );
    const sal_Int32 nNumBytes((nLen*m_nBitsPerOutputPixel+7)/8);

    uno::Sequence< sal_Int8 > aRes(nNumBytes);
    sal_uInt8* pColors=reinterpret_cast<sal_uInt8*>(aRes.getArray());

    if( m_aBmpEx.IsTransparent() )
    {
        const long nNonAlphaBytes( (m_nBitsPerInputPixel+7)/8 );
        for( sal_Size i=0; i<nLen; ++i )
        {
            const double nAlpha( rgbColor[i].Alpha );
            const BitmapColor aCol(toByteColor(rgbColor[i].Red / nAlpha),
                                   toByteColor(rgbColor[i].Green / nAlpha),
                                   toByteColor(rgbColor[i].Blue / nAlpha));
            const BitmapColor aCol2 = 
                m_bPalette ?
                BitmapColor(
                    sal::static_int_cast<sal_uInt8>(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : 
                aCol;

            m_pBmpAcc->SetPixelOnData(pColors,0,aCol2);
            pColors   += nNonAlphaBytes;
            *pColors++ = 255 - toByteColor(nAlpha);
        }
    }
    else
    {
        for( sal_Size i=0; i<nLen; ++i )
        {
            const BitmapColor aCol(toByteColor(rgbColor[i].Red),
                                   toByteColor(rgbColor[i].Green),
                                   toByteColor(rgbColor[i].Blue));
            const BitmapColor aCol2 = 
                m_bPalette ?
                BitmapColor(
                    sal::static_int_cast<sal_uInt8>(m_pBmpAcc->GetBestPaletteIndex( aCol ))) : 
                aCol;

            m_pBmpAcc->SetPixelOnData(pColors,i,aCol2);
        }
    }

    return aRes;
}

BitmapEx VclCanvasBitmap::getBitmapEx() const
{
    return m_aBmpEx;
}
