diff options
Diffstat (limited to 'vcl/win/gdi/salbmp.cxx')
-rw-r--r-- | vcl/win/gdi/salbmp.cxx | 1067 |
1 files changed, 1067 insertions, 0 deletions
diff --git a/vcl/win/gdi/salbmp.cxx b/vcl/win/gdi/salbmp.cxx new file mode 100644 index 000000000..f2b21a666 --- /dev/null +++ b/vcl/win/gdi/salbmp.cxx @@ -0,0 +1,1067 @@ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <svsys.h> +#include <vcl/bitmap.hxx> +#include <vcl/BitmapAccessMode.hxx> +#include <vcl/BitmapBuffer.hxx> +#include <vcl/BitmapPalette.hxx> +#include <vcl/ColorMask.hxx> +#include <vcl/Scanline.hxx> +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <win/wincomp.hxx> +#include <win/salgdi.h> +#include <win/saldata.hxx> +#include <win/salbmp.h> +#include <string.h> +#include <vcl/timer.hxx> +#include <cppuhelper/basemutex.hxx> +#include <sal/log.hxx> +#include <tools/helpers.hxx> +#include <map> + +#if defined _MSC_VER +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#endif + +#include <prewin.h> +#include <gdiplus.h> +#include <postwin.h> + +#if defined _MSC_VER +#undef min +#undef max +#endif + +static void ImplSetPixel4( sal_uInt8* pScanline, long nX, const BYTE cIndex ) +{ + BYTE& rByte = pScanline[ nX >> 1 ]; + + if ( nX & 1 ) + { + rByte &= 0xf0; + rByte |= cIndex & 0x0f; + } + else + { + rByte &= 0x0f; + rByte |= cIndex << 4; + } +} + +WinSalBitmap::WinSalBitmap() +: SalBitmap(), + basegfx::SystemDependentDataHolder(), + maSize(), + mhDIB(nullptr), + mhDDB(nullptr), + mnBitCount(0) +{ +} + +WinSalBitmap::~WinSalBitmap() +{ + Destroy(); +} + +void WinSalBitmap::Destroy() +{ + if( mhDIB ) + GlobalFree( mhDIB ); + else if( mhDDB ) + DeleteObject( mhDDB ); + + maSize = Size(); + mnBitCount = 0; +} + +namespace { + +class SystemDependentData_GdiPlusBitmap : public basegfx::SystemDependentData +{ +private: + std::shared_ptr<Gdiplus::Bitmap> mpGdiPlusBitmap; + const WinSalBitmap* mpAssociatedAlpha; + +public: + SystemDependentData_GdiPlusBitmap( + basegfx::SystemDependentDataManager& rSystemDependentDataManager, + const std::shared_ptr<Gdiplus::Bitmap>& rGdiPlusBitmap, + const WinSalBitmap* pAssociatedAlpha); + + const WinSalBitmap* getAssociatedAlpha() const { return mpAssociatedAlpha; } + const std::shared_ptr<Gdiplus::Bitmap>& getGdiPlusBitmap() const { return mpGdiPlusBitmap; } + + virtual sal_Int64 estimateUsageInBytes() const override; +}; + +} + +SystemDependentData_GdiPlusBitmap::SystemDependentData_GdiPlusBitmap( + basegfx::SystemDependentDataManager& rSystemDependentDataManager, + const std::shared_ptr<Gdiplus::Bitmap>& rGdiPlusBitmap, + const WinSalBitmap* pAssociatedAlpha) +: basegfx::SystemDependentData(rSystemDependentDataManager), + mpGdiPlusBitmap(rGdiPlusBitmap), + mpAssociatedAlpha(pAssociatedAlpha) +{ +} + +sal_Int64 SystemDependentData_GdiPlusBitmap::estimateUsageInBytes() const +{ + sal_Int64 nRetval(0); + + if(mpGdiPlusBitmap) + { + const UINT nWidth(mpGdiPlusBitmap->GetWidth()); + const UINT nHeight(mpGdiPlusBitmap->GetHeight()); + + if(0 != nWidth && 0 != nHeight) + { + nRetval = nWidth * nHeight; + + switch(mpGdiPlusBitmap->GetPixelFormat()) + { + case PixelFormat1bppIndexed: + nRetval /= 8; + break; + case PixelFormat4bppIndexed: + nRetval /= 4; + break; + case PixelFormat16bppGrayScale: + case PixelFormat16bppRGB555: + case PixelFormat16bppRGB565: + case PixelFormat16bppARGB1555: + nRetval *= 2; + break; + case PixelFormat24bppRGB: + nRetval *= 3; + break; + case PixelFormat32bppRGB: + case PixelFormat32bppARGB: + case PixelFormat32bppPARGB: + case PixelFormat32bppCMYK: + nRetval *= 4; + break; + case PixelFormat48bppRGB: + nRetval *= 6; + break; + case PixelFormat64bppARGB: + case PixelFormat64bppPARGB: + nRetval *= 8; + break; + default: + case PixelFormat8bppIndexed: + break; + } + } + } + + return nRetval; +} + +std::shared_ptr< Gdiplus::Bitmap > WinSalBitmap::ImplGetGdiPlusBitmap(const WinSalBitmap* pAlphaSource) const +{ + std::shared_ptr< Gdiplus::Bitmap > aRetval; + + // try to access buffered data + std::shared_ptr<SystemDependentData_GdiPlusBitmap> pSystemDependentData_GdiPlusBitmap( + getSystemDependentData<SystemDependentData_GdiPlusBitmap>()); + + if(pSystemDependentData_GdiPlusBitmap) + { + // check data validity + if(pSystemDependentData_GdiPlusBitmap->getAssociatedAlpha() != pAlphaSource + || 0 == maSize.Width() + || 0 == maSize.Height()) + { + // #122350# if associated alpha with which the GDIPlus was constructed has changed + // it is necessary to remove it from buffer, reset reference to it and reconstruct + // data invalid, forget + pSystemDependentData_GdiPlusBitmap.reset(); + } + } + + if(pSystemDependentData_GdiPlusBitmap) + { + // use from buffer + aRetval = pSystemDependentData_GdiPlusBitmap->getGdiPlusBitmap(); + } + else if(!maSize.IsEmpty()) + { + // create and set data + const WinSalBitmap* pAssociatedAlpha(nullptr); + + if(pAlphaSource) + { + aRetval.reset(const_cast< WinSalBitmap* >(this)->ImplCreateGdiPlusBitmap(*pAlphaSource)); + pAssociatedAlpha = pAlphaSource; + } + else + { + aRetval.reset(const_cast< WinSalBitmap* >(this)->ImplCreateGdiPlusBitmap()); + pAssociatedAlpha = nullptr; + } + + // add to buffering mechanism + addOrReplaceSystemDependentData<SystemDependentData_GdiPlusBitmap>( + ImplGetSystemDependentDataManager(), + aRetval, + pAssociatedAlpha); + } + + return aRetval; +} + +Gdiplus::Bitmap* WinSalBitmap::ImplCreateGdiPlusBitmap() +{ + Gdiplus::Bitmap* pRetval(nullptr); + WinSalBitmap* pSalRGB = this; + WinSalBitmap* pExtraWinSalRGB = nullptr; + + if(!pSalRGB->ImplGethDIB()) + { + // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap + pExtraWinSalRGB = new WinSalBitmap(); + pExtraWinSalRGB->Create(*pSalRGB, pSalRGB->GetBitCount()); + pSalRGB = pExtraWinSalRGB; + } + + BitmapBuffer* pRGB = pSalRGB->AcquireBuffer(BitmapAccessMode::Read); + std::unique_ptr<BitmapBuffer> pExtraRGB; + + if(pRGB && ScanlineFormat::N24BitTcBgr != RemoveScanline(pRGB->mnFormat)) + { + // convert source bitmap to BMP_FORMAT_24BIT_TC_BGR format if not yet in that format + SalTwoRect aSalTwoRect(0, 0, pRGB->mnWidth, pRGB->mnHeight, 0, 0, pRGB->mnWidth, pRGB->mnHeight); + pExtraRGB = StretchAndConvert( + *pRGB, + aSalTwoRect, + ScanlineFormat::N24BitTcBgr); + + pSalRGB->ReleaseBuffer(pRGB, BitmapAccessMode::Write); + pRGB = pExtraRGB.get(); + } + + if(pRGB + && pRGB->mnWidth > 0 + && pRGB->mnHeight > 0 + && ScanlineFormat::N24BitTcBgr == RemoveScanline(pRGB->mnFormat)) + { + const sal_uInt32 nW(pRGB->mnWidth); + const sal_uInt32 nH(pRGB->mnHeight); + + pRetval = new Gdiplus::Bitmap(nW, nH, PixelFormat24bppRGB); + + if ( pRetval->GetLastStatus() == Gdiplus::Ok ) + { + sal_uInt8* pSrcRGB(pRGB->mpBits); + const sal_uInt32 nExtraRGB(pRGB->mnScanlineSize - (nW * 3)); + const bool bTopDown(pRGB->mnFormat & ScanlineFormat::TopDown); + const Gdiplus::Rect aAllRect(0, 0, nW, nH); + Gdiplus::BitmapData aGdiPlusBitmapData; + pRetval->LockBits(&aAllRect, Gdiplus::ImageLockModeWrite, PixelFormat24bppRGB, &aGdiPlusBitmapData); + + // copy data to Gdiplus::Bitmap; format is BGR here in both cases, so memcpy is possible + for(sal_uInt32 y(0); y < nH; y++) + { + const sal_uInt32 nYInsert(bTopDown ? y : nH - y - 1); + sal_uInt8* targetPixels = static_cast<sal_uInt8*>(aGdiPlusBitmapData.Scan0) + (nYInsert * aGdiPlusBitmapData.Stride); + + memcpy(targetPixels, pSrcRGB, nW * 3); + pSrcRGB += nW * 3 + nExtraRGB; + } + + pRetval->UnlockBits(&aGdiPlusBitmapData); + } + else + { + delete pRetval; + pRetval = nullptr; + } + } + + if(pExtraRGB) + { + // #i123478# shockingly, BitmapBuffer does not free the memory it is controlling + // in its destructor, this *has to be done by hand*. Doing it here now + delete[] pExtraRGB->mpBits; + pExtraRGB.reset(); + } + else + { + pSalRGB->ReleaseBuffer(pRGB, BitmapAccessMode::Read); + } + + if(pExtraWinSalRGB) + { + delete pExtraWinSalRGB; + } + + return pRetval; +} + +Gdiplus::Bitmap* WinSalBitmap::ImplCreateGdiPlusBitmap(const WinSalBitmap& rAlphaSource) +{ + Gdiplus::Bitmap* pRetval(nullptr); + WinSalBitmap* pSalRGB = this; + WinSalBitmap* pExtraWinSalRGB = nullptr; + + if(!pSalRGB->ImplGethDIB()) + { + // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap + pExtraWinSalRGB = new WinSalBitmap(); + pExtraWinSalRGB->Create(*pSalRGB, pSalRGB->GetBitCount()); + pSalRGB = pExtraWinSalRGB; + } + + BitmapBuffer* pRGB = pSalRGB->AcquireBuffer(BitmapAccessMode::Read); + std::unique_ptr<BitmapBuffer> pExtraRGB; + + if(pRGB && ScanlineFormat::N24BitTcBgr != RemoveScanline(pRGB->mnFormat)) + { + // convert source bitmap to canlineFormat::N24BitTcBgr format if not yet in that format + SalTwoRect aSalTwoRect(0, 0, pRGB->mnWidth, pRGB->mnHeight, 0, 0, pRGB->mnWidth, pRGB->mnHeight); + pExtraRGB = StretchAndConvert( + *pRGB, + aSalTwoRect, + ScanlineFormat::N24BitTcBgr); + + pSalRGB->ReleaseBuffer(pRGB, BitmapAccessMode::Read); + pRGB = pExtraRGB.get(); + } + + WinSalBitmap* pSalA = const_cast< WinSalBitmap* >(&rAlphaSource); + WinSalBitmap* pExtraWinSalA = nullptr; + + if(!pSalA->ImplGethDIB()) + { + // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap + pExtraWinSalA = new WinSalBitmap(); + pExtraWinSalA->Create(*pSalA, pSalA->GetBitCount()); + pSalA = pExtraWinSalA; + } + + BitmapBuffer* pA = pSalA->AcquireBuffer(BitmapAccessMode::Read); + std::unique_ptr<BitmapBuffer> pExtraA; + + if(pA && ScanlineFormat::N8BitPal != RemoveScanline(pA->mnFormat)) + { + // convert alpha bitmap to ScanlineFormat::N8BitPal format if not yet in that format + SalTwoRect aSalTwoRect(0, 0, pA->mnWidth, pA->mnHeight, 0, 0, pA->mnWidth, pA->mnHeight); + const BitmapPalette& rTargetPalette = Bitmap::GetGreyPalette(256); + + pExtraA = StretchAndConvert( + *pA, + aSalTwoRect, + ScanlineFormat::N8BitPal, + &rTargetPalette); + + pSalA->ReleaseBuffer(pA, BitmapAccessMode::Read); + pA = pExtraA.get(); + } + + if(pRGB + && pA + && pRGB->mnWidth > 0 + && pRGB->mnHeight > 0 + && pRGB->mnWidth == pA->mnWidth + && pRGB->mnHeight == pA->mnHeight + && ScanlineFormat::N24BitTcBgr == RemoveScanline(pRGB->mnFormat) + && ScanlineFormat::N8BitPal == RemoveScanline(pA->mnFormat)) + { + // we have alpha and bitmap in known formats, create GdiPlus Bitmap as 32bit ARGB + const sal_uInt32 nW(pRGB->mnWidth); + const sal_uInt32 nH(pRGB->mnHeight); + + pRetval = new Gdiplus::Bitmap(nW, nH, PixelFormat32bppARGB); + + if ( pRetval->GetLastStatus() == Gdiplus::Ok ) // 2nd place to secure with new Gdiplus::Bitmap + { + sal_uInt8* pSrcRGB(pRGB->mpBits); + sal_uInt8* pSrcA(pA->mpBits); + const sal_uInt32 nExtraRGB(pRGB->mnScanlineSize - (nW * 3)); + const sal_uInt32 nExtraA(pA->mnScanlineSize - nW); + const bool bTopDown(pRGB->mnFormat & ScanlineFormat::TopDown); + const Gdiplus::Rect aAllRect(0, 0, nW, nH); + Gdiplus::BitmapData aGdiPlusBitmapData; + pRetval->LockBits(&aAllRect, Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &aGdiPlusBitmapData); + + // copy data to Gdiplus::Bitmap; format is BGRA; need to mix BGR from Bitmap and + // A from alpha, so inner loop is needed (who invented BitmapEx..?) + for(sal_uInt32 y(0); y < nH; y++) + { + const sal_uInt32 nYInsert(bTopDown ? y : nH - y - 1); + sal_uInt8* targetPixels = static_cast<sal_uInt8*>(aGdiPlusBitmapData.Scan0) + (nYInsert * aGdiPlusBitmapData.Stride); + + for(sal_uInt32 x(0); x < nW; x++) + { + *targetPixels++ = *pSrcRGB++; + *targetPixels++ = *pSrcRGB++; + *targetPixels++ = *pSrcRGB++; + *targetPixels++ = 0xff - *pSrcA++; + } + + pSrcRGB += nExtraRGB; + pSrcA += nExtraA; + } + + pRetval->UnlockBits(&aGdiPlusBitmapData); + } + else + { + delete pRetval; + pRetval = nullptr; + } + } + + if(pExtraA) + { + // #i123478# shockingly, BitmapBuffer does not free the memory it is controlling + // in its destructor, this *has to be done handish*. Doing it here now + delete[] pExtraA->mpBits; + pExtraA.reset(); + } + else + { + pSalA->ReleaseBuffer(pA, BitmapAccessMode::Read); + } + + if(pExtraWinSalA) + { + delete pExtraWinSalA; + } + + if(pExtraRGB) + { + // #i123478# shockingly, BitmapBuffer does not free the memory it is controlling + // in its destructor, this *has to be done by hand*. Doing it here now + delete[] pExtraRGB->mpBits; + pExtraRGB.reset(); + } + else + { + pSalRGB->ReleaseBuffer(pRGB, BitmapAccessMode::Read); + } + + if(pExtraWinSalRGB) + { + delete pExtraWinSalRGB; + } + + return pRetval; +} + +bool WinSalBitmap::Create( HANDLE hBitmap, bool bDIB, bool bCopyHandle ) +{ + bool bRet = true; + + if( bDIB ) + mhDIB = static_cast<HGLOBAL>( bCopyHandle ? ImplCopyDIBOrDDB( hBitmap, true ) : hBitmap ); + else + mhDDB = static_cast<HBITMAP>( bCopyHandle ? ImplCopyDIBOrDDB( hBitmap, false ) : hBitmap ); + + if( mhDIB ) + { + PBITMAPINFOHEADER pBIH = static_cast<PBITMAPINFOHEADER>(GlobalLock( mhDIB )); + + maSize = Size( pBIH->biWidth, pBIH->biHeight ); + mnBitCount = pBIH->biBitCount; + + if( mnBitCount ) + mnBitCount = ( mnBitCount <= 1 ) ? 1 : ( mnBitCount <= 4 ) ? 4 : ( mnBitCount <= 8 ) ? 8 : 24; + + GlobalUnlock( mhDIB ); + } + else if( mhDDB ) + { + BITMAP aDDBInfo; + + if( GetObjectW( mhDDB, sizeof( aDDBInfo ), &aDDBInfo ) ) + { + maSize = Size( aDDBInfo.bmWidth, aDDBInfo.bmHeight ); + mnBitCount = aDDBInfo.bmPlanes * aDDBInfo.bmBitsPixel; + + if( mnBitCount ) + { + mnBitCount = ( mnBitCount <= 1 ) ? 1 : + ( mnBitCount <= 4 ) ? 4 : + ( mnBitCount <= 8 ) ? 8 : 24; + } + } + else + { + mhDDB = nullptr; + bRet = false; + } + } + else + bRet = false; + + return bRet; +} + +bool WinSalBitmap::Create( const Size& rSize, sal_uInt16 nBitCount, const BitmapPalette& rPal ) +{ + bool bRet = false; + + mhDIB = ImplCreateDIB( rSize, nBitCount, rPal ); + + if( mhDIB ) + { + maSize = rSize; + mnBitCount = nBitCount; + bRet = true; + } + + return bRet; +} + +bool WinSalBitmap::Create( const SalBitmap& rSSalBitmap ) +{ + bool bRet = false; + const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap); + + if ( rSalBitmap.mhDIB || rSalBitmap.mhDDB ) + { + HANDLE hNewHdl = ImplCopyDIBOrDDB( rSalBitmap.mhDIB ? rSalBitmap.mhDIB : rSalBitmap.mhDDB, + rSalBitmap.mhDIB != nullptr ); + + if ( hNewHdl ) + { + if( rSalBitmap.mhDIB ) + mhDIB = static_cast<HGLOBAL>(hNewHdl); + else if( rSalBitmap.mhDDB ) + mhDDB = static_cast<HBITMAP>(hNewHdl); + + maSize = rSalBitmap.maSize; + mnBitCount = rSalBitmap.mnBitCount; + + bRet = true; + } + } + + return bRet; +} + +bool WinSalBitmap::Create( const SalBitmap& rSSalBmp, SalGraphics* pSGraphics ) +{ + bool bRet = false; + + const WinSalBitmap& rSalBmp = static_cast<const WinSalBitmap&>(rSSalBmp); + WinSalGraphics* pGraphics = static_cast<WinSalGraphics*>(pSGraphics); + + if( rSalBmp.mhDIB ) + { + PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( rSalBmp.mhDIB )); + HDC hDC = pGraphics->getHDC(); + HBITMAP hNewDDB; + BITMAP aDDBInfo; + PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize + + ImplGetDIBColorCount( rSalBmp.mhDIB ) * sizeof( RGBQUAD ); + + if( pBI->bmiHeader.biBitCount == 1 ) + { + hNewDDB = CreateBitmap( pBI->bmiHeader.biWidth, pBI->bmiHeader.biHeight, 1, 1, nullptr ); + + if( hNewDDB ) + SetDIBits( hDC, hNewDDB, 0, pBI->bmiHeader.biHeight, pBits, pBI, DIB_RGB_COLORS ); + } + else + hNewDDB = CreateDIBitmap( hDC, &pBI->bmiHeader, CBM_INIT, pBits, pBI, DIB_RGB_COLORS ); + + GlobalUnlock( rSalBmp.mhDIB ); + + if( hNewDDB && GetObjectW( hNewDDB, sizeof( aDDBInfo ), &aDDBInfo ) ) + { + mhDDB = hNewDDB; + maSize = Size( aDDBInfo.bmWidth, aDDBInfo.bmHeight ); + mnBitCount = aDDBInfo.bmPlanes * aDDBInfo.bmBitsPixel; + + bRet = true; + } + else if( hNewDDB ) + DeleteObject( hNewDDB ); + } + + return bRet; +} + +bool WinSalBitmap::Create( const SalBitmap& rSSalBmp, sal_uInt16 nNewBitCount ) +{ + bool bRet = false; + + const WinSalBitmap& rSalBmp = static_cast<const WinSalBitmap&>(rSSalBmp); + + if( rSalBmp.mhDDB ) + { + mhDIB = ImplCreateDIB( rSalBmp.maSize, nNewBitCount, BitmapPalette() ); + + if( mhDIB ) + { + PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( mhDIB )); + const int nLines = static_cast<int>(rSalBmp.maSize.Height()); + HDC hDC = GetDC( nullptr ); + PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize + + ImplGetDIBColorCount( mhDIB ) * sizeof( RGBQUAD ); + SalData* pSalData = GetSalData(); + HPALETTE hOldPal = nullptr; + + if ( pSalData->mhDitherPal ) + { + hOldPal = SelectPalette( hDC, pSalData->mhDitherPal, TRUE ); + RealizePalette( hDC ); + } + + if( GetDIBits( hDC, rSalBmp.mhDDB, 0, nLines, pBits, pBI, DIB_RGB_COLORS ) == nLines ) + { + GlobalUnlock( mhDIB ); + maSize = rSalBmp.maSize; + mnBitCount = nNewBitCount; + bRet = true; + } + else + { + GlobalUnlock( mhDIB ); + GlobalFree( mhDIB ); + mhDIB = nullptr; + } + + if( hOldPal ) + SelectPalette( hDC, hOldPal, TRUE ); + + ReleaseDC( nullptr, hDC ); + } + } + + return bRet; +} + +bool WinSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& rBitmapCanvas, Size& /*rSize*/, bool bMask ) +{ + css::uno::Reference< css::beans::XFastPropertySet > + xFastPropertySet( rBitmapCanvas, css::uno::UNO_QUERY ); + + if( xFastPropertySet.get() ) { + css::uno::Sequence< css::uno::Any > args; + + if( xFastPropertySet->getFastPropertyValue(bMask ? 2 : 1) >>= args ) { + sal_Int64 aHBmp64; + + if( args[0] >>= aHBmp64 ) { + return Create( HBITMAP(aHBmp64), false, false ); + } + } + } + return false; +} + +sal_uInt16 WinSalBitmap::ImplGetDIBColorCount( HGLOBAL hDIB ) +{ + sal_uInt16 nColors = 0; + + if( hDIB ) + { + PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( hDIB )); + + if ( pBI->bmiHeader.biSize != sizeof( BITMAPCOREHEADER ) ) + { + if( pBI->bmiHeader.biBitCount <= 8 ) + { + if ( pBI->bmiHeader.biClrUsed ) + nColors = static_cast<sal_uInt16>(pBI->bmiHeader.biClrUsed); + else + nColors = 1 << pBI->bmiHeader.biBitCount; + } + } + else if( reinterpret_cast<PBITMAPCOREHEADER>(pBI)->bcBitCount <= 8 ) + nColors = 1 << reinterpret_cast<PBITMAPCOREHEADER>(pBI)->bcBitCount; + + GlobalUnlock( hDIB ); + } + + return nColors; +} + +HGLOBAL WinSalBitmap::ImplCreateDIB( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rPal ) +{ + SAL_WARN_IF( nBits != 1 && nBits != 4 && nBits != 8 && nBits != 24, "vcl", "Unsupported BitCount!" ); + + HGLOBAL hDIB = nullptr; + + if( rSize.IsEmpty() ) + return hDIB; + + // calculate bitmap size in Bytes + const sal_uLong nAlignedWidth4Bytes = AlignedWidth4Bytes( nBits * rSize.Width() ); + const sal_uLong nImageSize = nAlignedWidth4Bytes * rSize.Height(); + bool bOverflow = (nImageSize / nAlignedWidth4Bytes) != static_cast<sal_uLong>(rSize.Height()); + if( bOverflow ) + return hDIB; + + // allocate bitmap memory including header and palette + const sal_uInt16 nColors = (nBits <= 8) ? (1 << nBits) : 0; + const sal_uLong nHeaderSize = sizeof( BITMAPINFOHEADER ) + nColors * sizeof( RGBQUAD ); + bOverflow = (nHeaderSize + nImageSize) < nImageSize; + if( bOverflow ) + return hDIB; + + hDIB = GlobalAlloc( GHND, nHeaderSize + nImageSize ); + if( !hDIB ) + return hDIB; + + PBITMAPINFO pBI = static_cast<PBITMAPINFO>( GlobalLock( hDIB ) ); + PBITMAPINFOHEADER pBIH = reinterpret_cast<PBITMAPINFOHEADER>( pBI ); + + pBIH->biSize = sizeof( BITMAPINFOHEADER ); + pBIH->biWidth = rSize.Width(); + pBIH->biHeight = rSize.Height(); + pBIH->biPlanes = 1; + pBIH->biBitCount = nBits; + pBIH->biCompression = BI_RGB; + pBIH->biSizeImage = nImageSize; + pBIH->biXPelsPerMeter = 0; + pBIH->biYPelsPerMeter = 0; + pBIH->biClrUsed = 0; + pBIH->biClrImportant = 0; + + if( nColors ) + { + // copy the palette entries if any + const sal_uInt16 nMinCount = std::min( nColors, rPal.GetEntryCount() ); + if( nMinCount ) + memcpy( pBI->bmiColors, rPal.ImplGetColorBuffer(), nMinCount * sizeof(RGBQUAD) ); + } + + GlobalUnlock( hDIB ); + + return hDIB; +} + +HANDLE WinSalBitmap::ImplCopyDIBOrDDB( HANDLE hHdl, bool bDIB ) +{ + HANDLE hCopy = nullptr; + + if ( bDIB && hHdl ) + { + const sal_uLong nSize = GlobalSize( hHdl ); + + if ( (hCopy = GlobalAlloc( GHND, nSize )) != nullptr ) + { + memcpy( GlobalLock( hCopy ), GlobalLock( hHdl ), nSize ); + + GlobalUnlock( hCopy ); + GlobalUnlock( hHdl ); + } + } + else if ( hHdl ) + { + BITMAP aBmp; + + // find out size of source bitmap + GetObjectW( hHdl, sizeof( aBmp ), &aBmp ); + + // create destination bitmap + if ( (hCopy = CreateBitmapIndirect( &aBmp )) != nullptr ) + { + HDC hBmpDC = CreateCompatibleDC( nullptr ); + HBITMAP hBmpOld = static_cast<HBITMAP>(SelectObject( hBmpDC, hHdl )); + HDC hCopyDC = CreateCompatibleDC( hBmpDC ); + HBITMAP hCopyOld = static_cast<HBITMAP>(SelectObject( hCopyDC, hCopy )); + + BitBlt( hCopyDC, 0, 0, aBmp.bmWidth, aBmp.bmHeight, hBmpDC, 0, 0, SRCCOPY ); + + SelectObject( hCopyDC, hCopyOld ); + DeleteDC( hCopyDC ); + + SelectObject( hBmpDC, hBmpOld ); + DeleteDC( hBmpDC ); + } + } + + return hCopy; +} + +BitmapBuffer* WinSalBitmap::AcquireBuffer( BitmapAccessMode /*nMode*/ ) +{ + BitmapBuffer* pBuffer = nullptr; + + if( mhDIB ) + { + PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( mhDIB )); + PBITMAPINFOHEADER pBIH = &pBI->bmiHeader; + + if( ( pBIH->biCompression == BI_RLE4 ) || ( pBIH->biCompression == BI_RLE8 ) ) + { + Size aSizePix( pBIH->biWidth, pBIH->biHeight ); + HGLOBAL hNewDIB = ImplCreateDIB( aSizePix, pBIH->biBitCount, BitmapPalette() ); + + if( hNewDIB ) + { + PBITMAPINFO pNewBI = static_cast<PBITMAPINFO>(GlobalLock( hNewDIB )); + PBITMAPINFOHEADER pNewBIH = &pNewBI->bmiHeader; + const sal_uInt16 nColorCount = ImplGetDIBColorCount( hNewDIB ); + const sal_uLong nOffset = pBI->bmiHeader.biSize + nColorCount * sizeof( RGBQUAD ); + BYTE* pOldBits = reinterpret_cast<PBYTE>(pBI) + nOffset; + BYTE* pNewBits = reinterpret_cast<PBYTE>(pNewBI) + nOffset; + + memcpy( pNewBI, pBI, nOffset ); + pNewBIH->biCompression = 0; + ImplDecodeRLEBuffer( pOldBits, pNewBits, aSizePix, pBIH->biCompression == BI_RLE4 ); + + GlobalUnlock( mhDIB ); + GlobalFree( mhDIB ); + mhDIB = hNewDIB; + pBI = pNewBI; + pBIH = pNewBIH; + } + } + + if( pBIH->biPlanes == 1 ) + { + pBuffer = new BitmapBuffer; + + pBuffer->mnFormat = pBIH->biBitCount == 1 ? ScanlineFormat::N1BitMsbPal : + pBIH->biBitCount == 4 ? ScanlineFormat::N4BitMsnPal : + pBIH->biBitCount == 8 ? ScanlineFormat::N8BitPal : + pBIH->biBitCount == 24 ? ScanlineFormat::N24BitTcBgr : + pBIH->biBitCount == 32 ? ScanlineFormat::N32BitTcMask : + ScanlineFormat::NONE; + + if( RemoveScanline( pBuffer->mnFormat ) != ScanlineFormat::NONE ) + { + pBuffer->mnWidth = maSize.Width(); + pBuffer->mnHeight = maSize.Height(); + pBuffer->mnScanlineSize = AlignedWidth4Bytes( maSize.Width() * pBIH->biBitCount ); + pBuffer->mnBitCount = static_cast<sal_uInt16>(pBIH->biBitCount); + + if( pBuffer->mnBitCount <= 8 ) + { + const sal_uInt16 nPalCount = ImplGetDIBColorCount( mhDIB ); + + pBuffer->maPalette.SetEntryCount( nPalCount ); + memcpy( pBuffer->maPalette.ImplGetColorBuffer(), pBI->bmiColors, nPalCount * sizeof( RGBQUAD ) ); + pBuffer->mpBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize + nPalCount * sizeof( RGBQUAD ); + } + else if( ( pBIH->biBitCount == 16 ) || ( pBIH->biBitCount == 32 ) ) + { + sal_uLong nOffset = 0; + + if( pBIH->biCompression == BI_BITFIELDS ) + { + nOffset = 3 * sizeof( RGBQUAD ); + ColorMaskElement aRedMask(*reinterpret_cast<UINT32*>(&pBI->bmiColors[ 0 ])); + aRedMask.CalcMaskShift(); + ColorMaskElement aGreenMask(*reinterpret_cast<UINT32*>(&pBI->bmiColors[ 1 ])); + aGreenMask.CalcMaskShift(); + ColorMaskElement aBlueMask(*reinterpret_cast<UINT32*>(&pBI->bmiColors[ 2 ])); + aBlueMask.CalcMaskShift(); + pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask); + } + else if( pBIH->biBitCount == 16 ) + { + ColorMaskElement aRedMask(0x00007c00UL); + aRedMask.CalcMaskShift(); + ColorMaskElement aGreenMask(0x000003e0UL); + aGreenMask.CalcMaskShift(); + ColorMaskElement aBlueMask(0x0000001fUL); + aBlueMask.CalcMaskShift(); + pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask); + } + else + { + ColorMaskElement aRedMask(0x00ff0000UL); + aRedMask.CalcMaskShift(); + ColorMaskElement aGreenMask(0x0000ff00UL); + aGreenMask.CalcMaskShift(); + ColorMaskElement aBlueMask(0x000000ffUL); + aBlueMask.CalcMaskShift(); + pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask); + } + + pBuffer->mpBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize + nOffset; + } + else + pBuffer->mpBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize; + } + else + { + GlobalUnlock( mhDIB ); + delete pBuffer; + pBuffer = nullptr; + } + } + else + GlobalUnlock( mhDIB ); + } + + return pBuffer; +} + +void WinSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, BitmapAccessMode nMode ) +{ + if( pBuffer ) + { + if( mhDIB ) + { + if( nMode == BitmapAccessMode::Write && !!pBuffer->maPalette ) + { + PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( mhDIB )); + const sal_uInt16 nCount = pBuffer->maPalette.GetEntryCount(); + const sal_uInt16 nDIBColorCount = ImplGetDIBColorCount( mhDIB ); + memcpy( pBI->bmiColors, pBuffer->maPalette.ImplGetColorBuffer(), std::min( nDIBColorCount, nCount ) * sizeof( RGBQUAD ) ); + GlobalUnlock( mhDIB ); + } + + GlobalUnlock( mhDIB ); + } + + delete pBuffer; + } + if( nMode == BitmapAccessMode::Write ) + InvalidateChecksum(); +} + +void WinSalBitmap::ImplDecodeRLEBuffer( const BYTE* pSrcBuf, BYTE* pDstBuf, + const Size& rSizePixel, bool bRLE4 ) +{ + sal_uInt8 const * pRLE = pSrcBuf; + sal_uInt8* pDIB = pDstBuf; + sal_uInt8* pRow = pDstBuf; + sal_uLong nWidthAl = AlignedWidth4Bytes( rSizePixel.Width() * ( bRLE4 ? 4UL : 8UL ) ); + sal_uInt8* pLast = pDIB + rSizePixel.Height() * nWidthAl - 1; + sal_uLong nCountByte; + sal_uLong nRunByte; + sal_uLong i; + BYTE cTmp; + bool bEndDecoding = false; + + if( pRLE && pDIB ) + { + sal_uLong nX = 0; + do + { + if( ( nCountByte = *pRLE++ ) == 0 ) + { + nRunByte = *pRLE++; + + if( nRunByte > 2 ) + { + if( bRLE4 ) + { + nCountByte = nRunByte >> 1; + + for( i = 0; i < nCountByte; i++ ) + { + cTmp = *pRLE++; + ImplSetPixel4( pDIB, nX++, cTmp >> 4 ); + ImplSetPixel4( pDIB, nX++, cTmp & 0x0f ); + } + + if( nRunByte & 1 ) + ImplSetPixel4( pDIB, nX++, *pRLE++ >> 4 ); + + if( ( ( nRunByte + 1 ) >> 1 ) & 1 ) + pRLE++; + } + else + { + memcpy( &pDIB[ nX ], pRLE, nRunByte ); + pRLE += nRunByte; + nX += nRunByte; + + if( nRunByte & 1 ) + pRLE++; + } + } + else if( !nRunByte ) + { + pDIB = ( pRow += nWidthAl ); + nX = 0; + } + else if( nRunByte == 1 ) + bEndDecoding = true; + else + { + nX += *pRLE++; + pDIB = ( pRow += ( *pRLE++ ) * nWidthAl ); + } + } + else + { + cTmp = *pRLE++; + + if( bRLE4 ) + { + nRunByte = nCountByte >> 1; + + for( i = 0; i < nRunByte; i++ ) + { + ImplSetPixel4( pDIB, nX++, cTmp >> 4 ); + ImplSetPixel4( pDIB, nX++, cTmp & 0x0f ); + } + + if( nCountByte & 1 ) + ImplSetPixel4( pDIB, nX++, cTmp >> 4 ); + } + else + { + for( i = 0; i < nCountByte; i++ ) + pDIB[ nX++ ] = cTmp; + } + } + } + while( !bEndDecoding && ( pDIB <= pLast ) ); + } +} + +bool WinSalBitmap::GetSystemData( BitmapSystemData& rData ) +{ + bool bRet = false; + if( mhDIB || mhDDB ) + { + bRet = true; + rData.pDIB = mhDIB; + const Size& rSize = GetSize (); + rData.mnWidth = rSize.Width(); + rData.mnHeight = rSize.Height(); + } + return bRet; +} + +bool WinSalBitmap::ScalingSupported() const +{ + return false; +} + +bool WinSalBitmap::Scale( const double& /*rScaleX*/, const double& /*rScaleY*/, BmpScaleFlag /*nScaleFlag*/ ) +{ + return false; +} + +bool WinSalBitmap::Replace( const Color& /*rSearchColor*/, const Color& /*rReplaceColor*/, sal_uInt8 /*nTol*/ ) +{ + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |