drawinglayer/source/processor2d/cairopixelprocessor2d.cxx  |  174 ++++++++++++-
 drawinglayer/source/processor2d/processor2dtools.cxx       |   62 ++++
 drawinglayer/source/tools/converters.cxx                   |   25 +
 include/drawinglayer/processor2d/cairopixelprocessor2d.hxx |   32 ++
 include/drawinglayer/processor2d/processor2dtools.hxx      |   34 ++
 sd/qa/unit/PNGExportTests.cxx                              |   15 +
 6 files changed, 327 insertions(+), 15 deletions(-)

New commits:
commit a57c9e1acad3d2be35920ca2bb399ba2e925a51f
Author:     Armin Le Grand (allotropia) <armin.le.grand.ext...@allotropia.de>
AuthorDate: Mon Sep 30 12:04:44 2024 +0200
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Tue Oct 1 11:14:14 2024 +0200

    CairoSDPR: Support direct RGBA for convertToBitmapEx
    
    Added general interfaces to be able to render directly
    to an RGBA target SDPR:
    
    - processor2d::createPixelProcessor2DFromScratch creates
      a target-SDPR with given pixel size and RGB or RGBA,
      owning and using a cairo surface internally
    - processor2d::extractBitmapExFromBaseProcessor2D extracts
      rendered content to BitmapEx, including alpha
    
    All this is currently only implemented for Ciaro, thus
    the internal impls are encapsulated by USE_HEADLESS_CODE,
    but already created gererally available. The return values
    have to be checked to see if an evtl. shortcut is possible.
    
    For convertToBitmapEx this means that it can do conversions
    much faster than up to now for cairo when CairoSDPR is
    available, else it has to to the older slower way that
    also works and is the default: Create RGB content, create
    Mask, RemoveBlendedStartColor (see convertToBitmapEx
    implementation). This works and has the same quality,
    but needs two renderings and one bitmap operation, thus
    is clearly slower.
    
    Note that these interfaces can and will be supported for
    other SDPR implementations in the future, thus will make
    this converter automatically faster on systems where we
    will have a SDPR in the future, too.
    
    Also note that this is the gereral converter from
    a sequence of Primitives to Bitmap data, already used in
    many places, inculding UNO API, thus is expected to have
    some impact on conversion efficiency - if Cairo is used,
    so e.g. also headless.
    
    Change-Id: Ia638a549a04b19622892d91651317ec6727b6cd0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174266
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>
    Tested-by: Jenkins

diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx 
b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index a3ee8546f196..e8339f8f785b 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -13,6 +13,8 @@
 #include <drawinglayer/processor2d/SDPRProcessor2dTools.hxx>
 #include <sal/log.hxx>
 #include <vcl/BitmapTools.hxx>
+#include <vcl/BitmapWriteAccess.hxx>
+#include <vcl/alpha.hxx>
 #include <vcl/cairo.hxx>
 #include <vcl/outdev.hxx>
 #include <vcl/svapp.hxx>
