(Second attempt at landing this) Image formats and graphics APIs use alpha, not transparency, so change our internal formats and data structures to work directly with alpha, so we don't need to modify data before we push it to graphics APIs. Add a couple of new Color constants to make the intention of the vcl code clearer. Notes (*) On macOS, tweaking the logic in CreateWithSalBitmapAndMask to more accurately reflect the requirements of the CGImageCreateWithMask function seems to fix some tests. (*) The vcl code does not properly support gradients with transparency. So the previous code was wrong, and this change is going to result in slightly different wrongness. Change-Id: I9e21c2e98d88ecfdc5f75db13bd1ffff7c38db98 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114168 Tested-by: Jenkins Reviewed-by: Patrick Luby <plubius@neooffice.org> Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
257 lines
9.8 KiB
C++
257 lines
9.8 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 <sal/config.h>
|
|
|
|
#include <memory>
|
|
|
|
#include <cppuhelper/supportsservice.hxx>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
#include <vcl/bitmapex.hxx>
|
|
|
|
#include <canvas/canvastools.hxx>
|
|
|
|
#include "dx_canvasbitmap.hxx"
|
|
#include "dx_impltools.hxx"
|
|
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
namespace dxcanvas
|
|
{
|
|
CanvasBitmap::CanvasBitmap( const IBitmapSharedPtr& rBitmap,
|
|
const DeviceRef& rDevice ) :
|
|
mpDevice( rDevice ),
|
|
mpBitmap( rBitmap )
|
|
{
|
|
ENSURE_OR_THROW( mpDevice.is() && mpBitmap,
|
|
"CanvasBitmap::CanvasBitmap(): Invalid surface or device" );
|
|
|
|
maCanvasHelper.setDevice( *mpDevice );
|
|
maCanvasHelper.setTarget( mpBitmap );
|
|
}
|
|
|
|
void CanvasBitmap::disposeThis()
|
|
{
|
|
mpBitmap.reset();
|
|
mpDevice.clear();
|
|
|
|
// forward to parent
|
|
CanvasBitmap_Base::disposeThis();
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct AlphaDIB
|
|
{
|
|
BITMAPINFOHEADER bmiHeader;
|
|
RGBQUAD bmiColors[256];
|
|
AlphaDIB()
|
|
: bmiHeader({0,0,0,1,8,BI_RGB,0,0,0,0,0})
|
|
{
|
|
for (size_t i = 0; i < 256; ++i)
|
|
{
|
|
// this here fills palette with grey level colors, starting
|
|
// from 0,0,0 up to 255,255,255
|
|
BYTE const b(i);
|
|
bmiColors[i] = { b,b,b,b };
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
uno::Any SAL_CALL CanvasBitmap::getFastPropertyValue( sal_Int32 nHandle )
|
|
{
|
|
uno::Any aRes;
|
|
// 0 ... get BitmapEx
|
|
// 1 ... get Pixbuf with bitmap RGB content
|
|
// 2 ... get Pixbuf with bitmap alpha mask
|
|
switch( nHandle )
|
|
{
|
|
// sorry, no BitmapEx here...
|
|
case 0:
|
|
aRes <<= reinterpret_cast<sal_Int64>( nullptr );
|
|
break;
|
|
|
|
case 1:
|
|
{
|
|
if(!mpBitmap->hasAlpha())
|
|
{
|
|
HBITMAP aHBmp;
|
|
mpBitmap->getBitmap()->GetHBITMAP(Gdiplus::Color(), &aHBmp );
|
|
|
|
uno::Sequence< uno::Any > args{ uno::Any(reinterpret_cast<sal_Int64>(aHBmp)) };
|
|
aRes <<= args;
|
|
}
|
|
else
|
|
{
|
|
// need to copy&convert the bitmap, since dx
|
|
// canvas uses inline alpha channel
|
|
HDC hScreenDC=GetDC(nullptr);
|
|
const basegfx::B2ISize aSize = mpBitmap->getSize();
|
|
HBITMAP hBmpBitmap = CreateCompatibleBitmap( hScreenDC,
|
|
aSize.getWidth(),
|
|
aSize.getHeight() );
|
|
if( !hBmpBitmap )
|
|
return aRes;
|
|
|
|
BITMAPINFOHEADER aBIH;
|
|
|
|
aBIH.biSize = sizeof( BITMAPINFOHEADER );
|
|
aBIH.biWidth = aSize.getWidth();
|
|
aBIH.biHeight = -aSize.getHeight();
|
|
aBIH.biPlanes = 1;
|
|
aBIH.biBitCount = 32;
|
|
aBIH.biCompression = BI_RGB; // expects pixel in
|
|
// bbggrrxx format
|
|
// (little endian)
|
|
aBIH.biSizeImage = 0;
|
|
aBIH.biXPelsPerMeter = 0;
|
|
aBIH.biYPelsPerMeter = 0;
|
|
aBIH.biClrUsed = 0;
|
|
aBIH.biClrImportant = 0;
|
|
|
|
Gdiplus::BitmapData aBmpData;
|
|
aBmpData.Width = aSize.getWidth();
|
|
aBmpData.Height = aSize.getHeight();
|
|
aBmpData.Stride = 4*aBmpData.Width;
|
|
aBmpData.PixelFormat = PixelFormat32bppARGB;
|
|
aBmpData.Scan0 = nullptr;
|
|
const Gdiplus::Rect aRect( 0,0,aSize.getWidth(),aSize.getHeight() );
|
|
BitmapSharedPtr pGDIPlusBitmap=mpBitmap->getBitmap();
|
|
if( Gdiplus::Ok != pGDIPlusBitmap->LockBits( &aRect,
|
|
Gdiplus::ImageLockModeRead,
|
|
PixelFormat32bppARGB, // outputs ARGB (big endian)
|
|
&aBmpData ) )
|
|
{
|
|
// failed to lock, bail out
|
|
return aRes;
|
|
}
|
|
|
|
// now aBmpData.Scan0 contains our bits - push
|
|
// them into HBITMAP, ignoring alpha
|
|
SetDIBits( hScreenDC, hBmpBitmap, 0, aSize.getHeight(), aBmpData.Scan0, reinterpret_cast<PBITMAPINFO>(&aBIH), DIB_RGB_COLORS );
|
|
|
|
pGDIPlusBitmap->UnlockBits( &aBmpData );
|
|
|
|
uno::Sequence< uno::Any > args{ uno::Any(reinterpret_cast<sal_Int64>(hBmpBitmap)) };
|
|
aRes <<= args;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
{
|
|
if(!mpBitmap->hasAlpha())
|
|
{
|
|
return aRes;
|
|
}
|
|
else
|
|
{
|
|
static AlphaDIB aDIB;
|
|
|
|
// need to copy&convert the bitmap, since dx
|
|
// canvas uses inline alpha channel
|
|
HDC hScreenDC=GetDC(nullptr);
|
|
const basegfx::B2ISize aSize = mpBitmap->getSize();
|
|
HBITMAP hBmpBitmap = CreateCompatibleBitmap( hScreenDC, aSize.getWidth(), aSize.getHeight() );
|
|
if( !hBmpBitmap )
|
|
return aRes;
|
|
|
|
aDIB.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
|
|
aDIB.bmiHeader.biWidth = aSize.getWidth();
|
|
aDIB.bmiHeader.biHeight = -aSize.getHeight();
|
|
aDIB.bmiHeader.biPlanes = 1;
|
|
aDIB.bmiHeader.biBitCount = 8;
|
|
aDIB.bmiHeader.biCompression = BI_RGB;
|
|
aDIB.bmiHeader.biSizeImage = 0;
|
|
aDIB.bmiHeader.biXPelsPerMeter = 0;
|
|
aDIB.bmiHeader.biYPelsPerMeter = 0;
|
|
aDIB.bmiHeader.biClrUsed = 0;
|
|
aDIB.bmiHeader.biClrImportant = 0;
|
|
|
|
Gdiplus::BitmapData aBmpData;
|
|
aBmpData.Width = aSize.getWidth();
|
|
aBmpData.Height = aSize.getHeight();
|
|
aBmpData.Stride = 4*aBmpData.Width;
|
|
aBmpData.PixelFormat = PixelFormat32bppARGB;
|
|
aBmpData.Scan0 = nullptr;
|
|
const Gdiplus::Rect aRect( 0,0,aSize.getWidth(),aSize.getHeight() );
|
|
BitmapSharedPtr pGDIPlusBitmap=mpBitmap->getBitmap();
|
|
if( Gdiplus::Ok != pGDIPlusBitmap->LockBits( &aRect,
|
|
Gdiplus::ImageLockModeRead,
|
|
PixelFormat32bppARGB, // outputs ARGB (big endian)
|
|
&aBmpData ) )
|
|
{
|
|
// failed to lock, bail out
|
|
return aRes;
|
|
}
|
|
|
|
// copy only alpha channel to pAlphaBits
|
|
const sal_Int32 nScanWidth((aSize.getWidth() + 3) & ~3);
|
|
std::unique_ptr<sal_uInt8[]> pAlphaBits( new sal_uInt8[nScanWidth*aSize.getHeight()] );
|
|
const sal_uInt8* pInBits=static_cast<sal_uInt8*>(aBmpData.Scan0);
|
|
pInBits+=3;
|
|
for( sal_Int32 y=0; y<aSize.getHeight(); ++y )
|
|
{
|
|
sal_uInt8* pOutBits=pAlphaBits.get()+y*nScanWidth;
|
|
for( sal_Int32 x=0; x<aSize.getWidth(); ++x )
|
|
{
|
|
*pOutBits++ = *pInBits;
|
|
pInBits += 4;
|
|
}
|
|
}
|
|
|
|
pGDIPlusBitmap->UnlockBits( &aBmpData );
|
|
|
|
// set bits to newly create HBITMAP
|
|
SetDIBits( hScreenDC, hBmpBitmap, 0,
|
|
aSize.getHeight(), pAlphaBits.get(),
|
|
reinterpret_cast<PBITMAPINFO>(&aDIB), DIB_RGB_COLORS );
|
|
|
|
uno::Sequence< uno::Any > args{ uno::Any(reinterpret_cast<sal_Int64>(hBmpBitmap)) };
|
|
aRes <<= args;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return aRes;
|
|
}
|
|
|
|
OUString SAL_CALL CanvasBitmap::getImplementationName( )
|
|
{
|
|
return "DXCanvas.CanvasBitmap";
|
|
}
|
|
|
|
sal_Bool SAL_CALL CanvasBitmap::supportsService( const OUString& ServiceName )
|
|
{
|
|
return cppu::supportsService( this, ServiceName );
|
|
}
|
|
|
|
uno::Sequence< OUString > SAL_CALL CanvasBitmap::getSupportedServiceNames( )
|
|
{
|
|
return { "com.sun.star.rendering.CanvasBitmap" };
|
|
}
|
|
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|