Related: tdf#146842 Eliminate temporary copies of SkiaSalBitmap when printing

Commit 9eb732a320 fixed crashing when
printing SkiaSalBitmaps to a non-Skia SalGraphics. However, the fix
almost always makes two copies of the SkiaSalBitmap's bitmap data: the
first copy is made in SkiaSalBitmap::AcquireBuffer() and then
QuartzSalBitmap makes a copy of the first copy.

By making QuartzSalBitmap's methods that return a CGImageRef pure
virtual, a non-Skia SalGraphics can now create a CGImageRef directly
from a SkiaSalBitmap's Skia bitmap data without copying to any
intermediate buffers.

Change-Id: If6ab7f175889cb4839d8a2461b7be7671e575c08
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144856
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
This commit is contained in:
Patrick Luby
2022-12-29 14:14:36 +00:00
committed by Noel Grandin
parent aef2ee893c
commit b99464ef2f
11 changed files with 323 additions and 292 deletions
+12
View File
@@ -684,6 +684,12 @@ ifeq ($(OS),iOS)
$(eval $(call gb_Library_add_cxxflags,vcl,\
$(gb_OBJCXXFLAGS) \
))
$(eval $(call gb_Library_add_objcxxobjects,vcl,\
vcl/quartz/cgutils \
$(if $(filter SKIA,$(BUILD_TYPE)), \
vcl/skia/quartz/salbmp \
) \
))
$(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/ios/iosinst \
vcl/ios/dummies \
@@ -709,6 +715,12 @@ endif
ifeq ($(OS),MACOSX)
$(eval $(call gb_Library_add_objcxxobjects,vcl,\
vcl/quartz/cgutils \
$(if $(filter SKIA,$(BUILD_TYPE)), \
vcl/skia/quartz/salbmp \
) \
))
$(eval $(call gb_Library_use_system_darwin_frameworks,vcl,\
Cocoa \
CoreFoundation \
+34
View File
@@ -0,0 +1,34 @@
/* -*- 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 .
*/
#pragma once
#include <vcl/dllapi.h>
#include <premac.h>
#include <CoreGraphics/CoreGraphics.h>
#include <postmac.h>
class SalBitmap;
CGImageRef VCL_DLLPUBLIC CreateWithSalBitmapAndMask(const SalBitmap& rBitmap,
const SalBitmap& rMask, int nX, int nY,
int nWidth, int nHeight);
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+3 -9
View File
@@ -30,9 +30,6 @@
#include <salinst.hxx>
#include <salvd.hxx>
#include <salbmp.hxx>
#if HAVE_FEATURE_SKIA
#include <skia/salbmp.hxx>
#endif
#include <memory>
@@ -64,9 +61,6 @@ public:
bool Create( const SalBitmap& rSalBmp ) override;
bool Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics ) override;
bool Create( const SalBitmap& rSalBmp, vcl::PixelFormat eNewPixelFormat) override;
#if HAVE_FEATURE_SKIA
bool Create( const SkiaSalBitmap& rSkiaSalBmp, const SalTwoRect& rPosAry );
#endif
virtual bool Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& rBitmapCanvas,
Size& rSize,
bool bMask = false ) override;
@@ -99,9 +93,9 @@ public:
bool Create(CGLayerHolder const & rLayerHolder, int nBitCount, int nX, int nY, int nWidth, int nHeight, bool bFlipped);
public:
CGImageRef CreateWithMask( const QuartzSalBitmap& rMask, int nX, int nY, int nWidth, int nHeight ) const;
CGImageRef CreateColorMask( int nX, int nY, int nWidth, int nHeight, Color nMaskColor ) const;
CGImageRef CreateCroppedImage( int nX, int nY, int nWidth, int nHeight ) const;
virtual CGImageRef CreateWithMask( const SalBitmap& rMask, int nX, int nY, int nWidth, int nHeight ) const override;
virtual CGImageRef CreateColorMask( int nX, int nY, int nWidth, int nHeight, Color nMaskColor ) const override;
virtual CGImageRef CreateCroppedImage( int nX, int nY, int nWidth, int nHeight ) const override;
void doDestroy();
};
+23
View File
@@ -29,6 +29,12 @@
#include <com/sun/star/rendering/XBitmapCanvas.hpp>
#include <basegfx/utils/systemdependentdata.hxx>
#if defined MACOSX || defined IOS
#include <premac.h>
#include <CoreGraphics/CoreGraphics.h>
#include <postmac.h>
#endif
struct BitmapBuffer;
class Color;
class SalGraphics;
@@ -98,6 +104,23 @@ public:
return false;
}
#if defined MACOSX || defined IOS
// Related: tdf#146842 Eliminate temporary copies of SkiaSalBitmap when
// printing
// Commit 9eb732a32023e74c44ac8c3b5af9f5424273bb6c fixed crashing when
// printing SkiaSalBitmaps to a non-Skia SalGraphics. However, the fix
// almost always makes two copies of the SkiaSalBitmap's bitmap data: the
// first copy is made in SkiaSalBitmap::AcquireBuffer() and then
// QuartzSalBitmap makes a copy of the first copy.
// By making QuartzSalBitmap's methods that return a CGImageRef pure
// virtual, a non-Skia SalGraphics can now create a CGImageRef directly
// from a SkiaSalBitmap's Skia bitmap data without copying to any
// intermediate buffers.
virtual CGImageRef CreateWithMask( const SalBitmap& rMask, int nX, int nY, int nWidth, int nHeight ) const = 0;
virtual CGImageRef CreateColorMask( int nX, int nY, int nWidth, int nHeight, Color nMaskColor ) const = 0;
virtual CGImageRef CreateCroppedImage( int nX, int nY, int nWidth, int nHeight ) const = 0;
#endif
BitmapChecksum GetChecksum() const
{
updateChecksum();
+40
View File
@@ -0,0 +1,40 @@
/* -*- 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 .
*/
#pragma once
constexpr uint32_t SkiaToCGBitmapType(SkColorType color, SkAlphaType alpha)
{
if (alpha == kPremul_SkAlphaType)
{
return color == kBGRA_8888_SkColorType
? (uint32_t(kCGImageAlphaPremultipliedFirst)
| uint32_t(kCGBitmapByteOrder32Little))
: (uint32_t(kCGImageAlphaPremultipliedLast) | uint32_t(kCGBitmapByteOrder32Big));
}
else
{
assert(alpha == kOpaque_SkAlphaType);
return color == kBGRA_8888_SkColorType
? (uint32_t(kCGImageAlphaNoneSkipFirst) | uint32_t(kCGBitmapByteOrder32Little))
: (uint32_t(kCGImageAlphaNoneSkipLast) | uint32_t(kCGBitmapByteOrder32Big));
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+7
View File
@@ -64,6 +64,13 @@ public:
virtual bool ConvertToGreyscale() override;
virtual bool Erase(const Color& color) override;
virtual bool AlphaBlendWith(const SalBitmap& rSalBmp) override;
#if defined MACOSX || defined IOS
virtual CGImageRef CreateWithMask(const SalBitmap& rMask, int nX, int nY, int nWidth,
int nHeight) const override;
virtual CGImageRef CreateColorMask(int nX, int nY, int nWidth, int nHeight,
Color nMaskColor) const override;
virtual CGImageRef CreateCroppedImage(int nX, int nY, int nWidth, int nHeight) const override;
#endif
const BitmapPalette& Palette() const { return mPalette; }
+11 -158
View File
@@ -44,11 +44,6 @@
#include <svdata.hxx>
#endif
#if HAVE_FEATURE_SKIA
#include <vcl/skia/SkiaHelper.hxx>
#include <skia/salbmp.hxx>
#endif
using namespace vcl;
namespace
@@ -192,48 +187,6 @@ void drawPattern50(void*, CGContextRef rContext)
CGContextAddRects(rContext, aRects, 2);
CGContextFillPath(rContext);
}
#if HAVE_FEATURE_SKIA
// Related: tdf#146842 Convert SkiaSalBitmap to QuartzSalBitmap
// Commit de3f13e2175564316eb5a62dee65e9ff8f31b460 disabled Skia for printing.
// However, since all SalBitmaps created are either all QuartzSalBitmaps or all
// SkiaSalBitmaps, a crash occurs whenever a SkiaSalBitmap is passed to a
// printer's SalGraphics instance which is now always non-Skia.
QuartzSalBitmap* checkAndConvertToQuartzSalBitmap(const SalTwoRect& rPosAry,
const SalBitmap& rSalBitmap,
SalTwoRect* pAdjustedSrcPosAry)
{
QuartzSalBitmap* pRet = nullptr;
if (SkiaHelper::isVCLSkiaEnabled() && dynamic_cast<const SkiaSalBitmap*>(&rSalBitmap))
{
const SkiaSalBitmap& rSkiaBitmap = static_cast<const SkiaSalBitmap&>(rSalBitmap);
SalTwoRect aSrcPosAry(rPosAry);
aSrcPosAry.mnDestX = 0;
aSrcPosAry.mnDestY = 0;
pRet = new QuartzSalBitmap;
if (pRet)
{
// Ignore any failures as returning a nullptr will lead to a crash
pRet->Create(rSkiaBitmap, aSrcPosAry);
if (pAdjustedSrcPosAry)
{
pAdjustedSrcPosAry->mnSrcX = 0;
pAdjustedSrcPosAry->mnSrcY = 0;
pAdjustedSrcPosAry->mnSrcWidth = aSrcPosAry.mnDestWidth;
pAdjustedSrcPosAry->mnSrcHeight = aSrcPosAry.mnDestHeight;
}
}
}
return pRet;
}
#endif
}
AquaGraphicsBackend::AquaGraphicsBackend(AquaSharedAttributes& rShared)
@@ -949,23 +902,7 @@ void AquaGraphicsBackend::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap&
if (!mrShared.checkContext())
return;
#if HAVE_FEATURE_SKIA
if (mrShared.mbPrinter)
{
SAL_INFO("vcl.print", "Printing with Skia bitmaps: AquaGraphicsBackend::drawBitmap");
SalTwoRect aDestPosAry(rPosAry);
std::unique_ptr<QuartzSalBitmap> pSalBitmap(
checkAndConvertToQuartzSalBitmap(rPosAry, rSalBitmap, &aDestPosAry));
if (pSalBitmap)
{
drawBitmap(aDestPosAry, *pSalBitmap);
return;
}
}
#endif
const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap);
CGImageRef xImage = rBitmap.CreateCroppedImage(
CGImageRef xImage = rSalBitmap.CreateCroppedImage(
static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight));
if (!xImage)
@@ -985,36 +922,9 @@ void AquaGraphicsBackend::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap&
if (!mrShared.checkContext())
return;
#if HAVE_FEATURE_SKIA
if (mrShared.mbPrinter)
{
SAL_INFO(
"vcl.print",
"Printing with Skia bitmaps: AquaGraphicsBackend::drawBitmapWithTranspraentBitmap");
SalTwoRect aDestPosAry(rPosAry);
std::unique_ptr<QuartzSalBitmap> pSalBitmap(
checkAndConvertToQuartzSalBitmap(rPosAry, rSalBitmap, &aDestPosAry));
if (pSalBitmap)
{
drawBitmap(aDestPosAry, *pSalBitmap, rTransparentBitmap);
return;
}
pSalBitmap.reset(
checkAndConvertToQuartzSalBitmap(rPosAry, rTransparentBitmap, &aDestPosAry));
if (pSalBitmap)
{
drawBitmap(aDestPosAry, rSalBitmap, *pSalBitmap);
return;
}
}
#endif
const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap);
const QuartzSalBitmap& rMask = static_cast<const QuartzSalBitmap&>(rTransparentBitmap);
CGImageRef xMaskedImage(rBitmap.CreateWithMask(rMask, rPosAry.mnSrcX, rPosAry.mnSrcY,
rPosAry.mnSrcWidth, rPosAry.mnSrcHeight));
CGImageRef xMaskedImage(rSalBitmap.CreateWithMask(rTransparentBitmap, rPosAry.mnSrcX,
rPosAry.mnSrcY, rPosAry.mnSrcWidth,
rPosAry.mnSrcHeight));
if (!xMaskedImage)
return;
@@ -1031,24 +941,8 @@ void AquaGraphicsBackend::drawMask(const SalTwoRect& rPosAry, const SalBitmap& r
if (!mrShared.checkContext())
return;
#if HAVE_FEATURE_SKIA
if (mrShared.mbPrinter)
{
SAL_INFO("vcl.print", "Printing with Skia bitmaps: AquaGraphicsBackend::drawMask");
SalTwoRect aDestPosAry(rPosAry);
std::unique_ptr<QuartzSalBitmap> pSalBitmap(
checkAndConvertToQuartzSalBitmap(rPosAry, rSalBitmap, &aDestPosAry));
if (pSalBitmap)
{
drawMask(aDestPosAry, *pSalBitmap, nMaskColor);
return;
}
}
#endif
const QuartzSalBitmap& rBitmap = static_cast<const QuartzSalBitmap&>(rSalBitmap);
CGImageRef xImage = rBitmap.CreateColorMask(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth,
rPosAry.mnSrcHeight, nMaskColor);
CGImageRef xImage = rSalBitmap.CreateColorMask(
rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, nMaskColor);
if (!xImage)
return;
@@ -1324,28 +1218,7 @@ bool AquaGraphicsBackend::drawAlphaBitmap(const SalTwoRect& rTR, const SalBitmap
if (rTR.mnDestWidth < 0 || rTR.mnDestHeight < 0)
return false;
#if HAVE_FEATURE_SKIA
if (!mrShared.checkContext())
return false;
if (mrShared.mbPrinter)
{
SAL_INFO("vcl.print", "Printing with Skia bitmaps: AquaGraphicsBackend::drawAlphaBitmap");
SalTwoRect aDestPosAry(rTR);
std::unique_ptr<QuartzSalBitmap> pSalBitmap(
checkAndConvertToQuartzSalBitmap(rTR, rSrcBitmap, &aDestPosAry));
if (pSalBitmap)
return drawAlphaBitmap(aDestPosAry, *pSalBitmap, rAlphaBmp);
pSalBitmap.reset(checkAndConvertToQuartzSalBitmap(rTR, rAlphaBmp, &aDestPosAry));
if (pSalBitmap)
return drawAlphaBitmap(aDestPosAry, rSrcBitmap, *pSalBitmap);
}
#endif
const QuartzSalBitmap& rSrcSalBmp = static_cast<const QuartzSalBitmap&>(rSrcBitmap);
const QuartzSalBitmap& rMaskSalBmp = static_cast<const QuartzSalBitmap&>(rAlphaBmp);
CGImageRef xMaskedImage = rSrcSalBmp.CreateWithMask(rMaskSalBmp, rTR.mnSrcX, rTR.mnSrcY,
CGImageRef xMaskedImage = rSrcBitmap.CreateWithMask(rAlphaBmp, rTR.mnSrcX, rTR.mnSrcY,
rTR.mnSrcWidth, rTR.mnSrcHeight);
if (!xMaskedImage)
return false;
@@ -1375,35 +1248,15 @@ bool AquaGraphicsBackend::drawTransformedBitmap(const basegfx::B2DPoint& rNull,
if (fAlpha != 1.0)
return false;
#if HAVE_FEATURE_SKIA
if (mrShared.mbPrinter)
{
SAL_INFO("vcl.print",
"Printing with Skia bitmaps: AquaGraphicsBackend::drawTransformedBitmap");
const Size aSize = rSrcBitmap.GetSize();
SalTwoRect aTR(0, 0, aSize.Width(), aSize.Height(), 0, 0, aSize.Width(), aSize.Height());
std::unique_ptr<QuartzSalBitmap> pSalBitmap(
checkAndConvertToQuartzSalBitmap(aTR, rSrcBitmap, nullptr));
if (pSalBitmap)
return drawTransformedBitmap(rNull, rX, rY, *pSalBitmap, pAlphaBmp, fAlpha);
pSalBitmap.reset(checkAndConvertToQuartzSalBitmap(aTR, *pAlphaBmp, nullptr));
if (pSalBitmap)
return drawTransformedBitmap(rNull, rX, rY, rSrcBitmap, pSalBitmap.get(), fAlpha);
}
#endif
// get the Quartz image
CGImageRef xImage = nullptr;
const Size aSize = rSrcBitmap.GetSize();
const QuartzSalBitmap& rSrcSalBmp = static_cast<const QuartzSalBitmap&>(rSrcBitmap);
const QuartzSalBitmap* pMaskSalBmp = static_cast<const QuartzSalBitmap*>(pAlphaBmp);
if (!pMaskSalBmp)
xImage = rSrcSalBmp.CreateCroppedImage(0, 0, int(aSize.Width()), int(aSize.Height()));
if (!pAlphaBmp)
xImage = rSrcBitmap.CreateCroppedImage(0, 0, int(aSize.Width()), int(aSize.Height()));
else
xImage = rSrcSalBmp.CreateWithMask(*pMaskSalBmp, 0, 0, int(aSize.Width()),
int(aSize.Height()));
xImage
= rSrcBitmap.CreateWithMask(*pAlphaBmp, 0, 0, int(aSize.Width()), int(aSize.Height()));
if (!xImage)
return false;
+73
View File
@@ -0,0 +1,73 @@
/* -*- 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 <quartz/cgutils.h>
#include <salbmp.hxx>
#include <osx/saldata.hxx>
static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/)
{
std::free( const_cast<void*>(data) );
}
CGImageRef CreateWithSalBitmapAndMask( const SalBitmap& rBitmap, const SalBitmap& rMask, int nX, int nY, int nWidth, int nHeight )
{
CGImageRef xImage( rBitmap.CreateCroppedImage( nX, nY, nWidth, nHeight ) );
if( !xImage )
return nullptr;
CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight );
if( !xMask )
return xImage;
// CGImageCreateWithMask() only likes masks or greyscale images => convert if needed
// TODO: isolate in an extra method?
if( !CGImageIsMask(xMask) || rMask.GetBitCount() != 8)//(CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) )
{
const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect has no offset
// create the alpha mask image fitting our image
// TODO: is caching the full mask or the subimage mask worth it?
int nMaskBytesPerRow = ((nWidth + 3) & ~3);
void* pMaskMem = std::malloc( nMaskBytesPerRow * nHeight );
CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem,
nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone );
CGContextDrawImage( xMaskContext, xImageRect, xMask );
CFRelease( xMask );
CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( nullptr,
pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) );
static const CGFloat* pDecode = nullptr;
xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, xDataProvider, pDecode, false );
CFRelease( xDataProvider );
CFRelease( xMaskContext );
}
if( !xMask )
return xImage;
// combine image and alpha mask
CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask );
CFRelease( xMask );
CFRelease( xImage );
return xMaskedImage;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+10 -105
View File
@@ -35,6 +35,7 @@
#include <vcl/Scanline.hxx>
#include <bitmap/bmpfast.hxx>
#include <quartz/cgutils.h>
#include <quartz/salbmp.h>
#include <quartz/utils.h>
#include <bitmap/ScanlineTools.hxx>
@@ -115,66 +116,6 @@ bool QuartzSalBitmap::Create( const SalBitmap& rSalBmp, vcl::PixelFormat eNewPix
return false;
}
#if HAVE_FEATURE_SKIA
bool QuartzSalBitmap::Create( const SkiaSalBitmap& rSalBmp, const SalTwoRect& rPosAry )
{
bool bRet = false;
// Ugly but necessary to acquire the bitmap buffer because all of the
// SalBitmap instances that callers pass are already const. At least we
// only need to read, not write to the bitmap parameter.
SkiaSalBitmap& rSkiaSalBmp = const_cast<SkiaSalBitmap&>( rSalBmp );
BitmapBuffer *pSrcBuffer = rSkiaSalBmp.AcquireBuffer( BitmapAccessMode::Read );
if ( !pSrcBuffer )
return bRet;
if ( !pSrcBuffer->mpBits )
{
rSkiaSalBmp.ReleaseBuffer( pSrcBuffer, BitmapAccessMode::Read );
return bRet;
}
// Create only a 1 pixel buffer as it will always be discarded
mnBits = 32;
mnWidth = 1;
mnHeight = 1;
if( AllocateUserData() )
{
BitmapBuffer *pDestBuffer = AcquireBuffer( BitmapAccessMode::Read );
if ( pDestBuffer )
{
std::unique_ptr<BitmapBuffer> pConvertedBuffer = StretchAndConvert( *pSrcBuffer, rPosAry, pDestBuffer->mnFormat, pDestBuffer->maPalette, &pDestBuffer->maColorMask );
bool bUseDestBuffer = ( pConvertedBuffer &&
pConvertedBuffer->mpBits &&
pConvertedBuffer->mnFormat == pDestBuffer->mnFormat &&
pConvertedBuffer->mnWidth == rPosAry.mnDestWidth &&
pConvertedBuffer->mnHeight == rPosAry.mnDestHeight );
ReleaseBuffer( pDestBuffer, BitmapAccessMode::Read );
if ( bUseDestBuffer )
{
// Surprisingly, BitmapBuffer does not delete the bits so
// discard our 1 pixel buffer and take ownership of the bits
DestroyContext();
m_pUserBuffer.reset( pConvertedBuffer->mpBits );
mnWidth = pConvertedBuffer->mnWidth;
mnHeight = pConvertedBuffer->mnHeight;
mnBytesPerRow = pConvertedBuffer->mnScanlineSize;
bRet = true;
}
}
}
rSkiaSalBmp.ReleaseBuffer( pSrcBuffer, BitmapAccessMode::Read );
return bRet;
}
#endif
bool QuartzSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/,
Size& /*rSize*/, bool /*bMask*/ )
{
@@ -576,48 +517,10 @@ static void CFRTLFree(void* /*info*/, const void* data, size_t /*size*/)
std::free( const_cast<void*>(data) );
}
CGImageRef QuartzSalBitmap::CreateWithMask( const QuartzSalBitmap& rMask,
CGImageRef QuartzSalBitmap::CreateWithMask( const SalBitmap& rMask,
int nX, int nY, int nWidth, int nHeight ) const
{
CGImageRef xImage( CreateCroppedImage( nX, nY, nWidth, nHeight ) );
if( !xImage )
return nullptr;
CGImageRef xMask = rMask.CreateCroppedImage( nX, nY, nWidth, nHeight );
if( !xMask )
return xImage;
// CGImageCreateWithMask() only likes masks or greyscale images => convert if needed
// TODO: isolate in an extra method?
if( !CGImageIsMask(xMask) || rMask.GetBitCount() != 8)//(CGImageGetColorSpace(xMask) != GetSalData()->mxGraySpace) )
{
const CGRect xImageRect=CGRectMake( 0, 0, nWidth, nHeight );//the rect has no offset
// create the alpha mask image fitting our image
// TODO: is caching the full mask or the subimage mask worth it?
int nMaskBytesPerRow = ((nWidth + 3) & ~3);
void* pMaskMem = std::malloc( nMaskBytesPerRow * nHeight );
CGContextRef xMaskContext = CGBitmapContextCreate( pMaskMem,
nWidth, nHeight, 8, nMaskBytesPerRow, GetSalData()->mxGraySpace, kCGImageAlphaNone );
CGContextDrawImage( xMaskContext, xImageRect, xMask );
CFRelease( xMask );
CGDataProviderRef xDataProvider( CGDataProviderCreateWithData( nullptr,
pMaskMem, nHeight * nMaskBytesPerRow, &CFRTLFree ) );
static const CGFloat* pDecode = nullptr;
xMask = CGImageMaskCreate( nWidth, nHeight, 8, 8, nMaskBytesPerRow, xDataProvider, pDecode, false );
CFRelease( xDataProvider );
CFRelease( xMaskContext );
}
if( !xMask )
return xImage;
// combine image and alpha mask
CGImageRef xMaskedImage = CGImageCreateWithMask( xImage, xMask );
CFRelease( xMask );
CFRelease( xImage );
return xMaskedImage;
return CreateWithSalBitmapAndMask( *this, rMask, nX, nY, nWidth, nHeight );
}
/** creates an image from the given rectangle, replacing all black pixels
@@ -628,13 +531,14 @@ CGImageRef QuartzSalBitmap::CreateColorMask( int nX, int nY, int nWidth,
CGImageRef xMask = nullptr;
if (m_pUserBuffer && (nX + nWidth <= mnWidth) && (nY + nHeight <= mnHeight))
{
auto pSourcePixels = vcl::bitmap::getScanlineTransformer(mnBits, maPalette);
// Don't allocate destination buffer if there is no scanline transformer
if( !pSourcePixels )
return xMask;
const sal_uInt32 nDestBytesPerRow = nWidth << 2;
std::unique_ptr<sal_uInt32[]> pMaskBuffer(new (std::nothrow) sal_uInt32[ nHeight * nDestBytesPerRow / 4] );
sal_uInt32* pDest = pMaskBuffer.get();
auto pSourcePixels = vcl::bitmap::getScanlineTransformer(mnBits, maPalette);
if( pMaskBuffer && pSourcePixels )
if( pMaskBuffer )
{
sal_uInt32 nColor;
reinterpret_cast<sal_uInt8*>(&nColor)[0] = 0xff;
@@ -643,6 +547,7 @@ CGImageRef QuartzSalBitmap::CreateColorMask( int nX, int nY, int nWidth,
reinterpret_cast<sal_uInt8*>(&nColor)[3] = nMaskColor.GetBlue();
sal_uInt8* pSource = m_pUserBuffer.get();
sal_uInt32* pDest = pMaskBuffer.get();
// First to nY on y-axis, as that is our starting point (sub-image)
if( nY )
pSource += nY * mnBytesPerRow;
+3 -20
View File
@@ -26,6 +26,7 @@
#include <tools/sk_app/mac/WindowContextFactory_mac.h>
#include <quartz/ctfonts.hxx>
#include <skia/quartz/cgutils.h>
#include <SkBitmap.h>
#include <SkCanvas.h>
@@ -102,24 +103,6 @@ void AquaSkiaSalGraphicsImpl::flushSurfaceToWindowContext()
SkiaSalGraphicsImpl::flushSurfaceToWindowContext();
}
constexpr static uint32_t toCGBitmapType(SkColorType color, SkAlphaType alpha)
{
if (alpha == kPremul_SkAlphaType)
{
return color == kBGRA_8888_SkColorType
? (uint32_t(kCGImageAlphaPremultipliedFirst)
| uint32_t(kCGBitmapByteOrder32Little))
: (uint32_t(kCGImageAlphaPremultipliedLast) | uint32_t(kCGBitmapByteOrder32Big));
}
else
{
assert(alpha == kOpaque_SkAlphaType);
return color == kBGRA_8888_SkColorType
? (uint32_t(kCGImageAlphaNoneSkipFirst) | uint32_t(kCGBitmapByteOrder32Little))
: (uint32_t(kCGImageAlphaNoneSkipLast) | uint32_t(kCGBitmapByteOrder32Big));
}
}
// For Raster we use our own screen blitting (see above).
void AquaSkiaSalGraphicsImpl::flushSurfaceToScreenCG()
{
@@ -174,7 +157,7 @@ void AquaSkiaSalGraphicsImpl::flushSurfaceToScreenCG()
CGImageRef fullImage = CGImageCreate(pixmap.bounds().width(), pixmap.bounds().height(), 8,
8 * image->imageInfo().bytesPerPixel(), pixmap.rowBytes(),
GetSalData()->mxRGBSpace,
toCGBitmapType(image->colorType(), image->alphaType()),
SkiaToCGBitmapType(image->colorType(), image->alphaType()),
dataProvider, nullptr, false, kCGRenderingIntentDefault);
if (!fullImage)
{
@@ -247,7 +230,7 @@ bool AquaSkiaSalGraphicsImpl::drawNativeControl(ControlType nType, ControlPart n
memset(data.get(), 0, bytes);
CGContextRef context = CGBitmapContextCreate(
data.get(), width, height, 8, width * 4, GetSalData()->mxRGBSpace,
toCGBitmapType(mSurface->imageInfo().colorType(), kPremul_SkAlphaType));
SkiaToCGBitmapType(mSurface->imageInfo().colorType(), kPremul_SkAlphaType));
if (!context)
{
SAL_WARN("vcl.skia", "drawNativeControl(): Failed to allocate bitmap context");
+107
View File
@@ -0,0 +1,107 @@
/* -*- 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 <skia/salbmp.hxx>
#include <skia/zone.hxx>
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkPaint.h>
#include <quartz/cgutils.h>
#ifdef MACOSX
#include <osx/saldata.hxx>
#else
#include <svdata.hxx>
#endif
#include <skia/quartz/cgutils.h>
CGImageRef SkiaSalBitmap::CreateWithMask(const SalBitmap& rMask, int nX, int nY, int nWidth,
int nHeight) const
{
return CreateWithSalBitmapAndMask( *this, rMask, nX, nY, nWidth, nHeight );
}
/** creates an image from the given rectangle, replacing all black pixels
with nMaskColor and make all other full transparent */
CGImageRef SkiaSalBitmap::CreateColorMask(int nX, int nY, int nWidth, int nHeight,
Color nMaskColor) const
{
SkiaZone zone;
SkBitmap targetBitmap;
if (!targetBitmap.tryAllocPixels(SkImageInfo::Make(nWidth, nHeight, kN32_SkColorType, kPremul_SkAlphaType)))
return nullptr;
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc); // set as is
SkMatrix matrix; // The image is needed upside-down.
matrix.preTranslate(0, targetBitmap.height());
matrix.setConcat(matrix, SkMatrix::Scale(1, -1));
SkCanvas canvas(targetBitmap);
canvas.concat(matrix);
canvas.drawImageRect(GetSkImage(), SkRect::MakeXYWH(nX, nY, nWidth, nHeight), SkRect::MakeXYWH(0, 0, nWidth, nHeight), SkSamplingOptions(), &paint, SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
canvas.flush();
const SkColor maskColor = SkiaHelper::toSkColor(nMaskColor);
for (int y = 0; y < targetBitmap.height(); y++)
{
SkColor *pPixels = targetBitmap.getAddr32(0, y);
for (int x = 0; x < targetBitmap.width(); x++)
*pPixels++ = (*pPixels == SK_ColorBLACK ? maskColor : SK_ColorTRANSPARENT);
}
targetBitmap.setImmutable();
CGContextRef xContext = CGBitmapContextCreate(targetBitmap.getAddr32(0, 0), targetBitmap.width(), targetBitmap.height(), 8, targetBitmap.rowBytes(), GetSalData()->mxRGBSpace, SkiaToCGBitmapType(targetBitmap.colorType(), targetBitmap.alphaType()));
if (!xContext)
return nullptr;
CGImageRef xCroppedImage = CGBitmapContextCreateImage(xContext);
CGContextRelease(xContext);
return xCroppedImage;
}
CGImageRef SkiaSalBitmap::CreateCroppedImage(int nX, int nY, int nWidth, int nHeight) const
{
SkiaZone zone;
SkBitmap targetBitmap;
if (!targetBitmap.tryAllocPixels(SkImageInfo::Make(nWidth, nHeight, kN32_SkColorType, kPremul_SkAlphaType)))
return nullptr;
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc); // set as is
SkMatrix matrix; // The image is needed upside-down.
matrix.preTranslate(0, targetBitmap.height());
matrix.setConcat(matrix, SkMatrix::Scale(1, -1));
SkCanvas canvas(targetBitmap);
canvas.concat(matrix);
canvas.drawImageRect(GetSkImage(), SkRect::MakeXYWH(nX, nY, nWidth, nHeight), SkRect::MakeXYWH(0, 0, nWidth, nHeight), SkSamplingOptions(), &paint, SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
canvas.flush();
targetBitmap.setImmutable();
CGContextRef xContext = CGBitmapContextCreate(targetBitmap.getAddr32(0, 0), targetBitmap.width(), targetBitmap.height(), 8, targetBitmap.rowBytes(), GetSalData()->mxRGBSpace, SkiaToCGBitmapType(targetBitmap.colorType(), targetBitmap.alphaType()));
if (!xContext)
return nullptr;
CGImageRef xCroppedImage = CGBitmapContextCreateImage(xContext);
CGContextRelease(xContext);
return xCroppedImage;
}