blob: aa055ecccc557291783b51096abfb1dd79d2035c [file] [log] [blame]
/**************************************************************
*
* 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_canvas.hxx"
#include <vcl/canvastools.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/bitmapex.hxx>
#include <vcl/bmpacc.hxx>
#include <tools/diagnose_ex.h>
#include "dx_impltools.hxx"
#include <basegfx/numeric/ftools.hxx>
#include <canvas/debug.hxx>
#include <canvas/verbosetrace.hxx>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/rendering/XIntegerBitmap.hpp>
#include <boost/scoped_array.hpp>
#include "dx_vcltools.hxx"
using namespace ::com::sun::star;
namespace dxcanvas
{
namespace tools
{
namespace
{
/// Calc number of colors in given BitmapInfoHeader
sal_Int32 calcDIBColorCount( const BITMAPINFOHEADER& rBIH )
{
if( rBIH.biSize != sizeof( BITMAPCOREHEADER ) )
{
if( rBIH.biBitCount <= 8 )
{
if( rBIH.biClrUsed )
return rBIH.biClrUsed;
else
return 1L << rBIH.biBitCount;
}
}
else
{
BITMAPCOREHEADER* pCoreHeader = (BITMAPCOREHEADER*)&rBIH;
if( pCoreHeader->bcBitCount <= 8 )
return 1L << pCoreHeader->bcBitCount;
}
return 0; // nothing known
}
/// Draw DI bits to given Graphics
bool drawDIBits( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics,
const void* hDIB )
{
bool bRet( false );
BitmapSharedPtr pBitmap;
const BITMAPINFO* pBI = (BITMAPINFO*)GlobalLock( (HGLOBAL)hDIB );
if( pBI )
{
const BITMAPINFOHEADER* pBIH = (BITMAPINFOHEADER*)pBI;
const BYTE* pBits = (BYTE*) pBI + *(DWORD*)pBI +
calcDIBColorCount( *pBIH ) * sizeof( RGBQUAD );
// forward to outsourced GDI+ rendering method
// (header clashes)
bRet = tools::drawDIBits( rGraphics, *pBI, (void*)pBits );
GlobalUnlock( (HGLOBAL)hDIB );
}
return bRet;
}
/** Draw VCL bitmap to given Graphics
@param rBmp
Reference to bitmap. Might get modified, in such a way
that it will hold a DIB after a successful function call.
*/
bool drawVCLBitmap( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics,
::Bitmap& rBmp )
{
BitmapSystemData aBmpSysData;
if( !rBmp.GetSystemData( aBmpSysData ) ||
!aBmpSysData.pDIB )
{
// first of all, ensure that Bitmap contains a DIB, by
// aquiring a read access
BitmapReadAccess* pReadAcc = rBmp.AcquireReadAccess();
// TODO(P2): Acquiring a read access can actually
// force a read from VRAM, thus, avoiding this
// step somehow will increase performance
// here.
if( pReadAcc )
{
// try again: now, WinSalBitmap must have
// generated a DIB
if( rBmp.GetSystemData( aBmpSysData ) &&
aBmpSysData.pDIB )
{
return drawDIBits( rGraphics,
aBmpSysData.pDIB );
}
rBmp.ReleaseAccess( pReadAcc );
}
}
else
{
return drawDIBits( rGraphics,
aBmpSysData.pDIB );
}
// failed to generate DIBits from vcl bitmap
return false;
}
/** Create a chunk of raw RGBA data GDI+ Bitmap from VCL BbitmapEX
*/
RawRGBABitmap bitmapFromVCLBitmapEx( const ::BitmapEx& rBmpEx )
{
// TODO(P2): Avoid temporary bitmap generation, maybe
// even ensure that created DIBs are copied back to
// BmpEx (currently, every AcquireReadAccess() will
// make the local bitmap copy unique, effectively
// duplicating the memory used)
ENSURE_OR_THROW( rBmpEx.IsTransparent(),
"::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
"BmpEx not transparent" );
// convert transparent bitmap to 32bit RGBA
// ========================================
const ::Size aBmpSize( rBmpEx.GetSizePixel() );
RawRGBABitmap aBmpData;
aBmpData.mnWidth = aBmpSize.Width();
aBmpData.mnHeight = aBmpSize.Height();
aBmpData.mpBitmapData.reset( new sal_uInt8[ 4*aBmpData.mnWidth*aBmpData.mnHeight ] );
Bitmap aBitmap( rBmpEx.GetBitmap() );
ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(),
aBitmap );
const sal_Int32 nWidth( aBmpSize.Width() );
const sal_Int32 nHeight( aBmpSize.Height() );
ENSURE_OR_THROW( pReadAccess.get() != NULL,
"::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
"Unable to acquire read acces to bitmap" );
if( rBmpEx.IsAlpha() )
{
Bitmap aAlpha( rBmpEx.GetAlpha().GetBitmap() );
ScopedBitmapReadAccess pAlphaReadAccess( aAlpha.AcquireReadAccess(),
aAlpha );
// By convention, the access buffer always has
// one of the following formats:
//
// BMP_FORMAT_1BIT_MSB_PAL
// BMP_FORMAT_4BIT_MSN_PAL
// BMP_FORMAT_8BIT_PAL
// BMP_FORMAT_16BIT_TC_LSB_MASK
// BMP_FORMAT_24BIT_TC_BGR
// BMP_FORMAT_32BIT_TC_MASK
//
// and is always BMP_FORMAT_BOTTOM_UP
//
// This is the way
// WinSalBitmap::AcquireBuffer() sets up the
// buffer
ENSURE_OR_THROW( pAlphaReadAccess.get() != NULL,
"::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
"Unable to acquire read acces to alpha" );
ENSURE_OR_THROW( pAlphaReadAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ||
pAlphaReadAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK,
"::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
"Unsupported alpha scanline format" );
BitmapColor aCol;
const sal_Int32 nWidth( aBmpSize.Width() );
const sal_Int32 nHeight( aBmpSize.Height() );
sal_uInt8* pCurrOutput( aBmpData.mpBitmapData.get() );
int x, y;
for( y=0; y<nHeight; ++y )
{
switch( pReadAccess->GetScanlineFormat() )
{
case BMP_FORMAT_8BIT_PAL:
{
Scanline pScan = pReadAccess->GetScanline( y );
Scanline pAScan = pAlphaReadAccess->GetScanline( y );
for( x=0; x<nWidth; ++x )
{
aCol = pReadAccess->GetPaletteColor( *pScan++ );
*pCurrOutput++ = aCol.GetBlue();
*pCurrOutput++ = aCol.GetGreen();
*pCurrOutput++ = aCol.GetRed();
// out notion of alpha is
// different from the rest
// of the world's
*pCurrOutput++ = 255 - (BYTE)*pAScan++;
}
}
break;
case BMP_FORMAT_24BIT_TC_BGR:
{
Scanline pScan = pReadAccess->GetScanline( y );
Scanline pAScan = pAlphaReadAccess->GetScanline( y );
for( x=0; x<nWidth; ++x )
{
// store as RGBA
*pCurrOutput++ = *pScan++;
*pCurrOutput++ = *pScan++;
*pCurrOutput++ = *pScan++;
// out notion of alpha is
// different from the rest
// of the world's
*pCurrOutput++ = 255 - (BYTE)*pAScan++;
}
}
break;
// TODO(P2): Might be advantageous
// to hand-formulate the following
// formats, too.
case BMP_FORMAT_1BIT_MSB_PAL:
// FALLTHROUGH intended
case BMP_FORMAT_4BIT_MSN_PAL:
// FALLTHROUGH intended
case BMP_FORMAT_16BIT_TC_LSB_MASK:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_MASK:
{
Scanline pAScan = pAlphaReadAccess->GetScanline( y );
// using fallback for those
// seldom formats
for( x=0; x<nWidth; ++x )
{
// yes. x and y are swapped on Get/SetPixel
aCol = pReadAccess->GetColor(y,x);
*pCurrOutput++ = aCol.GetBlue();
*pCurrOutput++ = aCol.GetGreen();
*pCurrOutput++ = aCol.GetRed();
// out notion of alpha is
// different from the rest
// of the world's
*pCurrOutput++ = 255 - (BYTE)*pAScan++;
}
}
break;
case BMP_FORMAT_1BIT_LSB_PAL:
// FALLTHROUGH intended
case BMP_FORMAT_4BIT_LSN_PAL:
// FALLTHROUGH intended
case BMP_FORMAT_8BIT_TC_MASK:
// FALLTHROUGH intended
case BMP_FORMAT_24BIT_TC_RGB:
// FALLTHROUGH intended
case BMP_FORMAT_24BIT_TC_MASK:
// FALLTHROUGH intended
case BMP_FORMAT_16BIT_TC_MSB_MASK:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_ABGR:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_ARGB:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_BGRA:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_RGBA:
// FALLTHROUGH intended
default:
ENSURE_OR_THROW( false,
"::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
"Unexpected scanline format - has "
"WinSalBitmap::AcquireBuffer() changed?" );
}
}
}
else
{
Bitmap aMask( rBmpEx.GetMask() );
ScopedBitmapReadAccess pMaskReadAccess( aMask.AcquireReadAccess(),
aMask );
// By convention, the access buffer always has
// one of the following formats:
//
// BMP_FORMAT_1BIT_MSB_PAL
// BMP_FORMAT_4BIT_MSN_PAL
// BMP_FORMAT_8BIT_PAL
// BMP_FORMAT_16BIT_TC_LSB_MASK
// BMP_FORMAT_24BIT_TC_BGR
// BMP_FORMAT_32BIT_TC_MASK
//
// and is always BMP_FORMAT_BOTTOM_UP
//
// This is the way
// WinSalBitmap::AcquireBuffer() sets up the
// buffer
ENSURE_OR_THROW( pMaskReadAccess.get() != NULL,
"::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
"Unable to acquire read acces to mask" );
ENSURE_OR_THROW( pMaskReadAccess->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL,
"::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
"Unsupported mask scanline format" );
BitmapColor aCol;
int nCurrBit;
const int nMask( 1L );
const int nInitialBit(7);
sal_uInt8* pCurrOutput( aBmpData.mpBitmapData.get() );
int x, y;
// mapping table, to get from mask index color to
// alpha value (which depends on the mask's palette)
sal_uInt8 aColorMap[2];
const BitmapColor& rCol0( pMaskReadAccess->GetPaletteColor( 0 ) );
const BitmapColor& rCol1( pMaskReadAccess->GetPaletteColor( 1 ) );
// shortcut for true luminance calculation
// (assumes that palette is grey-level). Note the
// swapped the indices here, to account for the
// fact that VCL's notion of alpha is inverted to
// the rest of the world's.
aColorMap[0] = rCol1.GetRed();
aColorMap[1] = rCol0.GetRed();
for( y=0; y<nHeight; ++y )
{
switch( pReadAccess->GetScanlineFormat() )
{
case BMP_FORMAT_8BIT_PAL:
{
Scanline pScan = pReadAccess->GetScanline( y );
Scanline pMScan = pMaskReadAccess->GetScanline( y );
for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x )
{
aCol = pReadAccess->GetPaletteColor( *pScan++ );
*pCurrOutput++ = aCol.GetBlue();
*pCurrOutput++ = aCol.GetGreen();
*pCurrOutput++ = aCol.GetRed();
*pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ];
nCurrBit = ((nCurrBit - 1) % 8L) & 7L;
}
}
break;
case BMP_FORMAT_24BIT_TC_BGR:
{
Scanline pScan = pReadAccess->GetScanline( y );
Scanline pMScan = pMaskReadAccess->GetScanline( y );
for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x )
{
// store as RGBA
*pCurrOutput++ = *pScan++;
*pCurrOutput++ = *pScan++;
*pCurrOutput++ = *pScan++;
*pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ];
nCurrBit = ((nCurrBit - 1) % 8L) & 7L;
}
}
break;
// TODO(P2): Might be advantageous
// to hand-formulate the following
// formats, too.
case BMP_FORMAT_1BIT_MSB_PAL:
// FALLTHROUGH intended
case BMP_FORMAT_4BIT_MSN_PAL:
// FALLTHROUGH intended
case BMP_FORMAT_16BIT_TC_LSB_MASK:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_MASK:
{
Scanline pMScan = pMaskReadAccess->GetScanline( y );
// using fallback for those
// seldom formats
for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x )
{
// yes. x and y are swapped on Get/SetPixel
aCol = pReadAccess->GetColor(y,x);
// store as RGBA
*pCurrOutput++ = aCol.GetBlue();
*pCurrOutput++ = aCol.GetGreen();
*pCurrOutput++ = aCol.GetRed();
*pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ];
nCurrBit = ((nCurrBit - 1) % 8L) & 7L;
}
}
break;
case BMP_FORMAT_1BIT_LSB_PAL:
// FALLTHROUGH intended
case BMP_FORMAT_4BIT_LSN_PAL:
// FALLTHROUGH intended
case BMP_FORMAT_8BIT_TC_MASK:
// FALLTHROUGH intended
case BMP_FORMAT_24BIT_TC_RGB:
// FALLTHROUGH intended
case BMP_FORMAT_24BIT_TC_MASK:
// FALLTHROUGH intended
case BMP_FORMAT_16BIT_TC_MSB_MASK:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_ABGR:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_ARGB:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_BGRA:
// FALLTHROUGH intended
case BMP_FORMAT_32BIT_TC_RGBA:
// FALLTHROUGH intended
default:
ENSURE_OR_THROW( false,
"::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
"Unexpected scanline format - has "
"WinSalBitmap::AcquireBuffer() changed?" );
}
}
}
return aBmpData;
}
bool drawVCLBitmapEx( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics,
const ::BitmapEx& rBmpEx )
{
if( !rBmpEx.IsTransparent() )
{
Bitmap aBmp( rBmpEx.GetBitmap() );
return drawVCLBitmap( rGraphics, aBmp );
}
else
{
return drawRGBABits( rGraphics,
bitmapFromVCLBitmapEx( rBmpEx ) );
}
}
}
bool drawVCLBitmapFromXBitmap( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics,
const uno::Reference< rendering::XBitmap >& xBitmap )
{
// TODO(F2): add support for floating point bitmap formats
uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp(
xBitmap, uno::UNO_QUERY );
if( !xIntBmp.is() )
return false;
::BitmapEx aBmpEx = ::vcl::unotools::bitmapExFromXBitmap( xIntBmp );
if( !aBmpEx )
return false;
return drawVCLBitmapEx( rGraphics, aBmpEx );
}
}
}