| /************************************************************** |
| * |
| * 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 ); |
| } |
| } |
| } |