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:
committed by
Noel Grandin
parent
aef2ee893c
commit
b99464ef2f
@@ -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 \
|
||||
|
||||
@@ -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: */
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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: */
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user