@@ -865,13 +867,51 @@ basegfx::B2DRange getDiscreteViewRange(cairo_t* pRT)
 
 namespace drawinglayer::processor2d
 {
+CairoPixelProcessor2D::CairoPixelProcessor2D(const 
geometry::ViewInformation2D& rViewInformation,
+                                             tools::Long nWidthPixel, 
tools::Long nHeightPixel,
+                                             bool bUseRGBA)
+    : BaseProcessor2D(rViewInformation)
+    , maBColorModifierStack()
+    , mpOwnedSurface(nullptr)
+    , mpRT(nullptr)
+    , mbRenderSimpleTextDirect(
+          
officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get())
+    , mbRenderDecoratedTextDirect(
+          
officecfg::Office::Common::Drawinglayer::RenderDecoratedTextDirect::get())
+    , mnClipRecursionCount(0)
+{
+    if (nWidthPixel <= 0 || nHeightPixel <= 0)
+        // no size, invalid
+        return;
+
+    mpOwnedSurface = cairo_image_surface_create(bUseRGBA ? CAIRO_FORMAT_ARGB32 
: CAIRO_FORMAT_RGB24,
+                                                nWidthPixel, nHeightPixel);
+
+    if (nullptr == mpOwnedSurface)
+        // error, invalid
+        return;
+
+    // create RenderTarget for full target
+    mpRT = cairo_create(mpOwnedSurface);
+
+    if (nullptr == mpRT)
+        // error, invalid
+        return;
+
+    // initialize some basic used values/settings
+    cairo_set_antialias(mpRT, rViewInformation.getUseAntiAliasing() ? 
CAIRO_ANTIALIAS_DEFAULT
+                                                                    : 
CAIRO_ANTIALIAS_NONE);
+    cairo_set_fill_rule(mpRT, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_set_operator(mpRT, CAIRO_OPERATOR_OVER);
+}
+
 CairoPixelProcessor2D::CairoPixelProcessor2D(const 
geometry::ViewInformation2D& rViewInformation,
                                              cairo_surface_t* pTarget, 
tools::Long nOffsetPixelX,
                                              tools::Long nOffsetPixelY, 
tools::Long nWidthPixel,
                                              tools::Long nHeightPixel)
     : BaseProcessor2D(rViewInformation)
     , maBColorModifierStack()
-    , mpCreateForRectangle(nullptr)
+    , mpOwnedSurface(nullptr)
     , mpRT(nullptr)
     , mbRenderSimpleTextDirect(
           
officecfg::Office::Common::Drawinglayer::RenderSimpleTextDirect::get())
@@ -909,11 +949,11 @@ CairoPixelProcessor2D::CairoPixelProcessor2D(const 
geometry::ViewInformation2D&
             // optional: if the possibility to add an initial clip relative
             // to the real pixel dimensions of the target surface is used,
             // apply it here using that nice existing method of cairo
-            mpCreateForRectangle = cairo_surface_create_for_rectangle(
+            mpOwnedSurface = cairo_surface_create_for_rectangle(
                 pTarget, nOffsetPixelX, nOffsetPixelY, nWidthPixel, 
nHeightPixel);
 
-            if (nullptr != mpCreateForRectangle)
-                mpRT = cairo_create(mpCreateForRectangle);
+            if (nullptr != mpOwnedSurface)
+                mpRT = cairo_create(mpOwnedSurface);
         }
         else
         {
@@ -937,8 +977,129 @@ CairoPixelProcessor2D::~CairoPixelProcessor2D()
 {
     if (nullptr != mpRT)
         cairo_destroy(mpRT);
-    if (nullptr != mpCreateForRectangle)
-        cairo_surface_destroy(mpCreateForRectangle);
+    if (nullptr != mpOwnedSurface)
+        cairo_surface_destroy(mpOwnedSurface);
+}
+
+BitmapEx CairoPixelProcessor2D::extractBitmapEx() const
+{
+    // default is empty BitmapEx
+    BitmapEx aRetval;
+
+    if (nullptr == mpRT)
+        // no RenderContext, not valid
+        return aRetval;
+
+    cairo_surface_t* pSource(cairo_get_target(mpRT));
+    if (nullptr == pSource)
+        // no surface, not valid
+        return aRetval;
+
+    // check pixel sizes
+    const sal_uInt32 nWidth(cairo_image_surface_get_width(pSource));
+    const sal_uInt32 nHeight(cairo_image_surface_get_height(pSource));
+    if (0 == nWidth || 0 == nHeight)
+        // no content, not valid
+        return aRetval;
+
+    // check format
+    const cairo_format_t aFormat(cairo_image_surface_get_format(pSource));
+    if (CAIRO_FORMAT_ARGB32 != aFormat && CAIRO_FORMAT_RGB24 != aFormat)
+        // we for now only support ARGB32 and RGB24, format not supported, not 
valid
+        return aRetval;
+
+    // ensure surface read access, wer need CAIRO_SURFACE_TYPE_IMAGE
+    cairo_surface_t* pReadSource(pSource);
+
+    if (CAIRO_SURFACE_TYPE_IMAGE != cairo_surface_get_type(pReadSource))
+    {
+        // create mapping for read access to source
+        pReadSource = cairo_surface_map_to_image(pReadSource, nullptr);
+    }
+
+    // prepare VCL/Bitmap stuff
+    const Size aBitmapSize(nWidth, nHeight);
+    Bitmap aBitmap(aBitmapSize, vcl::PixelFormat::N24_BPP);
+    BitmapWriteAccess aAccess(aBitmap);
+
+    // prepare VCL/AlphaMask stuff
+    const bool bHasAlpha(CAIRO_FORMAT_ARGB32 == aFormat);
+    std::optional<AlphaMask> aAlphaMask;
+    // NOTE: Tried to use std::optional for pAlphaWrite but
+    // BitmapWriteAccess does not have all needed operators
+    BitmapWriteAccess* pAlphaWrite(nullptr);
+    if (bHasAlpha)
+    {
+        aAlphaMask = AlphaMask(aBitmapSize);
+        pAlphaWrite = new BitmapWriteAccess(*aAlphaMask);
+    }
+
+    // prepare cairo stuff
+    const sal_uInt32 nStride(cairo_image_surface_get_stride(pReadSource));
+    unsigned char* pStartPixelData(cairo_image_surface_get_data(pReadSource));
+
+    // separate loops for bHasAlpha so that we have *no* branch in the
+    // loops itself
+    if (bHasAlpha)
+    {
+        for (sal_uInt32 y(0); y < nHeight; ++y)
+        {
+            // prepare scanline
+            unsigned char* pPixelData(pStartPixelData + (nStride * y));
+            Scanline pWriteRGB = aAccess.GetScanline(y);
+            Scanline pWriteA = pAlphaWrite->GetScanline(y);
+
+            for (sal_uInt32 x(0); x < nWidth; ++x)
+            {
+                // RGBA: Do not forget: it's pre-mulitiplied
+                sal_uInt8 nAlpha(pPixelData[SVP_CAIRO_ALPHA]);
+                aAccess.SetPixelOnData(
+                    pWriteRGB, x,
+                    
BitmapColor(vcl::bitmap::unpremultiply(pPixelData[SVP_CAIRO_RED], nAlpha),
+                                
vcl::bitmap::unpremultiply(pPixelData[SVP_CAIRO_GREEN], nAlpha),
+                                
vcl::bitmap::unpremultiply(pPixelData[SVP_CAIRO_BLUE], nAlpha)));
+                pAlphaWrite->SetPixelOnData(pWriteA, x, BitmapColor(nAlpha));
+                pPixelData += 4;
+            }
+        }
+    }
+    else
+    {
+        for (sal_uInt32 y(0); y < nHeight; ++y)
+        {
+            // prepare scanline
+            unsigned char* pPixelData(pStartPixelData + (nStride * y));
+            Scanline pWriteRGB = aAccess.GetScanline(y);
+
+            for (sal_uInt32 x(0); x < nWidth; ++x)
+            {
+                aAccess.SetPixelOnData(pWriteRGB, x,
+                                       BitmapColor(pPixelData[SVP_CAIRO_RED],
+                                                   pPixelData[SVP_CAIRO_GREEN],
+                                                   
pPixelData[SVP_CAIRO_BLUE]));
+                pPixelData += 4;
+            }
+        }
+    }
+
+    // cleanup optional BitmapWriteAccess pAlphaWrite
+    if (nullptr != pAlphaWrite)
+        delete pAlphaWrite;
+
+    if (bHasAlpha)
+        // construct and return BitmapEx
+        aRetval = BitmapEx(aBitmap, *aAlphaMask);
+    else
+        // reset BitmapEx to just Bitmap content
+        aRetval = aBitmap;
+
+    if (pReadSource != pSource)
+    {
+        // cleanup mapping for read/write access to source
+        cairo_surface_unmap_image(pSource, pReadSource);
+    }
+
+    return aRetval;
 }
 
 void CairoPixelProcessor2D::processBitmapPrimitive2D(
@@ -1394,6 +1555,7 @@ void CairoPixelProcessor2D::processInvertPrimitive2D(
 
         if (CAIRO_SURFACE_TYPE_IMAGE != cairo_surface_get_type(pRenderTarget))
         {
+            // create mapping for read/write access to pRenderTarget
             pRenderTarget = cairo_surface_map_to_image(pRenderTarget, nullptr);
         }
 
diff --git a/drawinglayer/source/processor2d/processor2dtools.cxx 
b/drawinglayer/source/processor2d/processor2dtools.cxx
index 9aee0df04672..f87bffea6b21 100644
--- a/drawinglayer/source/processor2d/processor2dtools.cxx
+++ b/drawinglayer/source/processor2d/processor2dtools.cxx
@@ -32,6 +32,46 @@
 
 namespace drawinglayer::processor2d
 {
+std::unique_ptr<BaseProcessor2D> createPixelProcessor2DFromScratch(
+    const drawinglayer::geometry::ViewInformation2D& rViewInformation2D,
+    sal_uInt32 nPixelWidth,
+    sal_uInt32 nPixelHeight,
+    bool bUseRGBA)
+{
+    if (0 == nPixelWidth || 0 == nPixelHeight)
+        // error: no size given
+        return nullptr;
+
+#if USE_HEADLESS_CODE
+    // Linux/Cairo: now globally activated in master. Leave a
+    // possibility to deactivate for easy test/request testing
+    static bool bUsePrimitiveRenderer(nullptr == 
std::getenv("DISABLE_SYSTEM_DEPENDENT_PRIMITIVE_RENDERER"));
+
+    if (bUsePrimitiveRenderer)
+    {
+        // create CairoPixelProcessor2D with given size
+        std::unique_ptr<CairoPixelProcessor2D> aRetval(
+            std::make_unique<CairoPixelProcessor2D>(
+                rViewInformation2D,
+                nPixelWidth,
+                nPixelHeight,
+                bUseRGBA));
+
+        if (aRetval->valid())
+            return aRetval;
+    }
+#endif
+
+    // avoid unused parameter errors
+    (void)rViewInformation2D;
+    (void)nPixelWidth;
+    (void)nPixelHeight;
+    (void)bUseRGBA;
+
+    // error: no result when no SDPR supported
+    return nullptr;
+}
+
 std::unique_ptr<BaseProcessor2D> createPixelProcessor2DFromOutputDevice(
     OutputDevice& rTargetOutDev,
     const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
@@ -69,7 +109,6 @@ std::unique_ptr<BaseProcessor2D> 
createPixelProcessor2DFromOutputDevice(
     if (bUsePrimitiveRenderer)
     {
         SystemGraphicsData aData(rTargetOutDev.GetSystemGfxData());
-        const Size aSizePixel(rTargetOutDev.GetOutputSizePixel());
 
         // create CairoPixelProcessor2D, make use of the possibility to
         // add an initial clip relative to the real pixel dimensions of
@@ -83,7 +122,7 @@ std::unique_ptr<BaseProcessor2D> 
createPixelProcessor2DFromOutputDevice(
             std::make_unique<CairoPixelProcessor2D>(
                 rViewInformation2D, 
static_cast<cairo_surface_t*>(aData.pSurface),
                 rTargetOutDev.GetOutOffXPixel(), 
rTargetOutDev.GetOutOffYPixel(),
-                aSizePixel.getWidth(), aSizePixel.getHeight()));
+                rTargetOutDev.GetOutputWidthPixel(), 
rTargetOutDev.GetOutputHeightPixel()));
 
         if (aRetval->valid())
             return aRetval;
@@ -117,6 +156,25 @@ std::unique_ptr<BaseProcessor2D> 
createProcessor2DFromOutputDevice(
     }
 }
 
+BitmapEx extractBitmapExFromBaseProcessor2D(const 
std::unique_ptr<BaseProcessor2D>& rProcessor)
+{
+    BitmapEx aRetval;
+
+#if USE_HEADLESS_CODE
+    // currently only defined for cairo
+    CairoPixelProcessor2D* 
pSource(dynamic_cast<CairoPixelProcessor2D*>(rProcessor.get()));
+
+    if (nullptr != pSource)
+        aRetval = pSource->extractBitmapEx();
+#endif
+
+    // avoid unused parameter errors
+    (void)rProcessor;
+
+    // default: return empty BitmapEx
+    return aRetval;
+}
+
 } // end of namespace
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/tools/converters.cxx 
b/drawinglayer/source/tools/converters.cxx
index f21ac499b073..6d1a87c3e0f3 100644
--- a/drawinglayer/source/tools/converters.cxx
+++ b/drawinglayer/source/tools/converters.cxx
@@ -29,15 +29,13 @@
 #include <comphelper/diagnose_ex.hxx>
 
 #include <drawinglayer/converters.hxx>
+#include <config_vclplug.h>
 
 #ifdef DBG_UTIL
 #include <tools/stream.hxx>
-// #include <vcl/filter/PngImageWriter.hxx>
 #include <vcl/dibtools.hxx>
 #endif
 
-// #include <vcl/BitmapReadAccess.hxx>
-
 namespace
 {
 bool implPrepareConversion(drawinglayer::primitive2d::Primitive2DContainer& 
rSequence,
@@ -165,6 +163,27 @@ BitmapEx 
convertToBitmapEx(drawinglayer::primitive2d::Primitive2DContainer&& rSe
         return BitmapEx();
     }
 
+#if USE_HEADLESS_CODE
+    // shortcut: try to directly create a PixelProcessor2D with
+    // RGBA support - that's what we need
+    // Currently only implemented for CairoSDPR, so add code only
+    // for USE_HEADLESS_CODE, but is designed as a general functionality
+    std::unique_ptr<processor2d::BaseProcessor2D> pRGBAProcessor
+        = processor2d::createPixelProcessor2DFromScratch(rViewInformation2D, 
nDiscreteWidth, nDiscreteHeight, true);
+    if (pRGBAProcessor)
+    {
+        // render content
+        pRGBAProcessor->process(aSequence);
+
+        // create final BitmapEx result (content)
+        const BitmapEx 
aRetval(processor2d::extractBitmapExFromBaseProcessor2D(pRGBAProcessor));
+
+        // check if we have a result and return if so
+        if (!aRetval.IsEmpty())
+            return aRetval;
+    }
+#endif
+
     const Point aEmptyPoint;
     const Size aSizePixel(nDiscreteWidth, nDiscreteHeight);
 
diff --git a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx 
b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
index cd0f8a562e50..b9d6bdb3ab99 100644
--- a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
+++ b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
@@ -70,10 +70,11 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) 
CairoPixelProcessor2D final : pub
     basegfx::BColorModifierStack maBColorModifierStack;
 
     // cairo_surface_t created when initial clip from the constructor
-    // parameters is requested
-    cairo_surface_t* mpCreateForRectangle;
+    // parameters is requested, or by the constructor that creates an
+    // owned surface
+    cairo_surface_t* mpOwnedSurface;
 
-    // cairo specific data
+    // cairo specific data, the render target
     cairo_t* mpRT;
 
     // get text render config settings
@@ -182,7 +183,29 @@ protected:
 
 public:
     bool valid() const { return hasRenderTarget() && !hasError(); }
+
+    // construtcor to create a CairoPixelProcessor2D which
+    // allocates and owns a cairo surface of given size. You
+    // should check the result using valid()
+    CairoPixelProcessor2D(
+
+        // the initial ViewInformation
+        const geometry::ViewInformation2D& rViewInformation,
+
+        // the pixel size
+        tools::Long nWidthPixel, tools::Long nHeightPixel,
+
+        // define RGBA (true) or RGB (false)
+        bool bUseRGBA);
+
+    // constructor to create a CairoPixelProcessor2D for
+    // the given cairo_surface_t pTarget. pTarget will not
+    // be owned and not destroyed, but be used as render
+    // target. If needed you can define a sub-rectangle
+    // to which the rendering will be limited (clipped).
+    // You should check the result using valid()
     CairoPixelProcessor2D(
+
         // the initial ViewInformation
         const geometry::ViewInformation2D& rViewInformation,
 
@@ -205,6 +228,9 @@ public:
     {
         maBColorModifierStack = rStack;
     }
+
+    // try to extract current content as BitmapEx
+    BitmapEx extractBitmapEx() const;
 };
 }
 
diff --git a/include/drawinglayer/processor2d/processor2dtools.hxx 
b/include/drawinglayer/processor2d/processor2dtools.hxx
index 86ad2562e711..1f9960f57f68 100644
--- a/include/drawinglayer/processor2d/processor2dtools.hxx
+++ b/include/drawinglayer/processor2d/processor2dtools.hxx
@@ -20,6 +20,7 @@
 #define INCLUDED_DRAWINGLAYER_PROCESSOR2D_PROCESSOR2DTOOLS_HXX
 
 #include <drawinglayer/drawinglayerdllapi.h>
+#include <vcl/bitmapex.hxx>
 #include <memory>
 
 namespace drawinglayer::geometry { class ViewInformation2D; }
@@ -29,6 +30,28 @@ class OutputDevice;
 
 namespace drawinglayer::processor2d
     {
+        /** create the best available pixel based BaseProcessor2D
+            (which may be system-dependent) for a given pixel size
+            and format
+
+            @param rViewInformation2D
+            The ViewInformation to use
+
+            @param nPixelWidth, nPixelHeight
+            The dimensions in Pixles
+
+            @param bUseRGBA
+            Define RGBA (true) or RGB (false)
+
+            @return
+            the created BaseProcessor2D OR empty (ownership change)
+        */
+        DRAWINGLAYER_DLLPUBLIC std::unique_ptr<BaseProcessor2D> 
createPixelProcessor2DFromScratch(
+            const drawinglayer::geometry::ViewInformation2D& 
rViewInformation2D,
+            sal_uInt32 nPixelWidth,
+            sal_uInt32 nPixelHeight,
+            bool bUseRGBA);
+
         /** create the best available pixel based BaseProcessor2D
             (which may be system-dependent)
 
@@ -63,6 +86,17 @@ namespace drawinglayer::processor2d
             OutputDevice& rTargetOutDev,
             const drawinglayer::geometry::ViewInformation2D& 
rViewInformation2D);
 
+        /** extract the pixel data from a given BaseProcessor2D to
+            a BitmapEx. This may fail due to maybe system-dependent
+
+            @param rProcessor
+            A unique_ptr to a BaseProcessor2D from which to extract
+
+            @return
+            a BitmapEx, may be empty, so check result
+        */
+        DRAWINGLAYER_DLLPUBLIC BitmapEx 
extractBitmapExFromBaseProcessor2D(const std::unique_ptr<BaseProcessor2D>& 
rProcessor);
+
 
 } // end of namespace drawinglayer::processor2d
 
diff --git a/sd/qa/unit/PNGExportTests.cxx b/sd/qa/unit/PNGExportTests.cxx
index d4544f8a3dde..fd23e4867509 100644
--- a/sd/qa/unit/PNGExportTests.cxx
+++ b/sd/qa/unit/PNGExportTests.cxx
@@ -334,15 +334,28 @@ CPPUNIT_TEST_FIXTURE(SdPNGExportTest, testTdf158743)
     // make sure the bitmap is not empty and correct size (PNG export->import 
was successful)
     Size aSize = aBMPEx.GetSizePixel();
     CPPUNIT_ASSERT_EQUAL(Size(100, 100), aSize);
+
+    // read RGB
     Bitmap aBMP = aBMPEx.GetBitmap();
     BitmapScopedReadAccess pReadAccess(aBMP);
+
+    // read Alpha
+    Bitmap aAlpha = aBMPEx.GetAlphaMask().GetBitmap();
+    BitmapScopedReadAccess pReadAccessAlpha(aAlpha);
+
     int nBlackCount = 0;
     for (tools::Long nX = 1; nX < aSize.Width() - 1; ++nX)
     {
         for (tools::Long nY = 1; nY < aSize.Height() - 1; ++nY)
         {
             const Color aColor = pReadAccess->GetColor(nY, nX);
-            if (aColor == COL_BLACK)
+            const Color aTrans = pReadAccessAlpha->GetColor(nY, nX);
+
+            // only count as black when *not* transparent, else
+            // the color is random/luck. Note that when accessing
+            // AlphaMask like this alpha is actually in R, G and B,
+            // *not* in GetAlpha() (sigh...)
+            if (0 != aTrans.GetRed() && aColor == COL_BLACK)
                 ++nBlackCount;
         }
     }

Reply via email to