vcl: move SurfaceHelper class to CairoCommon
Intermediate step beore moving bitmap related members.
Change-Id: Icf2d4cfb787dfb029f299cba4b4ffabae563bf6d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/127923
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/vcl/headless/CairoCommon.cxx b/vcl/headless/CairoCommon.cxx
index 6194d97..94ca2c3 100644
--- a/vcl/headless/CairoCommon.cxx
+++ b/vcl/headless/CairoCommon.cxx
@@ -1173,4 +1173,128 @@ void Toggle1BitTransparency(const BitmapBuffer& rBuf)
}
}
namespace
{
// check for env var that decides for using downscale pattern
const char* pDisableDownScale(getenv("SAL_DISABLE_CAIRO_DOWNSCALE"));
bool bDisableDownScale(nullptr != pDisableDownScale);
}
cairo_surface_t* SurfaceHelper::implCreateOrReuseDownscale(unsigned long nTargetWidth,
unsigned long nTargetHeight)
{
const unsigned long nSourceWidth(cairo_image_surface_get_width(pSurface));
const unsigned long nSourceHeight(cairo_image_surface_get_height(pSurface));
// zoomed in, need to stretch at paint, no pre-scale useful
if (nTargetWidth >= nSourceWidth || nTargetHeight >= nSourceHeight)
{
return pSurface;
}
// calculate downscale factor
unsigned long nWFactor(1);
unsigned long nW((nSourceWidth + 1) / 2);
unsigned long nHFactor(1);
unsigned long nH((nSourceHeight + 1) / 2);
while (nW > nTargetWidth && nW > 1)
{
nW = (nW + 1) / 2;
nWFactor *= 2;
}
while (nH > nTargetHeight && nH > 1)
{
nH = (nH + 1) / 2;
nHFactor *= 2;
}
if (1 == nWFactor && 1 == nHFactor)
{
// original size *is* best binary size, use it
return pSurface;
}
// go up one scale again - look for no change
nW = (1 == nWFactor) ? nTargetWidth : nW * 2;
nH = (1 == nHFactor) ? nTargetHeight : nH * 2;
// check if we have a downscaled version of required size
// bail out if the multiplication for the key would overflow
if (nW >= SAL_MAX_UINT32 || nH >= SAL_MAX_UINT32)
return pSurface;
const sal_uInt64 key((nW * static_cast<sal_uInt64>(SAL_MAX_UINT32)) + nH);
auto isHit(maDownscaled.find(key));
if (isHit != maDownscaled.end())
{
return isHit->second;
}
// create new surface in the targeted size
cairo_surface_t* pSurfaceTarget
= cairo_surface_create_similar(pSurface, cairo_surface_get_content(pSurface), nW, nH);
// made a version to scale self first that worked well, but would've
// been hard to support CAIRO_FORMAT_A1 including bit shifting, so
// I decided to go with cairo itself - use CAIRO_FILTER_FAST or
// CAIRO_FILTER_GOOD though. Please modify as needed for
// performance/quality
cairo_t* cr = cairo_create(pSurfaceTarget);
const double fScaleX(static_cast<double>(nW) / static_cast<double>(nSourceWidth));
const double fScaleY(static_cast<double>(nH) / static_cast<double>(nSourceHeight));
cairo_scale(cr, fScaleX, fScaleY);
cairo_set_source_surface(cr, pSurface, 0.0, 0.0);
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_GOOD);
cairo_paint(cr);
cairo_destroy(cr);
// need to set device_scale for downscale surfaces to get
// them handled correctly
cairo_surface_set_device_scale(pSurfaceTarget, fScaleX, fScaleY);
// add entry to cached entries
maDownscaled[key] = pSurfaceTarget;
return pSurfaceTarget;
}
bool SurfaceHelper::isTrivial() const
{
constexpr unsigned long nMinimalSquareSizeToBuffer(64 * 64);
const unsigned long nSourceWidth(cairo_image_surface_get_width(pSurface));
const unsigned long nSourceHeight(cairo_image_surface_get_height(pSurface));
return nSourceWidth * nSourceHeight < nMinimalSquareSizeToBuffer;
}
SurfaceHelper::SurfaceHelper()
: pSurface(nullptr)
{
}
SurfaceHelper::~SurfaceHelper()
{
cairo_surface_destroy(pSurface);
for (auto& candidate : maDownscaled)
{
cairo_surface_destroy(candidate.second);
}
}
cairo_surface_t* SurfaceHelper::getSurface(unsigned long nTargetWidth,
unsigned long nTargetHeight) const
{
if (bDisableDownScale || 0 == nTargetWidth || 0 == nTargetHeight || !pSurface || isTrivial())
{
// caller asks for original or disabled or trivial (smaller then a minimal square size)
// also excludes zero cases for width/height after this point if need to prescale
return pSurface;
}
return const_cast<SurfaceHelper*>(this)->implCreateOrReuseDownscale(nTargetWidth,
nTargetHeight);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/headless/svpgdi.cxx b/vcl/headless/svpgdi.cxx
index 92c0cbe..c894ebb 100644
--- a/vcl/headless/svpgdi.cxx
+++ b/vcl/headless/svpgdi.cxx
@@ -63,142 +63,6 @@ namespace
const char* pDisableDownScale(getenv("SAL_DISABLE_CAIRO_DOWNSCALE"));
bool bDisableDownScale(nullptr != pDisableDownScale);
class SurfaceHelper
{
private:
cairo_surface_t* pSurface;
std::unordered_map<sal_uInt64, cairo_surface_t*> maDownscaled;
SurfaceHelper(const SurfaceHelper&) = delete;
SurfaceHelper& operator=(const SurfaceHelper&) = delete;
cairo_surface_t* implCreateOrReuseDownscale(
unsigned long nTargetWidth,
unsigned long nTargetHeight)
{
const unsigned long nSourceWidth(cairo_image_surface_get_width(pSurface));
const unsigned long nSourceHeight(cairo_image_surface_get_height(pSurface));
// zoomed in, need to stretch at paint, no pre-scale useful
if(nTargetWidth >= nSourceWidth || nTargetHeight >= nSourceHeight)
{
return pSurface;
}
// calculate downscale factor
unsigned long nWFactor(1);
unsigned long nW((nSourceWidth + 1) / 2);
unsigned long nHFactor(1);
unsigned long nH((nSourceHeight + 1) / 2);
while(nW > nTargetWidth && nW > 1)
{
nW = (nW + 1) / 2;
nWFactor *= 2;
}
while(nH > nTargetHeight && nH > 1)
{
nH = (nH + 1) / 2;
nHFactor *= 2;
}
if(1 == nWFactor && 1 == nHFactor)
{
// original size *is* best binary size, use it
return pSurface;
}
// go up one scale again - look for no change
nW = (1 == nWFactor) ? nTargetWidth : nW * 2;
nH = (1 == nHFactor) ? nTargetHeight : nH * 2;
// check if we have a downscaled version of required size
// bail out if the multiplication for the key would overflow
if( nW >= SAL_MAX_UINT32 || nH >= SAL_MAX_UINT32 )
return pSurface;
const sal_uInt64 key((nW * static_cast<sal_uInt64>(SAL_MAX_UINT32)) + nH);
auto isHit(maDownscaled.find(key));
if(isHit != maDownscaled.end())
{
return isHit->second;
}
// create new surface in the targeted size
cairo_surface_t* pSurfaceTarget = cairo_surface_create_similar(
pSurface,
cairo_surface_get_content(pSurface),
nW,
nH);
// made a version to scale self first that worked well, but would've
// been hard to support CAIRO_FORMAT_A1 including bit shifting, so
// I decided to go with cairo itself - use CAIRO_FILTER_FAST or
// CAIRO_FILTER_GOOD though. Please modify as needed for
// performance/quality
cairo_t* cr = cairo_create(pSurfaceTarget);
const double fScaleX(static_cast<double>(nW)/static_cast<double>(nSourceWidth));
const double fScaleY(static_cast<double>(nH)/static_cast<double>(nSourceHeight));
cairo_scale(cr, fScaleX, fScaleY);
cairo_set_source_surface(cr, pSurface, 0.0, 0.0);
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_GOOD);
cairo_paint(cr);
cairo_destroy(cr);
// need to set device_scale for downscale surfaces to get
// them handled correctly
cairo_surface_set_device_scale(pSurfaceTarget, fScaleX, fScaleY);
// add entry to cached entries
maDownscaled[key] = pSurfaceTarget;
return pSurfaceTarget;
}
protected:
cairo_surface_t* implGetSurface() const { return pSurface; }
void implSetSurface(cairo_surface_t* pNew) { pSurface = pNew; }
bool isTrivial() const
{
constexpr unsigned long nMinimalSquareSizeToBuffer(64*64);
const unsigned long nSourceWidth(cairo_image_surface_get_width(pSurface));
const unsigned long nSourceHeight(cairo_image_surface_get_height(pSurface));
return nSourceWidth * nSourceHeight < nMinimalSquareSizeToBuffer;
}
public:
explicit SurfaceHelper()
: pSurface(nullptr)
{
}
~SurfaceHelper()
{
cairo_surface_destroy(pSurface);
for(auto& candidate : maDownscaled)
{
cairo_surface_destroy(candidate.second);
}
}
cairo_surface_t* getSurface(
unsigned long nTargetWidth = 0,
unsigned long nTargetHeight = 0) const
{
if (bDisableDownScale || 0 == nTargetWidth || 0 == nTargetHeight || !pSurface || isTrivial())
{
// caller asks for original or disabled or trivial (smaller then a minimal square size)
// also excludes zero cases for width/height after this point if need to prescale
return pSurface;
}
return const_cast<SurfaceHelper*>(this)->implCreateOrReuseDownscale(
nTargetWidth,
nTargetHeight);
}
};
class BitmapHelper : public SurfaceHelper
{
private:
diff --git a/vcl/inc/headless/CairoCommon.hxx b/vcl/inc/headless/CairoCommon.hxx
index b1f6a81..159714c 100644
--- a/vcl/inc/headless/CairoCommon.hxx
+++ b/vcl/inc/headless/CairoCommon.hxx
@@ -38,6 +38,8 @@
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <unordered_map>
//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
@@ -199,4 +201,29 @@ struct VCL_DLLPUBLIC CairoCommon
static cairo_surface_t* createCairoSurface(const BitmapBuffer* pBuffer);
};
class SurfaceHelper
{
private:
cairo_surface_t* pSurface;
std::unordered_map<sal_uInt64, cairo_surface_t*> maDownscaled;
SurfaceHelper(const SurfaceHelper&) = delete;
SurfaceHelper& operator=(const SurfaceHelper&) = delete;
cairo_surface_t* implCreateOrReuseDownscale(unsigned long nTargetWidth,
unsigned long nTargetHeight);
protected:
cairo_surface_t* implGetSurface() const { return pSurface; }
void implSetSurface(cairo_surface_t* pNew) { pSurface = pNew; }
bool isTrivial() const;
public:
explicit SurfaceHelper();
~SurfaceHelper();
cairo_surface_t* getSurface(unsigned long nTargetWidth = 0,
unsigned long nTargetHeight = 0) const;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */