vcl/headless/CairoCommon.cxx | 204 +++++++++++++++++++++++++++++ vcl/headless/svpgdi.cxx | 267 ++++----------------------------------- vcl/inc/headless/CairoCommon.hxx | 26 +++ vcl/inc/headless/svpgdi.hxx | 31 ++-- 4 files changed, 276 insertions(+), 252 deletions(-)
New commits: commit 26836c3a4f7032f597b829f4d6b896294404ef8f Author: Tomaž Vajngerl <[email protected]> AuthorDate: Tue Nov 16 22:01:14 2021 +0100 Commit: Tomaž Vajngerl <[email protected]> CommitDate: Thu Dec 30 11:48:03 2021 +0100 vcl: move getting and releasing cairo context to CairoCommon The getting/creating and releasing of the cairo context is needed by the SvpGraphicBackend. Change-Id: I133b181b8a6b114e08c8acc4596ccefb88a3f514 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127707 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <[email protected]> diff --git a/vcl/headless/CairoCommon.cxx b/vcl/headless/CairoCommon.cxx index 3db6f14f8417..f4aea95a1bdd 100644 --- a/vcl/headless/CairoCommon.cxx +++ b/vcl/headless/CairoCommon.cxx @@ -18,5 +18,209 @@ */ #include <headless/CairoCommon.hxx> +#include <dlfcn.h> +#include <vcl/BitmapTools.hxx> +#include <basegfx/utils/canvastools.hxx> + +void dl_cairo_surface_set_device_scale(cairo_surface_t* surface, double x_scale, double y_scale) +{ +#ifdef ANDROID + cairo_surface_set_device_scale(surface, x_scale, y_scale); +#else + static auto func = reinterpret_cast<void (*)(cairo_surface_t*, double, double)>( + dlsym(nullptr, "cairo_surface_set_device_scale")); + if (func) + func(surface, x_scale, y_scale); +#endif +} + +void dl_cairo_surface_get_device_scale(cairo_surface_t* surface, double* x_scale, double* y_scale) +{ +#ifdef ANDROID + cairo_surface_get_device_scale(surface, x_scale, y_scale); +#else + static auto func = reinterpret_cast<void (*)(cairo_surface_t*, double*, double*)>( + dlsym(nullptr, "cairo_surface_get_device_scale")); + if (func) + func(surface, x_scale, y_scale); + else + { + if (x_scale) + *x_scale = 1.0; + if (y_scale) + *y_scale = 1.0; + } +#endif +} + +cairo_user_data_key_t* CairoCommon::getDamageKey() +{ + static cairo_user_data_key_t aDamageKey; + return &aDamageKey; +} + +cairo_t* CairoCommon::getCairoContext(bool bXorModeAllowed, bool bAntiAlias) const +{ + cairo_t* cr; + if (m_ePaintMode == PaintMode::Xor && bXorModeAllowed) + cr = createTmpCompatibleCairoContext(); + else + cr = cairo_create(m_pSurface); + cairo_set_line_width(cr, 1); + cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); + cairo_set_antialias(cr, bAntiAlias ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + // ensure no linear transformation and no PathInfo in local cairo_path_t + cairo_identity_matrix(cr); + cairo_new_path(cr); + + return cr; +} + +void CairoCommon::releaseCairoContext(cairo_t* cr, bool bXorModeAllowed, + const basegfx::B2DRange& rExtents) const +{ + const bool bXoring = (m_ePaintMode == PaintMode::Xor && bXorModeAllowed); + + if (rExtents.isEmpty()) + { + //nothing changed, return early + if (bXoring) + { + cairo_surface_t* surface = cairo_get_target(cr); + cairo_surface_destroy(surface); + } + cairo_destroy(cr); + return; + } + + basegfx::B2IRange aIntExtents(basegfx::unotools::b2ISurroundingRangeFromB2DRange(rExtents)); + sal_Int32 nExtentsLeft(aIntExtents.getMinX()), nExtentsTop(aIntExtents.getMinY()); + sal_Int32 nExtentsRight(aIntExtents.getMaxX()), nExtentsBottom(aIntExtents.getMaxY()); + sal_Int32 nWidth = m_aFrameSize.getX(); + sal_Int32 nHeight = m_aFrameSize.getY(); + nExtentsLeft = std::max<sal_Int32>(nExtentsLeft, 0); + nExtentsTop = std::max<sal_Int32>(nExtentsTop, 0); + nExtentsRight = std::min<sal_Int32>(nExtentsRight, nWidth); + nExtentsBottom = std::min<sal_Int32>(nExtentsBottom, nHeight); + + cairo_surface_t* surface = cairo_get_target(cr); + cairo_surface_flush(surface); + + //For the most part we avoid the use of XOR these days, but there + //are some edge cases where legacy stuff still supports it, so + //emulate it (slowly) here. + if (bXoring) + { + cairo_surface_t* target_surface = m_pSurface; + if (cairo_surface_get_type(target_surface) != CAIRO_SURFACE_TYPE_IMAGE) + { + //in the unlikely case we can't use m_pSurface directly, copy contents + //to another temp image surface + cairo_t* copycr = createTmpCompatibleCairoContext(); + cairo_rectangle(copycr, nExtentsLeft, nExtentsTop, nExtentsRight - nExtentsLeft, + nExtentsBottom - nExtentsTop); + cairo_set_source_surface(copycr, m_pSurface, 0, 0); + cairo_paint(copycr); + target_surface = cairo_get_target(copycr); + cairo_destroy(copycr); + } + + cairo_surface_flush(target_surface); + unsigned char* target_surface_data = cairo_image_surface_get_data(target_surface); + unsigned char* xor_surface_data = cairo_image_surface_get_data(surface); + + cairo_format_t nFormat = cairo_image_surface_get_format(target_surface); + assert(nFormat == CAIRO_FORMAT_ARGB32 + && "need to implement CAIRO_FORMAT_A1 after all here"); + sal_Int32 nStride = cairo_format_stride_for_width(nFormat, nWidth * m_fScale); + sal_Int32 nUnscaledExtentsLeft = nExtentsLeft * m_fScale; + sal_Int32 nUnscaledExtentsRight = nExtentsRight * m_fScale; + sal_Int32 nUnscaledExtentsTop = nExtentsTop * m_fScale; + sal_Int32 nUnscaledExtentsBottom = nExtentsBottom * m_fScale; + + // Handle headless size forced to (1,1) by SvpSalFrame::GetSurfaceFrameSize(). + int target_surface_width = cairo_image_surface_get_width(target_surface); + if (nUnscaledExtentsLeft > target_surface_width) + nUnscaledExtentsLeft = target_surface_width; + if (nUnscaledExtentsRight > target_surface_width) + nUnscaledExtentsRight = target_surface_width; + int target_surface_height = cairo_image_surface_get_height(target_surface); + if (nUnscaledExtentsTop > target_surface_height) + nUnscaledExtentsTop = target_surface_height; + if (nUnscaledExtentsBottom > target_surface_height) + nUnscaledExtentsBottom = target_surface_height; + + vcl::bitmap::lookup_table const& unpremultiply_table + = vcl::bitmap::get_unpremultiply_table(); + vcl::bitmap::lookup_table const& premultiply_table = vcl::bitmap::get_premultiply_table(); + for (sal_Int32 y = nUnscaledExtentsTop; y < nUnscaledExtentsBottom; ++y) + { + unsigned char* true_row = target_surface_data + (nStride * y); + unsigned char* xor_row = xor_surface_data + (nStride * y); + unsigned char* true_data = true_row + (nUnscaledExtentsLeft * 4); + unsigned char* xor_data = xor_row + (nUnscaledExtentsLeft * 4); + for (sal_Int32 x = nUnscaledExtentsLeft; x < nUnscaledExtentsRight; ++x) + { + sal_uInt8 a = true_data[SVP_CAIRO_ALPHA]; + sal_uInt8 xor_a = xor_data[SVP_CAIRO_ALPHA]; + sal_uInt8 b = unpremultiply_table[a][true_data[SVP_CAIRO_BLUE]] + ^ unpremultiply_table[xor_a][xor_data[SVP_CAIRO_BLUE]]; + sal_uInt8 g = unpremultiply_table[a][true_data[SVP_CAIRO_GREEN]] + ^ unpremultiply_table[xor_a][xor_data[SVP_CAIRO_GREEN]]; + sal_uInt8 r = unpremultiply_table[a][true_data[SVP_CAIRO_RED]] + ^ unpremultiply_table[xor_a][xor_data[SVP_CAIRO_RED]]; + true_data[SVP_CAIRO_BLUE] = premultiply_table[a][b]; + true_data[SVP_CAIRO_GREEN] = premultiply_table[a][g]; + true_data[SVP_CAIRO_RED] = premultiply_table[a][r]; + true_data += 4; + xor_data += 4; + } + } + cairo_surface_mark_dirty(target_surface); + + if (target_surface != m_pSurface) + { + cairo_t* copycr = cairo_create(m_pSurface); + //unlikely case we couldn't use m_pSurface directly, copy contents + //back from image surface + cairo_rectangle(copycr, nExtentsLeft, nExtentsTop, nExtentsRight - nExtentsLeft, + nExtentsBottom - nExtentsTop); + cairo_set_source_surface(copycr, target_surface, 0, 0); + cairo_paint(copycr); + cairo_destroy(copycr); + cairo_surface_destroy(target_surface); + } + + cairo_surface_destroy(surface); + } + + cairo_destroy(cr); // unref + + DamageHandler* pDamage + = static_cast<DamageHandler*>(cairo_surface_get_user_data(m_pSurface, getDamageKey())); + + if (pDamage) + { + pDamage->damaged(pDamage->handle, nExtentsLeft, nExtentsTop, nExtentsRight - nExtentsLeft, + nExtentsBottom - nExtentsTop); + } +} + +cairo_t* CairoCommon::createTmpCompatibleCairoContext() const +{ +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) + cairo_surface_t* target = cairo_surface_create_similar_image( + m_pSurface, +#else + cairo_surface_t* target = cairo_image_surface_create( +#endif + CAIRO_FORMAT_ARGB32, m_aFrameSize.getX() * m_fScale, m_aFrameSize.getY() * m_fScale); + + dl_cairo_surface_set_device_scale(target, m_fScale, m_fScale); + + return cairo_create(target); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx index af105897efae..b119a0b749e4 100644 --- a/vcl/headless/svpgdi.cxx +++ b/vcl/headless/svpgdi.cxx @@ -703,7 +703,7 @@ bool SvpSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR, const SalBitmap& rS return false; } - cairo_t* cr = getCairoContext(false); + cairo_t* cr = m_aCairoCommon.getCairoContext(false, getAntiAlias()); clipRegion(cr); cairo_rectangle(cr, rTR.mnDestX, rTR.mnDestY, rTR.mnDestWidth, rTR.mnDestHeight); @@ -739,7 +739,7 @@ bool SvpSalGraphics::drawAlphaBitmap( const SalTwoRect& rTR, const SalBitmap& rS cairo_pattern_destroy(maskpattern); - releaseCairoContext(cr, false, extents); + m_aCairoCommon.releaseCairoContext(cr, false, extents); return true; } @@ -800,7 +800,7 @@ bool SvpSalGraphics::drawTransformedBitmap( } const Size aSize = rSourceBitmap.GetSize(); - cairo_t* cr = getCairoContext(false); + cairo_t* cr = m_aCairoCommon.getCairoContext(false, getAntiAlias()); clipRegion(cr); // setup the image transformation @@ -825,7 +825,7 @@ bool SvpSalGraphics::drawTransformedBitmap( else cairo_paint(cr); - releaseCairoContext(cr, false, extents); + m_aCairoCommon.releaseCairoContext(cr, false, extents); return true; } @@ -867,7 +867,7 @@ bool SvpSalGraphics::drawAlphaRect(tools::Long nX, tools::Long nY, tools::Long n return true; } - cairo_t* cr = getCairoContext(false); + cairo_t* cr = m_aCairoCommon.getCairoContext(false, getAntiAlias()); clipRegion(cr); const double fTransparency = nTransparency * (1.0/100); @@ -907,14 +907,13 @@ bool SvpSalGraphics::drawAlphaRect(tools::Long nX, tools::Long nY, tools::Long n cairo_stroke(cr); } - releaseCairoContext(cr, false, extents); + m_aCairoCommon.releaseCairoContext(cr, false, extents); return true; } SvpSalGraphics::SvpSalGraphics() - : m_fScale(1.0) - , m_aTextRenderImpl(*this) + : m_aTextRenderImpl(*this) , m_pBackend(new SvpGraphicsBackend(m_aCairoCommon)) { bool bLOKActive = comphelper::LibreOfficeKit::isActive(); @@ -930,7 +929,7 @@ void SvpSalGraphics::setSurface(cairo_surface_t* pSurface, const basegfx::B2IVec { m_aCairoCommon.m_pSurface = pSurface; m_aCairoCommon.m_aFrameSize = rSize; - dl_cairo_surface_get_device_scale(pSurface, &m_fScale, nullptr); + dl_cairo_surface_get_device_scale(pSurface, &m_aCairoCommon.m_fScale, nullptr); ResetClipRegion(); } @@ -949,7 +948,7 @@ void SvpSalGraphics::drawPixel( tools::Long nX, tools::Long nY ) void SvpSalGraphics::drawPixel( tools::Long nX, tools::Long nY, Color aColor ) { - cairo_t* cr = getCairoContext(true); + cairo_t* cr = m_aCairoCommon.getCairoContext(true, getAntiAlias()); clipRegion(cr); cairo_rectangle(cr, nX, nY, 1, 1); @@ -957,7 +956,7 @@ void SvpSalGraphics::drawPixel( tools::Long nX, tools::Long nY, Color aColor ) cairo_fill(cr); basegfx::B2DRange extents = getClippedFillDamage(cr); - releaseCairoContext(cr, true, extents); + m_aCairoCommon.releaseCairoContext(cr, true, extents); } void SvpSalGraphics::drawRect( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) @@ -1257,7 +1256,7 @@ void SvpSalGraphics::drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2 aPoly.append(basegfx::B2DPoint(nX1, nY1)); aPoly.append(basegfx::B2DPoint(nX2, nY2)); - cairo_t* cr = getCairoContext(false); + cairo_t* cr = m_aCairoCommon.getCairoContext(false, getAntiAlias()); clipRegion(cr); // PixelOffset used: Set PixelOffset as linear transformation @@ -1279,7 +1278,7 @@ void SvpSalGraphics::drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2 cairo_stroke(cr); - releaseCairoContext(cr, false, extents); + m_aCairoCommon.releaseCairoContext(cr, false, extents); } namespace { @@ -1393,7 +1392,7 @@ bool SvpSalGraphics::drawPolyLine( // This is mainly about extended handling of extents // and the way destruction of CairoContext is handled // due to current XOR stuff - cairo_t* cr = getCairoContext(false); + cairo_t* cr = m_aCairoCommon.getCairoContext(false, getAntiAlias()); basegfx::B2DRange aExtents; clipRegion(cr); @@ -1413,7 +1412,7 @@ bool SvpSalGraphics::drawPolyLine( fMiterMinimumAngle, bPixelSnapHairline)); - releaseCairoContext(cr, false, aExtents); + m_aCairoCommon.releaseCairoContext(cr, false, aExtents); return bRetval; } @@ -1768,7 +1767,7 @@ bool SvpSalGraphics::drawPolyPolygon( return true; } - cairo_t* cr = getCairoContext(true); + cairo_t* cr = m_aCairoCommon.getCairoContext(true, getAntiAlias()); clipRegion(cr); // Set full (Object-to-Device) transformation - if used @@ -1823,7 +1822,7 @@ bool SvpSalGraphics::drawPolyPolygon( // if transformation has been applied, transform also extents (ranges) // of damage so they can be correctly redrawn extents.transform(rObjectToDevice); - releaseCairoContext(cr, true, extents); + m_aCairoCommon.releaseCairoContext(cr, true, extents); return true; } @@ -1836,7 +1835,7 @@ bool SvpSalGraphics::drawGradient(const tools::PolyPolygon& rPolyPolygon, const if (rGradient.GetSteps() != 0) return false; // We can't tell cairo how many colors to use in the gradient. - cairo_t* cr = getCairoContext(true); + cairo_t* cr = m_aCairoCommon.getCairoContext(true, getAntiAlias()); clipRegion(cr); tools::Rectangle aInputRect(rPolyPolygon.GetBoundRect()); @@ -1903,14 +1902,14 @@ bool SvpSalGraphics::drawGradient(const tools::PolyPolygon& rPolyPolygon, const basegfx::B2DRange extents = getClippedFillDamage(cr); cairo_fill_preserve(cr); - releaseCairoContext(cr, true, extents); + m_aCairoCommon.releaseCairoContext(cr, true, extents); return true; } bool SvpSalGraphics::implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolygon, SalGradient const & rGradient) { - cairo_t* cr = getCairoContext(true); + cairo_t* cr = m_aCairoCommon.getCairoContext(true, getAntiAlias()); clipRegion(cr); basegfx::B2DHomMatrix rObjectToDevice; @@ -1936,7 +1935,7 @@ bool SvpSalGraphics::implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolyg basegfx::B2DRange extents = getClippedFillDamage(cr); cairo_fill_preserve(cr); - releaseCairoContext(cr, true, extents); + m_aCairoCommon.releaseCairoContext(cr, true, extents); return true; } @@ -2012,12 +2011,12 @@ static basegfx::B2DRange renderSource(cairo_t* cr, const SalTwoRect& rTR, void SvpSalGraphics::copyWithOperator( const SalTwoRect& rTR, cairo_surface_t* source, cairo_operator_t eOp ) { - cairo_t* cr = getCairoContext(false); + cairo_t* cr = m_aCairoCommon.getCairoContext(false, getAntiAlias()); clipRegion(cr); basegfx::B2DRange extents = renderWithOperator(cr, rTR, source, eOp); - releaseCairoContext(cr, false, extents); + m_aCairoCommon.releaseCairoContext(cr, false, extents); } void SvpSalGraphics::copySource( const SalTwoRect& rTR, cairo_surface_t* source ) @@ -2041,9 +2040,9 @@ void SvpSalGraphics::copyBits( const SalTwoRect& rTR, //self copy is a problem, so dup source in that case pCopy = cairo_surface_create_similar(source, cairo_surface_get_content(m_aCairoCommon.m_pSurface), - aTR.mnSrcWidth * m_fScale, - aTR.mnSrcHeight * m_fScale); - dl_cairo_surface_set_device_scale(pCopy, m_fScale, m_fScale); + aTR.mnSrcWidth * m_aCairoCommon.m_fScale, + aTR.mnSrcHeight * m_aCairoCommon.m_fScale); + dl_cairo_surface_set_device_scale(pCopy, m_aCairoCommon.m_fScale, m_aCairoCommon.m_fScale); cairo_t* cr = cairo_create(pCopy); cairo_set_source_surface(cr, source, -aTR.mnSrcX, -aTR.mnSrcY); cairo_rectangle(cr, 0, 0, aTR.mnSrcWidth, aTR.mnSrcHeight); @@ -2147,7 +2146,7 @@ void SvpSalGraphics::drawMask( const SalTwoRect& rTR, } aSurface.mark_dirty(); - cairo_t* cr = getCairoContext(false); + cairo_t* cr = m_aCairoCommon.getCairoContext(false, getAntiAlias()); clipRegion(cr); cairo_rectangle(cr, rTR.mnDestX, rTR.mnDestY, rTR.mnDestWidth, rTR.mnDestHeight); @@ -2169,7 +2168,7 @@ void SvpSalGraphics::drawMask( const SalTwoRect& rTR, } cairo_paint(cr); - releaseCairoContext(cr, false, extents); + m_aCairoCommon.releaseCairoContext(cr, false, extents); } std::shared_ptr<SalBitmap> SvpSalGraphics::getBitmap( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) @@ -2261,7 +2260,7 @@ namespace void SvpSalGraphics::invert(const basegfx::B2DPolygon &rPoly, SalInvert nFlags) { - cairo_t* cr = getCairoContext(false); + cairo_t* cr = m_aCairoCommon.getCairoContext(false, getAntiAlias()); clipRegion(cr); // To make releaseCairoContext work, use empty extents @@ -2313,10 +2312,10 @@ void SvpSalGraphics::invert(const basegfx::B2DPolygon &rPoly, SalInvert nFlags) cairo_pattern_t *pattern = create_stipple(); cairo_surface_t* surface = cairo_surface_create_similar(m_aCairoCommon.m_pSurface, cairo_surface_get_content(m_aCairoCommon.m_pSurface), - extents.getWidth() * m_fScale, - extents.getHeight() * m_fScale); + extents.getWidth() * m_aCairoCommon.m_fScale, + extents.getHeight() * m_aCairoCommon.m_fScale); - dl_cairo_surface_set_device_scale(surface, m_fScale, m_fScale); + dl_cairo_surface_set_device_scale(surface, m_aCairoCommon.m_fScale, m_aCairoCommon.m_fScale); cairo_t* stipple_cr = cairo_create(surface); cairo_set_source_rgb(stipple_cr, 1.0, 1.0, 1.0); cairo_mask(stipple_cr, pattern); @@ -2331,7 +2330,7 @@ void SvpSalGraphics::invert(const basegfx::B2DPolygon &rPoly, SalInvert nFlags) } } - releaseCairoContext(cr, false, extents); + m_aCairoCommon.releaseCairoContext(cr, false, extents); } void SvpSalGraphics::invert( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, SalInvert nFlags ) @@ -2396,177 +2395,6 @@ cairo_surface_t* SvpSalGraphics::createCairoSurface(const BitmapBuffer *pBuffer) return target; } -cairo_t* SvpSalGraphics::createTmpCompatibleCairoContext() const -{ -#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 12, 0) - cairo_surface_t *target = cairo_surface_create_similar_image(m_aCairoCommon.m_pSurface, -#else - cairo_surface_t *target = cairo_image_surface_create( -#endif - CAIRO_FORMAT_ARGB32, - m_aCairoCommon.m_aFrameSize.getX() * m_fScale, - m_aCairoCommon.m_aFrameSize.getY() * m_fScale); - - dl_cairo_surface_set_device_scale(target, m_fScale, m_fScale); - - return cairo_create(target); -} - -cairo_t* SvpSalGraphics::getCairoContext(bool bXorModeAllowed) const -{ - cairo_t* cr; - if (m_aCairoCommon.m_ePaintMode == PaintMode::Xor && bXorModeAllowed) - cr = createTmpCompatibleCairoContext(); - else - cr = cairo_create(m_aCairoCommon.m_pSurface); - cairo_set_line_width(cr, 1); - cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD); - cairo_set_antialias(cr, getAntiAlias() ? CAIRO_ANTIALIAS_DEFAULT : CAIRO_ANTIALIAS_NONE); - cairo_set_operator(cr, CAIRO_OPERATOR_OVER); - - // ensure no linear transformation and no PathInfo in local cairo_path_t - cairo_identity_matrix(cr); - cairo_new_path(cr); - - return cr; -} - -cairo_user_data_key_t* SvpSalGraphics::getDamageKey() -{ - static cairo_user_data_key_t aDamageKey; - return &aDamageKey; -} - -void SvpSalGraphics::releaseCairoContext(cairo_t* cr, bool bXorModeAllowed, const basegfx::B2DRange& rExtents) const -{ - const bool bXoring = (m_aCairoCommon.m_ePaintMode == PaintMode::Xor && bXorModeAllowed); - - if (rExtents.isEmpty()) - { - //nothing changed, return early - if (bXoring) - { - cairo_surface_t* surface = cairo_get_target(cr); - cairo_surface_destroy(surface); - } - cairo_destroy(cr); - return; - } - - basegfx::B2IRange aIntExtents(basegfx::unotools::b2ISurroundingRangeFromB2DRange(rExtents)); - sal_Int32 nExtentsLeft(aIntExtents.getMinX()), nExtentsTop(aIntExtents.getMinY()); - sal_Int32 nExtentsRight(aIntExtents.getMaxX()), nExtentsBottom(aIntExtents.getMaxY()); - sal_Int32 nWidth = m_aCairoCommon.m_aFrameSize.getX(); - sal_Int32 nHeight = m_aCairoCommon.m_aFrameSize.getY(); - nExtentsLeft = std::max<sal_Int32>(nExtentsLeft, 0); - nExtentsTop = std::max<sal_Int32>(nExtentsTop, 0); - nExtentsRight = std::min<sal_Int32>(nExtentsRight, nWidth); - nExtentsBottom = std::min<sal_Int32>(nExtentsBottom, nHeight); - - cairo_surface_t* surface = cairo_get_target(cr); - cairo_surface_flush(surface); - - //For the most part we avoid the use of XOR these days, but there - //are some edge cases where legacy stuff still supports it, so - //emulate it (slowly) here. - if (bXoring) - { - cairo_surface_t* target_surface = m_aCairoCommon.m_pSurface; - if (cairo_surface_get_type(target_surface) != CAIRO_SURFACE_TYPE_IMAGE) - { - //in the unlikely case we can't use m_aCairoCommon.m_pSurface directly, copy contents - //to another temp image surface - cairo_t* copycr = createTmpCompatibleCairoContext(); - cairo_rectangle(copycr, nExtentsLeft, nExtentsTop, - nExtentsRight - nExtentsLeft, - nExtentsBottom - nExtentsTop); - cairo_set_source_surface(copycr, m_aCairoCommon.m_pSurface, 0, 0); - cairo_paint(copycr); - target_surface = cairo_get_target(copycr); - cairo_destroy(copycr); - } - - cairo_surface_flush(target_surface); - unsigned char *target_surface_data = cairo_image_surface_get_data(target_surface); - unsigned char *xor_surface_data = cairo_image_surface_get_data(surface); - - cairo_format_t nFormat = cairo_image_surface_get_format(target_surface); - assert(nFormat == CAIRO_FORMAT_ARGB32 && "need to implement CAIRO_FORMAT_A1 after all here"); - sal_Int32 nStride = cairo_format_stride_for_width(nFormat, nWidth * m_fScale); - sal_Int32 nUnscaledExtentsLeft = nExtentsLeft * m_fScale; - sal_Int32 nUnscaledExtentsRight = nExtentsRight * m_fScale; - sal_Int32 nUnscaledExtentsTop = nExtentsTop * m_fScale; - sal_Int32 nUnscaledExtentsBottom = nExtentsBottom * m_fScale; - - // Handle headless size forced to (1,1) by SvpSalFrame::GetSurfaceFrameSize(). - int target_surface_width = cairo_image_surface_get_width(target_surface); - if (nUnscaledExtentsLeft > target_surface_width) - nUnscaledExtentsLeft = target_surface_width; - if (nUnscaledExtentsRight > target_surface_width) - nUnscaledExtentsRight = target_surface_width; - int target_surface_height = cairo_image_surface_get_height(target_surface); - if (nUnscaledExtentsTop > target_surface_height) - nUnscaledExtentsTop = target_surface_height; - if (nUnscaledExtentsBottom > target_surface_height) - nUnscaledExtentsBottom = target_surface_height; - - vcl::bitmap::lookup_table const & unpremultiply_table - = vcl::bitmap::get_unpremultiply_table(); - vcl::bitmap::lookup_table const & premultiply_table = vcl::bitmap::get_premultiply_table(); - for (sal_Int32 y = nUnscaledExtentsTop; y < nUnscaledExtentsBottom; ++y) - { - unsigned char *true_row = target_surface_data + (nStride*y); - unsigned char *xor_row = xor_surface_data + (nStride*y); - unsigned char *true_data = true_row + (nUnscaledExtentsLeft * 4); - unsigned char *xor_data = xor_row + (nUnscaledExtentsLeft * 4); - for (sal_Int32 x = nUnscaledExtentsLeft; x < nUnscaledExtentsRight; ++x) - { - sal_uInt8 a = true_data[SVP_CAIRO_ALPHA]; - sal_uInt8 xor_a = xor_data[SVP_CAIRO_ALPHA]; - sal_uInt8 b = unpremultiply_table[a][true_data[SVP_CAIRO_BLUE]] ^ - unpremultiply_table[xor_a][xor_data[SVP_CAIRO_BLUE]]; - sal_uInt8 g = unpremultiply_table[a][true_data[SVP_CAIRO_GREEN]] ^ - unpremultiply_table[xor_a][xor_data[SVP_CAIRO_GREEN]]; - sal_uInt8 r = unpremultiply_table[a][true_data[SVP_CAIRO_RED]] ^ - unpremultiply_table[xor_a][xor_data[SVP_CAIRO_RED]]; - true_data[SVP_CAIRO_BLUE] = premultiply_table[a][b]; - true_data[SVP_CAIRO_GREEN] = premultiply_table[a][g]; - true_data[SVP_CAIRO_RED] = premultiply_table[a][r]; - true_data+=4; - xor_data+=4; - } - } - cairo_surface_mark_dirty(target_surface); - - if (target_surface != m_aCairoCommon.m_pSurface) - { - cairo_t* copycr = cairo_create(m_aCairoCommon.m_pSurface); - //unlikely case we couldn't use m_aCairoCommon.m_pSurface directly, copy contents - //back from image surface - cairo_rectangle(copycr, nExtentsLeft, nExtentsTop, - nExtentsRight - nExtentsLeft, - nExtentsBottom - nExtentsTop); - cairo_set_source_surface(copycr, target_surface, 0, 0); - cairo_paint(copycr); - cairo_destroy(copycr); - cairo_surface_destroy(target_surface); - } - - cairo_surface_destroy(surface); - } - - cairo_destroy(cr); // unref - - DamageHandler* pDamage = static_cast<DamageHandler*>(cairo_surface_get_user_data(m_aCairoCommon.m_pSurface, getDamageKey())); - - if (pDamage) - { - pDamage->damaged(pDamage->handle, nExtentsLeft, nExtentsTop, - nExtentsRight - nExtentsLeft, - nExtentsBottom - nExtentsTop); - } -} - #if ENABLE_CAIRO_CANVAS bool SvpSalGraphics::SupportsCairo() const { @@ -2611,35 +2439,4 @@ bool SvpSalGraphics::supportsOperation(OutDevSupportType eType) const return false; } -void dl_cairo_surface_set_device_scale(cairo_surface_t *surface, double x_scale, double y_scale) -{ -#ifdef ANDROID - cairo_surface_set_device_scale(surface, x_scale, y_scale); -#else - static auto func = reinterpret_cast<void(*)(cairo_surface_t*, double, double)>( - dlsym(nullptr, "cairo_surface_set_device_scale")); - if (func) - func(surface, x_scale, y_scale); -#endif -} - -void dl_cairo_surface_get_device_scale(cairo_surface_t *surface, double* x_scale, double* y_scale) -{ -#ifdef ANDROID - cairo_surface_get_device_scale(surface, x_scale, y_scale); -#else - static auto func = reinterpret_cast<void(*)(cairo_surface_t*, double*, double*)>( - dlsym(nullptr, "cairo_surface_get_device_scale")); - if (func) - func(surface, x_scale, y_scale); - else - { - if (x_scale) - *x_scale = 1.0; - if (y_scale) - *y_scale = 1.0; - } -#endif -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/headless/CairoCommon.hxx b/vcl/inc/headless/CairoCommon.hxx index f0b929880d0c..c817a85de8ca 100644 --- a/vcl/inc/headless/CairoCommon.hxx +++ b/vcl/inc/headless/CairoCommon.hxx @@ -28,6 +28,9 @@ #include <vcl/region.hxx> #include <vcl/salgtype.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/range/b2irange.hxx> + //Using formats that match cairo's formats. For android we patch cairo, //which is internal in that case, to swap the rgb components so that //cairo then matches the OpenGL GL_RGBA format so we can use it there @@ -60,12 +63,26 @@ typedef struct _cairo cairo_t; typedef struct _cairo_surface cairo_surface_t; typedef struct _cairo_user_data_key cairo_user_data_key_t; +VCL_DLLPUBLIC void dl_cairo_surface_set_device_scale(cairo_surface_t* surface, double x_scale, + double y_scale); +VCL_DLLPUBLIC void dl_cairo_surface_get_device_scale(cairo_surface_t* surface, double* x_scale, + double* y_scale); + enum class PaintMode { Over, Xor }; +typedef void (*damageHandler)(void* handle, sal_Int32 nExtentsX, sal_Int32 nExtentsY, + sal_Int32 nExtentsWidth, sal_Int32 nExtentsHeight); + +struct VCL_DLLPUBLIC DamageHandler +{ + void* handle; + damageHandler damaged; +}; + struct VCL_DLLPUBLIC CairoCommon { cairo_surface_t* m_pSurface; @@ -74,16 +91,25 @@ struct VCL_DLLPUBLIC CairoCommon Color m_aLineColor; Color m_aFillColor; PaintMode m_ePaintMode; + double m_fScale; CairoCommon() : m_pSurface(nullptr) , m_aLineColor(Color(0x00, 0x00, 0x00)) , m_aFillColor(Color(0xFF, 0xFF, 0XFF)) , m_ePaintMode(PaintMode::Over) + , m_fScale(1.0) { } + static cairo_user_data_key_t* getDamageKey(); + cairo_surface_t* getSurface() const { return m_pSurface; } + + cairo_t* getCairoContext(bool bXorModeAllowed, bool bAntiAlias) const; + void releaseCairoContext(cairo_t* cr, bool bXorModeAllowed, + const basegfx::B2DRange& rExtents) const; + cairo_t* createTmpCompatibleCairoContext() const; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/headless/svpgdi.hxx b/vcl/inc/headless/svpgdi.hxx index 7c4c2d8813a1..73ec3b175082 100644 --- a/vcl/inc/headless/svpgdi.hxx +++ b/vcl/inc/headless/svpgdi.hxx @@ -42,28 +42,17 @@ struct BitmapBuffer; class FreetypeFont; -VCL_DLLPUBLIC void dl_cairo_surface_set_device_scale(cairo_surface_t *surface, double x_scale, double y_scale); -VCL_DLLPUBLIC void dl_cairo_surface_get_device_scale(cairo_surface_t *surface, double *x_scale, double *y_scale); - -typedef void (*damageHandler)(void* handle, - sal_Int32 nExtentsX, sal_Int32 nExtentsY, - sal_Int32 nExtentsWidth, sal_Int32 nExtentsHeight); - -struct VCL_DLLPUBLIC DamageHandler -{ - void *handle; - damageHandler damaged; -}; - class VCL_DLLPUBLIC SvpSalGraphics : public SalGraphicsAutoDelegateToImpl { CairoCommon m_aCairoCommon; - double m_fScale; public: void setSurface(cairo_surface_t* pSurface, const basegfx::B2IVector& rSize); cairo_surface_t* getSurface() const { return m_aCairoCommon.m_pSurface; } - static cairo_user_data_key_t* getDamageKey(); + static cairo_user_data_key_t* getDamageKey() + { + return CairoCommon::getDamageKey(); + } static void clipRegion(cairo_t* cr, const vcl::Region& rClipRegion); @@ -227,8 +216,16 @@ public: virtual css::uno::Any GetNativeSurfaceHandle(cairo::SurfaceSharedPtr& rSurface, const basegfx::B2ISize& rSize) const override; #endif // ENABLE_CAIRO_CANVAS - cairo_t* getCairoContext(bool bXorModeAllowed) const; - void releaseCairoContext(cairo_t* cr, bool bXorModeAllowed, const basegfx::B2DRange& rExtents) const; + cairo_t* getCairoContext(bool bXorModeAllowed) const + { + return m_aCairoCommon.getCairoContext(bXorModeAllowed, getAntiAlias()); + } + + void releaseCairoContext(cairo_t* cr, bool bXorModeAllowed, const basegfx::B2DRange& rExtents) const + { + return m_aCairoCommon.releaseCairoContext(cr, bXorModeAllowed, rExtents); + } + static cairo_surface_t* createCairoSurface(const BitmapBuffer *pBuffer); void clipRegion(cairo_t* cr); };
