1
0
Fork 0
libreoffice/vcl/win/gdi/salgdi.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

1145 lines
36 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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 <string.h>
#include <svsys.h>
#include <rtl/strbuf.hxx>
#include <tools/poly.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <comphelper/windowserrorstring.hxx>
#include <win/wincomp.hxx>
#include <win/saldata.hxx>
#include <win/salgdi.h>
#include <win/salframe.h>
#include <win/salvd.h>
#include <win/winlayout.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <salgdiimpl.hxx>
#include "gdiimpl.hxx"
#include <config_features.h>
#include <vcl/skia/SkiaHelper.hxx>
#if HAVE_FEATURE_SKIA
#include <skia/win/gdiimpl.hxx>
#endif
#define DITHER_PAL_DELTA 51
#define DITHER_PAL_STEPS 6
#define DITHER_PAL_COUNT (DITHER_PAL_STEPS*DITHER_PAL_STEPS*DITHER_PAL_STEPS)
#define DITHER_MAX_SYSCOLOR 16
#define DITHER_EXTRA_COLORS 1
namespace
{
struct SysColorEntry
{
DWORD nRGB;
SysColorEntry* pNext;
};
SysColorEntry* pFirstSysColor = nullptr;
SysColorEntry* pActSysColor = nullptr;
void DeleteSysColorList()
{
SysColorEntry* pEntry = pFirstSysColor;
pActSysColor = pFirstSysColor = nullptr;
while( pEntry )
{
SysColorEntry* pTmp = pEntry->pNext;
delete pEntry;
pEntry = pTmp;
}
}
} // namespace
// Blue7
static PALETTEENTRY aImplExtraColor1 =
{
0, 184, 255, 0
};
static PALETTEENTRY aImplSalSysPalEntryAry[ DITHER_MAX_SYSCOLOR ] =
{
{ 0, 0, 0, 0 },
{ 0, 0, 0x80, 0 },
{ 0, 0x80, 0, 0 },
{ 0, 0x80, 0x80, 0 },
{ 0x80, 0, 0, 0 },
{ 0x80, 0, 0x80, 0 },
{ 0x80, 0x80, 0, 0 },
{ 0x80, 0x80, 0x80, 0 },
{ 0xC0, 0xC0, 0xC0, 0 },
{ 0, 0, 0xFF, 0 },
{ 0, 0xFF, 0, 0 },
{ 0, 0xFF, 0xFF, 0 },
{ 0xFF, 0, 0, 0 },
{ 0xFF, 0, 0xFF, 0 },
{ 0xFF, 0xFF, 0, 0 },
{ 0xFF, 0xFF, 0xFF, 0 }
};
// we must create pens with 1-pixel width; otherwise the S3-graphics card
// map has many paint problems when drawing polygons/polyLines and a
// complex is set
#define GSL_PEN_WIDTH 1
void ImplInitSalGDI()
{
SalData* pSalData = GetSalData();
pSalData->mbResourcesAlreadyFreed = false;
// init stock brushes
pSalData->maStockPenColorAry[0] = PALETTERGB( 0, 0, 0 );
pSalData->maStockPenColorAry[1] = PALETTERGB( 0xFF, 0xFF, 0xFF );
pSalData->maStockPenColorAry[2] = PALETTERGB( 0xC0, 0xC0, 0xC0 );
pSalData->maStockPenColorAry[3] = PALETTERGB( 0x80, 0x80, 0x80 );
pSalData->mhStockPenAry[0] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[0] );
pSalData->mhStockPenAry[1] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[1] );
pSalData->mhStockPenAry[2] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[2] );
pSalData->mhStockPenAry[3] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[3] );
pSalData->mnStockPenCount = 4;
pSalData->maStockBrushColorAry[0] = PALETTERGB( 0, 0, 0 );
pSalData->maStockBrushColorAry[1] = PALETTERGB( 0xFF, 0xFF, 0xFF );
pSalData->maStockBrushColorAry[2] = PALETTERGB( 0xC0, 0xC0, 0xC0 );
pSalData->maStockBrushColorAry[3] = PALETTERGB( 0x80, 0x80, 0x80 );
pSalData->mhStockBrushAry[0] = CreateSolidBrush( pSalData->maStockBrushColorAry[0] );
pSalData->mhStockBrushAry[1] = CreateSolidBrush( pSalData->maStockBrushColorAry[1] );
pSalData->mhStockBrushAry[2] = CreateSolidBrush( pSalData->maStockBrushColorAry[2] );
pSalData->mhStockBrushAry[3] = CreateSolidBrush( pSalData->maStockBrushColorAry[3] );
pSalData->mnStockBrushCount = 4;
// initialize temporary font lists
pSalData->mpSharedTempFontItem = nullptr;
pSalData->mpOtherTempFontItem = nullptr;
// support palettes for 256 color displays
HDC hDC = GetDC( nullptr );
int nBitsPixel = GetDeviceCaps( hDC, BITSPIXEL );
int nPlanes = GetDeviceCaps( hDC, PLANES );
int nRasterCaps = GetDeviceCaps( hDC, RASTERCAPS );
int nBitCount = nBitsPixel * nPlanes;
if ( (nBitCount > 8) && (nBitCount < 24) )
{
// test if we have to dither
HDC hMemDC = ::CreateCompatibleDC( hDC );
HBITMAP hMemBmp = ::CreateCompatibleBitmap( hDC, 8, 8 );
HBITMAP hBmpOld = static_cast<HBITMAP>(::SelectObject( hMemDC, hMemBmp ));
HBRUSH hMemBrush = ::CreateSolidBrush( PALETTERGB( 175, 171, 169 ) );
HBRUSH hBrushOld = static_cast<HBRUSH>(::SelectObject( hMemDC, hMemBrush ));
bool bDither16 = true;
::PatBlt( hMemDC, 0, 0, 8, 8, PATCOPY );
const COLORREF aCol( ::GetPixel( hMemDC, 0, 0 ) );
for( int nY = 0; ( nY < 8 ) && bDither16; nY++ )
for( int nX = 0; ( nX < 8 ) && bDither16; nX++ )
if( ::GetPixel( hMemDC, nX, nY ) != aCol )
bDither16 = false;
::SelectObject( hMemDC, hBrushOld );
::DeleteObject( hMemBrush );
::SelectObject( hMemDC, hBmpOld );
::DeleteObject( hMemBmp );
::DeleteDC( hMemDC );
if( bDither16 )
{
// create DIBPattern for 16Bit dithering
tools::Long n;
pSalData->mhDitherDIB = GlobalAlloc( GMEM_FIXED, sizeof( BITMAPINFOHEADER ) + 192 );
pSalData->mpDitherDIB = static_cast<BYTE*>(GlobalLock( pSalData->mhDitherDIB ));
pSalData->mpDitherDiff.reset(new tools::Long[ 256 ]);
pSalData->mpDitherLow.reset(new BYTE[ 256 ]);
pSalData->mpDitherHigh.reset(new BYTE[ 256 ]);
pSalData->mpDitherDIBData = pSalData->mpDitherDIB + sizeof( BITMAPINFOHEADER );
memset( pSalData->mpDitherDIB, 0, sizeof( BITMAPINFOHEADER ) );
BITMAPINFOHEADER* pBIH = reinterpret_cast<BITMAPINFOHEADER*>(pSalData->mpDitherDIB);
pBIH->biSize = sizeof( BITMAPINFOHEADER );
pBIH->biWidth = 8;
pBIH->biHeight = 8;
pBIH->biPlanes = 1;
pBIH->biBitCount = 24;
for( n = 0; n < 256; n++ )
pSalData->mpDitherDiff[ n ] = n - ( n & 248L );
for( n = 0; n < 256; n++ )
pSalData->mpDitherLow[ n ] = static_cast<BYTE>( n & 248 );
for( n = 0; n < 256; n++ )
pSalData->mpDitherHigh[ n ] = static_cast<BYTE>(std::min( pSalData->mpDitherLow[ n ] + 8, 255 ));
}
}
else if ( (nRasterCaps & RC_PALETTE) && (nBitCount == 8) )
{
BYTE nRed, nGreen, nBlue;
BYTE nR, nG, nB;
PALETTEENTRY* pPalEntry;
LOGPALETTE* pLogPal;
const sal_uInt16 nDitherPalCount = DITHER_PAL_COUNT;
sal_uLong nTotalCount = DITHER_MAX_SYSCOLOR + nDitherPalCount + DITHER_EXTRA_COLORS;
// create logical palette
pLogPal = reinterpret_cast<LOGPALETTE*>(new char[ sizeof( LOGPALETTE ) + ( nTotalCount * sizeof( PALETTEENTRY ) ) ]);
pLogPal->palVersion = 0x0300;
pLogPal->palNumEntries = static_cast<sal_uInt16>(nTotalCount);
pPalEntry = pLogPal->palPalEntry;
// Standard colors
memcpy( pPalEntry, aImplSalSysPalEntryAry, DITHER_MAX_SYSCOLOR * sizeof( PALETTEENTRY ) );
pPalEntry += DITHER_MAX_SYSCOLOR;
// own palette (6/6/6)
for( nB=0, nBlue=0; nB < DITHER_PAL_STEPS; nB++, nBlue += DITHER_PAL_DELTA )
{
for( nG=0, nGreen=0; nG < DITHER_PAL_STEPS; nG++, nGreen += DITHER_PAL_DELTA )
{
for( nR=0, nRed=0; nR < DITHER_PAL_STEPS; nR++, nRed += DITHER_PAL_DELTA )
{
pPalEntry->peRed = nRed;
pPalEntry->peGreen = nGreen;
pPalEntry->peBlue = nBlue;
pPalEntry->peFlags = 0;
pPalEntry++;
}
}
}
// insert special 'Blue' as standard drawing color
*pPalEntry++ = aImplExtraColor1;
// create palette
pSalData->mhDitherPal = CreatePalette( pLogPal );
delete[] reinterpret_cast<char*>(pLogPal);
if( pSalData->mhDitherPal )
{
// create DIBPattern for 8Bit dithering
tools::Long const nSize = sizeof( BITMAPINFOHEADER ) + ( 256 * sizeof( short ) ) + 64;
tools::Long n;
pSalData->mhDitherDIB = GlobalAlloc( GMEM_FIXED, nSize );
pSalData->mpDitherDIB = static_cast<BYTE*>(GlobalLock( pSalData->mhDitherDIB ));
pSalData->mpDitherDiff.reset(new tools::Long[ 256 ]);
pSalData->mpDitherLow.reset(new BYTE[ 256 ]);
pSalData->mpDitherHigh.reset(new BYTE[ 256 ]);
pSalData->mpDitherDIBData = pSalData->mpDitherDIB + sizeof( BITMAPINFOHEADER ) + ( 256 * sizeof( short ) );
memset( pSalData->mpDitherDIB, 0, sizeof( BITMAPINFOHEADER ) );
BITMAPINFOHEADER* pBIH = reinterpret_cast<BITMAPINFOHEADER*>(pSalData->mpDitherDIB);
short* pColors = reinterpret_cast<short*>( pSalData->mpDitherDIB + sizeof( BITMAPINFOHEADER ) );
pBIH->biSize = sizeof( BITMAPINFOHEADER );
pBIH->biWidth = 8;
pBIH->biHeight = 8;
pBIH->biPlanes = 1;
pBIH->biBitCount = 8;
for( n = 0; n < nDitherPalCount; n++ )
pColors[ n ] = static_cast<short>( n + DITHER_MAX_SYSCOLOR );
for( n = 0; n < 256; n++ )
pSalData->mpDitherDiff[ n ] = n % 51;
for( n = 0; n < 256; n++ )
pSalData->mpDitherLow[ n ] = static_cast<BYTE>( n / 51 );
for( n = 0; n < 256; n++ )
pSalData->mpDitherHigh[ n ] = static_cast<BYTE>(std::min( pSalData->mpDitherLow[ n ] + 1, 5 ));
}
// get system color entries
ImplUpdateSysColorEntries();
}
ReleaseDC( nullptr, hDC );
}
void ImplFreeSalGDI()
{
SalData* pSalData = GetSalData();
if (pSalData->mbResourcesAlreadyFreed)
return;
// destroy stock objects
int i;
for ( i = 0; i < pSalData->mnStockPenCount; i++ )
DeletePen( pSalData->mhStockPenAry[i] );
for ( i = 0; i < pSalData->mnStockBrushCount; i++ )
DeleteBrush( pSalData->mhStockBrushAry[i] );
// delete 50% Brush
if ( pSalData->mh50Brush )
{
DeleteBrush( pSalData->mh50Brush );
pSalData->mh50Brush = nullptr;
}
// delete 50% Bitmap
if ( pSalData->mh50Bmp )
{
DeleteBitmap( pSalData->mh50Bmp );
pSalData->mh50Bmp = nullptr;
}
ImplClearHDCCache( pSalData );
// delete Ditherpalette, if existing
if ( pSalData->mhDitherPal )
{
DeleteObject( pSalData->mhDitherPal );
pSalData->mhDitherPal = nullptr;
}
// delete buffers for dithering DIB patterns, if necessary
if ( pSalData->mhDitherDIB )
{
GlobalUnlock( pSalData->mhDitherDIB );
GlobalFree( pSalData->mhDitherDIB );
pSalData->mhDitherDIB = nullptr;
pSalData->mpDitherDiff.reset();
pSalData->mpDitherLow.reset();
pSalData->mpDitherHigh.reset();
}
DeleteSysColorList();
// delete icon cache
SalIcon* pIcon = pSalData->mpFirstIcon;
pSalData->mpFirstIcon = nullptr;
while( pIcon )
{
SalIcon* pTmp = pIcon->pNext;
DestroyIcon( pIcon->hIcon );
DestroyIcon( pIcon->hSmallIcon );
delete pIcon;
pIcon = pTmp;
}
// delete temporary font list
ImplReleaseTempFonts(*pSalData, true);
pSalData->mbResourcesAlreadyFreed = true;
}
int ImplIsSysColorEntry( Color nColor )
{
SysColorEntry* pEntry = pFirstSysColor;
const DWORD nTestRGB = static_cast<DWORD>(RGB( nColor.GetRed(),
nColor.GetGreen(),
nColor.GetBlue() ));
while ( pEntry )
{
if ( pEntry->nRGB == nTestRGB )
return TRUE;
pEntry = pEntry->pNext;
}
return FALSE;
}
static int ImplIsPaletteEntry( BYTE nRed, BYTE nGreen, BYTE nBlue )
{
// dither color?
if ( !(nRed % DITHER_PAL_DELTA) && !(nGreen % DITHER_PAL_DELTA) && !(nBlue % DITHER_PAL_DELTA) )
return TRUE;
PALETTEENTRY* pPalEntry = aImplSalSysPalEntryAry;
// standard palette color?
for ( sal_uInt16 i = 0; i < DITHER_MAX_SYSCOLOR; i++, pPalEntry++ )
{
if( pPalEntry->peRed == nRed && pPalEntry->peGreen == nGreen && pPalEntry->peBlue == nBlue )
return TRUE;
}
// extra color?
if ( aImplExtraColor1.peRed == nRed &&
aImplExtraColor1.peGreen == nGreen &&
aImplExtraColor1.peBlue == nBlue )
{
return TRUE;
}
return FALSE;
}
static void ImplInsertSysColorEntry( int nSysIndex )
{
const DWORD nRGB = GetSysColor( nSysIndex );
if ( !ImplIsPaletteEntry( GetRValue( nRGB ), GetGValue( nRGB ), GetBValue( nRGB ) ) )
{
if ( !pFirstSysColor )
{
pActSysColor = pFirstSysColor = new SysColorEntry;
pFirstSysColor->nRGB = nRGB;
pFirstSysColor->pNext = nullptr;
}
else
{
pActSysColor = pActSysColor->pNext = new SysColorEntry;
pActSysColor->nRGB = nRGB;
pActSysColor->pNext = nullptr;
}
}
}
void ImplUpdateSysColorEntries()
{
DeleteSysColorList();
// create new sys color list
ImplInsertSysColorEntry( COLOR_ACTIVEBORDER );
ImplInsertSysColorEntry( COLOR_INACTIVEBORDER );
ImplInsertSysColorEntry( COLOR_GRADIENTACTIVECAPTION );
ImplInsertSysColorEntry( COLOR_GRADIENTINACTIVECAPTION );
ImplInsertSysColorEntry( COLOR_3DFACE );
ImplInsertSysColorEntry( COLOR_3DHILIGHT );
ImplInsertSysColorEntry( COLOR_3DLIGHT );
ImplInsertSysColorEntry( COLOR_3DSHADOW );
ImplInsertSysColorEntry( COLOR_3DDKSHADOW );
ImplInsertSysColorEntry( COLOR_INFOBK );
ImplInsertSysColorEntry( COLOR_INFOTEXT );
ImplInsertSysColorEntry( COLOR_BTNTEXT );
ImplInsertSysColorEntry( COLOR_WINDOW );
ImplInsertSysColorEntry( COLOR_WINDOWTEXT );
ImplInsertSysColorEntry( COLOR_HIGHLIGHT );
ImplInsertSysColorEntry( COLOR_HIGHLIGHTTEXT );
ImplInsertSysColorEntry( COLOR_MENU );
ImplInsertSysColorEntry( COLOR_MENUTEXT );
ImplInsertSysColorEntry( COLOR_ACTIVECAPTION );
ImplInsertSysColorEntry( COLOR_CAPTIONTEXT );
ImplInsertSysColorEntry( COLOR_INACTIVECAPTION );
ImplInsertSysColorEntry( COLOR_INACTIVECAPTIONTEXT );
}
void WinSalGraphics::InitGraphics()
{
if (!getHDC())
return;
// calculate the minimal line width for the printer
if ( isPrinter() )
{
int nDPIX = GetDeviceCaps( getHDC(), LOGPIXELSX );
if ( nDPIX <= 300 )
mnPenWidth = 0;
else
mnPenWidth = nDPIX/300;
}
::SetTextAlign( getHDC(), TA_BASELINE | TA_LEFT | TA_NOUPDATECP );
::SetBkMode( getHDC(), TRANSPARENT );
::SetROP2( getHDC(), R2_COPYPEN );
}
void WinSalGraphics::DeInitGraphics()
{
if (!getHDC())
return;
// clear clip region
SelectClipRgn( getHDC(), nullptr );
// select default objects
if ( mhDefPen )
{
SelectPen( getHDC(), mhDefPen );
mhDefPen = nullptr;
}
if ( mhDefBrush )
{
SelectBrush( getHDC(), mhDefBrush );
mhDefBrush = nullptr;
}
if ( mhDefFont )
{
SelectFont( getHDC(), mhDefFont );
mhDefFont = nullptr;
}
setPalette(nullptr);
mpImpl->DeInit();
}
void WinSalGraphics::setHDC(HDC aNew)
{
DeInitGraphics();
mhLocalDC = aNew;
InitGraphics();
}
HDC ImplGetCachedDC( sal_uLong nID, HBITMAP hBmp )
{
SalData* pSalData = GetSalData();
HDCCache* pC = &pSalData->maHDCCache[ nID ];
if( !pC->mhDC )
{
HDC hDC = GetDC( nullptr );
// create new DC with DefaultBitmap
pC->mhDC = CreateCompatibleDC( hDC );
if( pSalData->mhDitherPal )
{
pC->mhDefPal = SelectPalette( pC->mhDC, pSalData->mhDitherPal, TRUE );
RealizePalette( pC->mhDC );
}
pC->mhSelBmp = CreateCompatibleBitmap( hDC, CACHED_HDC_DEFEXT, CACHED_HDC_DEFEXT );
pC->mhDefBmp = static_cast<HBITMAP>(SelectObject( pC->mhDC, pC->mhSelBmp ));
ReleaseDC( nullptr, hDC );
}
if ( hBmp )
SelectObject( pC->mhDC, pC->mhActBmp = hBmp );
else
pC->mhActBmp = nullptr;
return pC->mhDC;
}
void ImplReleaseCachedDC( sal_uLong nID )
{
SalData* pSalData = GetSalData();
HDCCache* pC = &pSalData->maHDCCache[ nID ];
if ( pC->mhActBmp )
SelectObject( pC->mhDC, pC->mhSelBmp );
}
void ImplClearHDCCache( SalData* pData )
{
for( sal_uLong i = 0; i < CACHESIZE_HDC; i++ )
{
HDCCache* pC = &pData->maHDCCache[ i ];
if( pC->mhDC )
{
SelectObject( pC->mhDC, pC->mhDefBmp );
if( pC->mhDefPal )
SelectPalette( pC->mhDC, pC->mhDefPal, TRUE );
DeleteDC( pC->mhDC );
DeleteObject( pC->mhSelBmp );
}
}
}
std::unique_ptr< CompatibleDC > CompatibleDC::create(SalGraphics &rGraphics, int x, int y, int width, int height)
{
#if HAVE_FEATURE_SKIA
if (SkiaHelper::isVCLSkiaEnabled())
return std::make_unique< SkiaCompatibleDC >( rGraphics, x, y, width, height );
#endif
return std::unique_ptr< CompatibleDC >( new CompatibleDC( rGraphics, x, y, width, height ));
}
CompatibleDC::CompatibleDC(SalGraphics &rGraphics, int x, int y, int width, int height, bool disable)
: mhBitmap(nullptr)
, mpData(nullptr)
, maRects(0, 0, width, height, x, y, width, height)
, mpImpl(nullptr)
{
WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
if( disable )
{
// we avoid the OpenGL drawing, instead we draw directly to the DC
mhCompatibleDC = rWinGraphics.getHDC();
return;
}
mpImpl = rWinGraphics.getWinSalGraphicsImplBase();
mhCompatibleDC = CreateCompatibleDC(rWinGraphics.getHDC());
// move the origin so that we always paint at 0,0 - to keep the bitmap
// small
OffsetViewportOrgEx(mhCompatibleDC, -x, -y, nullptr);
mhBitmap = WinSalVirtualDevice::ImplCreateVirDevBitmap(mhCompatibleDC, width, height, 32, reinterpret_cast<void **>(&mpData));
mhOrigBitmap = static_cast<HBITMAP>(SelectObject(mhCompatibleDC, mhBitmap));
}
CompatibleDC::~CompatibleDC()
{
if (mpImpl)
{
SelectObject(mhCompatibleDC, mhOrigBitmap);
DeleteObject(mhBitmap);
DeleteDC(mhCompatibleDC);
}
}
void CompatibleDC::fill(sal_uInt32 color)
{
if (!mpData)
return;
sal_uInt32 *p = mpData;
for (int i = maRects.mnSrcWidth * maRects.mnSrcHeight; i > 0; --i)
*p++ = color;
}
WinSalGraphics::WinSalGraphics(WinSalGraphics::Type eType, bool bScreen, HWND hWnd, [[maybe_unused]] SalGeometryProvider *pProvider):
mhLocalDC(nullptr),
mbPrinter(eType == WinSalGraphics::PRINTER),
mbVirDev(eType == WinSalGraphics::VIRTUAL_DEVICE),
mbWindow(eType == WinSalGraphics::WINDOW),
mbScreen(bScreen),
mhWnd(hWnd),
mhRegion(nullptr),
mhDefPen(nullptr),
mhDefBrush(nullptr),
mhDefFont(nullptr),
mhDefPal(nullptr),
mpStdClipRgnData(nullptr),
mnPenWidth(GSL_PEN_WIDTH)
{
#if HAVE_FEATURE_SKIA
if (SkiaHelper::isVCLSkiaEnabled() && !mbPrinter)
{
auto const impl = new WinSkiaSalGraphicsImpl(*this, pProvider);
mpImpl.reset(impl);
mWinSalGraphicsImplBase = impl;
}
else
#endif
{
auto const impl = new WinSalGraphicsImpl(*this);
mpImpl.reset(impl);
mWinSalGraphicsImplBase = impl;
}
}
WinSalGraphics::~WinSalGraphics()
{
// free obsolete GDI objects
ReleaseFonts();
if ( mhRegion )
{
DeleteRegion( mhRegion );
mhRegion = nullptr;
}
// delete cache data
delete [] reinterpret_cast<BYTE*>(mpStdClipRgnData);
setHDC(nullptr);
}
SalGraphicsImpl* WinSalGraphics::GetImpl() const
{
return mpImpl.get();
}
bool WinSalGraphics::isPrinter() const
{
return mbPrinter;
}
bool WinSalGraphics::isVirtualDevice() const
{
return mbVirDev;
}
bool WinSalGraphics::isWindow() const
{
return mbWindow;
}
bool WinSalGraphics::isScreen() const
{
return mbScreen;
}
HWND WinSalGraphics::gethWnd()
{
return mhWnd;
}
void WinSalGraphics::setHWND(HWND hWnd)
{
mhWnd = hWnd;
}
HPALETTE WinSalGraphics::getDefPal() const
{
assert(getHDC() || !mhDefPal);
return mhDefPal;
}
UINT WinSalGraphics::setPalette(HPALETTE hNewPal, BOOL bForceBkgd)
{
UINT res = GDI_ERROR;
if (!getHDC())
{
assert(!mhDefPal);
return res;
}
if (hNewPal)
{
HPALETTE hOldPal = SelectPalette(getHDC(), hNewPal, bForceBkgd);
if (hOldPal)
{
if (!mhDefPal)
mhDefPal = hOldPal;
res = RealizePalette(getHDC());
}
}
else
{
res = 0;
if (mhDefPal)
{
SelectPalette(getHDC(), mhDefPal, bForceBkgd);
mhDefPal = nullptr;
}
}
return res;
}
HRGN WinSalGraphics::getRegion() const
{
return mhRegion;
}
void WinSalGraphics::GetResolution( sal_Int32& rDPIX, sal_Int32& rDPIY )
{
rDPIX = GetDeviceCaps( getHDC(), LOGPIXELSX );
rDPIY = GetDeviceCaps( getHDC(), LOGPIXELSY );
// #111139# this fixes the symptom of div by zero on startup
// however, printing will fail most likely as communication with
// the printer seems not to work in this case
if( !rDPIX || !rDPIY )
rDPIX = rDPIY = 600;
}
// static
IDWriteFactory* WinSalGraphics::getDWriteFactory()
{
static sal::systools::COMReference<IDWriteFactory> pDWriteFactory(
[]()
{
sal::systools::COMReference<IDWriteFactory> pResult;
HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
reinterpret_cast<IUnknown**>(&pResult));
if (FAILED(hr))
{
SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << ": "
<< WindowsErrorStringFromHRESULT(hr));
abort();
}
return pResult;
}());
return pDWriteFactory.get();
}
// static
IDWriteGdiInterop* WinSalGraphics::getDWriteGdiInterop()
{
static sal::systools::COMReference<IDWriteGdiInterop> pDWriteGdiInterop(
[]()
{
sal::systools::COMReference<IDWriteGdiInterop> pResult;
HRESULT hr = getDWriteFactory()->GetGdiInterop(&pResult);
if (FAILED(hr))
{
SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << ": "
<< WindowsErrorStringFromHRESULT(hr));
abort();
}
return pResult;
}());
return pDWriteGdiInterop.get();
}
sal_uInt16 WinSalGraphics::GetBitCount() const
{
return mpImpl->GetBitCount();
}
tools::Long WinSalGraphics::GetGraphicsWidth() const
{
return mpImpl->GetGraphicsWidth();
}
void WinSalGraphics::Flush()
{
mWinSalGraphicsImplBase->Flush();
}
void WinSalGraphics::ResetClipRegion()
{
mpImpl->ResetClipRegion();
}
void WinSalGraphics::setClipRegion( const vcl::Region& i_rClip )
{
mpImpl->setClipRegion( i_rClip );
}
void WinSalGraphics::SetLineColor()
{
mpImpl->SetLineColor();
}
void WinSalGraphics::SetLineColor( Color nColor )
{
mpImpl->SetLineColor( nColor );
}
void WinSalGraphics::SetFillColor()
{
mpImpl->SetFillColor();
}
void WinSalGraphics::SetFillColor( Color nColor )
{
mpImpl->SetFillColor( nColor );
}
void WinSalGraphics::SetXORMode( bool bSet, bool bInvertOnly )
{
mpImpl->SetXORMode( bSet, bInvertOnly );
}
void WinSalGraphics::SetROPLineColor( SalROPColor nROPColor )
{
mpImpl->SetROPLineColor( nROPColor );
}
void WinSalGraphics::SetROPFillColor( SalROPColor nROPColor )
{
mpImpl->SetROPFillColor( nROPColor );
}
void WinSalGraphics::drawPixel( tools::Long nX, tools::Long nY )
{
mpImpl->drawPixel( nX, nY );
}
void WinSalGraphics::drawPixel( tools::Long nX, tools::Long nY, Color nColor )
{
mpImpl->drawPixel( nX, nY, nColor );
}
void WinSalGraphics::drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 )
{
mpImpl->drawLine( nX1, nY1, nX2, nY2 );
}
void WinSalGraphics::drawRect( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight )
{
mpImpl->drawRect( nX, nY, nWidth, nHeight );
}
void WinSalGraphics::drawPolyLine( sal_uInt32 nPoints, const Point* pPtAry )
{
mpImpl->drawPolyLine( nPoints, pPtAry );
}
void WinSalGraphics::drawPolygon( sal_uInt32 nPoints, const Point* pPtAry )
{
mpImpl->drawPolygon( nPoints, pPtAry );
}
void WinSalGraphics::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
const Point** pPtAry )
{
mpImpl->drawPolyPolygon( nPoly, pPoints, pPtAry );
}
bool WinSalGraphics::drawPolyLineBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
{
return mpImpl->drawPolyLineBezier( nPoints, pPtAry, pFlgAry );
}
bool WinSalGraphics::drawPolygonBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
{
return mpImpl->drawPolygonBezier( nPoints, pPtAry, pFlgAry );
}
bool WinSalGraphics::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints,
const Point* const* pPtAry, const PolyFlags* const* pFlgAry )
{
return mpImpl->drawPolyPolygonBezier( nPoly, pPoints, pPtAry, pFlgAry );
}
bool WinSalGraphics::drawGradient(const tools::PolyPolygon& rPoly, const Gradient& rGradient)
{
return mpImpl->drawGradient(rPoly, rGradient);
}
bool WinSalGraphics::implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolygon, SalGradient const & rGradient)
{
return mpImpl->implDrawGradient(rPolyPolygon, rGradient);
}
static BYTE* ImplSearchEntry( BYTE* pSource, BYTE const * pDest, sal_uLong nComp, sal_uLong nSize )
{
while ( nComp-- >= nSize )
{
sal_uLong i;
for ( i = 0; i < nSize; i++ )
{
if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
break;
}
if ( i == nSize )
return pSource;
pSource++;
}
return nullptr;
}
static bool ImplGetBoundingBox( double* nNumb, BYTE* pSource, sal_uLong nSize )
{
bool bRetValue = false;
BYTE* pDest = ImplSearchEntry( pSource, reinterpret_cast<BYTE const *>("%%BoundingBox:"), nSize, 14 );
if ( pDest )
{
nNumb[0] = nNumb[1] = nNumb[2] = nNumb[3] = 0;
pDest += 14;
int nSizeLeft = nSize - ( pDest - pSource );
if ( nSizeLeft > 100 )
nSizeLeft = 100; // only 100 bytes following the bounding box will be checked
int i;
for ( i = 0; ( i < 4 ) && nSizeLeft; i++ )
{
int nDivision = 1;
bool bDivision = false;
bool bNegative = false;
bool bValid = true;
while ( ( --nSizeLeft ) && ( ( *pDest == ' ' ) || ( *pDest == 0x9 ) ) ) pDest++;
BYTE nByte = *pDest;
while ( nSizeLeft && ( nByte != ' ' ) && ( nByte != 0x9 ) && ( nByte != 0xd ) && ( nByte != 0xa ) )
{
switch ( nByte )
{
case '.' :
if ( bDivision )
bValid = false;
else
bDivision = true;
break;
case '-' :
bNegative = true;
break;
default :
if ( ( nByte < '0' ) || ( nByte > '9' ) )
nSizeLeft = 1; // error parsing the bounding box values
else if ( bValid )
{
if ( bDivision )
nDivision*=10;
nNumb[i] *= 10;
nNumb[i] += nByte - '0';
}
break;
}
nSizeLeft--;
nByte = *(++pDest);
}
if ( bNegative )
nNumb[i] = -nNumb[i];
if ( bDivision && ( nDivision != 1 ) )
nNumb[i] /= nDivision;
}
if ( i == 4 )
bRetValue = true;
}
return bRetValue;
}
#define POSTSCRIPT_BUFSIZE 0x4000 // MAXIMUM BUFSIZE EQ 0xFFFF
bool WinSalGraphics::drawEPS( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, void* pPtr, sal_uInt32 nSize )
{
bool bRetValue = false;
if ( mbPrinter )
{
int nEscape = POSTSCRIPT_PASSTHROUGH;
if ( Escape( getHDC(), QUERYESCSUPPORT, sizeof( int ), reinterpret_cast<LPSTR>(&nEscape), nullptr ) )
{
double nBoundingBox[4];
if ( ImplGetBoundingBox( nBoundingBox, static_cast<BYTE*>(pPtr), nSize ) )
{
OStringBuffer aBuf( POSTSCRIPT_BUFSIZE );
// reserve place for a sal_uInt16
aBuf.append( "aa" );
// #107797# Write out EPS encapsulation header
// directly taken from the PLRM 3.0, p. 726. Note:
// this will definitely cause problems when
// recursively creating and embedding PostScript files
// in OOo, since we use statically-named variables
// here (namely, b4_Inc_state_salWin, dict_count_salWin and
// op_count_salWin). Currently, I have no idea on how to
// work around that, except from scanning and
// interpreting the EPS for unused identifiers.
// append the real text
aBuf.append( "\n\n/b4_Inc_state_salWin save def\n"
"/dict_count_salWin countdictstack def\n"
"/op_count_salWin count 1 sub def\n"
"userdict begin\n"
"/showpage {} def\n"
"0 setgray 0 setlinecap\n"
"1 setlinewidth 0 setlinejoin\n"
"10 setmiterlimit [] 0 setdash newpath\n"
"/languagelevel where\n"
"{\n"
" pop languagelevel\n"
" 1 ne\n"
" {\n"
" false setstrokeadjust false setoverprint\n"
" } if\n"
"} if\n\n" );
// #i10737# Apply clipping manually
// Windows seems to ignore any clipping at the HDC,
// when followed by a POSTSCRIPT_PASSTHROUGH
// Check whether we've got a clipping, consisting of
// exactly one rect (other cases should be, but aren't
// handled currently)
// TODO: Handle more than one rectangle here (take
// care, the buffer can handle only POSTSCRIPT_BUFSIZE
// characters!)
if ( mhRegion != nullptr &&
mpStdClipRgnData != nullptr &&
mpClipRgnData == mpStdClipRgnData &&
mpClipRgnData->rdh.nCount == 1 )
{
RECT* pRect = &(mpClipRgnData->rdh.rcBound);
aBuf.append( "\nnewpath\n"
+ OString::number(pRect->left) + " " + OString::number(pRect->top)
+ " moveto\n"
+ OString::number(pRect->right) + " " + OString::number(pRect->top)
+ " lineto\n"
+ OString::number(pRect->right) + " "
+ OString::number(pRect->bottom) + " lineto\n"
+ OString::number(pRect->left) + " "
+ OString::number(pRect->bottom) + " lineto\n"
"closepath\n"
"clip\n"
"newpath\n" );
}
// #107797# Write out buffer
*reinterpret_cast<sal_uInt16*>(const_cast<char *>(aBuf.getStr())) = static_cast<sal_uInt16>( aBuf.getLength() - 2 );
Escape ( getHDC(), nEscape, aBuf.getLength(), aBuf.getStr(), nullptr );
// #107797# Write out EPS transformation code
double dM11 = nWidth / ( nBoundingBox[2] - nBoundingBox[0] );
double dM22 = nHeight / (nBoundingBox[1] - nBoundingBox[3] );
// reserve a sal_uInt16 again
aBuf.setLength( 2 );
aBuf.append( "\n\n[" + OString::number(dM11) + " 0 0 " + OString::number(dM22) + " "
+ OString::number(nX - ( dM11 * nBoundingBox[0] )) + " "
+ OString::number(nY - ( dM22 * nBoundingBox[3] )) + "] concat\n"
"%%BeginDocument:\n" );
*reinterpret_cast<sal_uInt16*>(const_cast<char *>(aBuf.getStr())) = static_cast<sal_uInt16>( aBuf.getLength() - 2 );
Escape ( getHDC(), nEscape, aBuf.getLength(), aBuf.getStr(), nullptr );
// #107797# Write out actual EPS content
sal_uLong nToDo = nSize;
sal_uLong nDoNow;
while ( nToDo )
{
nDoNow = nToDo;
if ( nToDo > POSTSCRIPT_BUFSIZE - 2 )
nDoNow = POSTSCRIPT_BUFSIZE - 2;
// the following is based on the string buffer allocation
// of size POSTSCRIPT_BUFSIZE at construction time of aBuf
*reinterpret_cast<sal_uInt16*>(const_cast<char *>(aBuf.getStr())) = static_cast<sal_uInt16>(nDoNow);
memcpy( const_cast<char *>(aBuf.getStr() + 2), static_cast<BYTE*>(pPtr) + nSize - nToDo, nDoNow );
sal_uLong nResult = Escape ( getHDC(), nEscape, nDoNow + 2, aBuf.getStr(), nullptr );
if (!nResult )
break;
nToDo -= nResult;
}
// #107797# Write out EPS encapsulation footer
// reserve a sal_uInt16 again
aBuf.setLength( 2 );
aBuf.append( "%%EndDocument\n"
"count op_count_salWin sub {pop} repeat\n"
"countdictstack dict_count_salWin sub {end} repeat\n"
"b4_Inc_state_salWin restore\n\n" );
*reinterpret_cast<sal_uInt16*>(const_cast<char *>(aBuf.getStr())) = static_cast<sal_uInt16>( aBuf.getLength() - 2 );
Escape ( getHDC(), nEscape, aBuf.getLength(), aBuf.getStr(), nullptr );
bRetValue = true;
}
}
}
return bRetValue;
}
SystemGraphicsData WinSalGraphics::GetGraphicsData() const
{
SystemGraphicsData aRes;
aRes.nSize = sizeof(aRes);
aRes.hDC = getHDC();
return aRes;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */