office-gobmx/drawinglayer/source/processor2d/SDPRProcessor2dTools.cxx
Armin Le Grand (Collabora) 8a6fb1793d SDPRCairo: enhance CairoPixelProcessor2D
Added and corrected quite some stuff to this basic
construct provided by Caolan (thanks again!). No
detailed descriptions here, but it's getting more
complete and faster.

May contain errors and is primitive-complete in the
sense that all gets rendered, but some basic stuff
that should be directly supported is still missing,
e.g. text rendering.

Adding this to allow people to check it out (speed)
also since it's not yet used as default in master,
so it's safe and also safes the changes publically.

To test it, use TEST_SYSTEM_PRIMITIVE_RENDERER as
set EnvVar in linux systems.

Change-Id: I25f795c9ea4ad4f3b99591304f8803dffa499436
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168911
Tested-by: Jenkins
Reviewed-by: Armin Le Grand <Armin.Le.Grand@me.com>
2024-07-02 20:34:29 +02:00

302 lines
12 KiB
C++

/* -*- 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 <drawinglayer/processor2d/SDPRProcessor2dTools.hxx>
#include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <vcl/bitmapex.hxx>
#include <vcl/graph.hxx>
#include <basegfx/range/b2drange.hxx>
#ifdef DBG_UTIL
#include <tools/stream.hxx>
#include <vcl/filter/PngImageWriter.hxx>
#endif
namespace drawinglayer::processor2d
{
void setOffsetXYCreatedBitmap(
drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
const BitmapEx& rBitmap)
{
rFillGraphicPrimitive2D.impSetOffsetXYCreatedBitmap(rBitmap);
}
void takeCareOfOffsetXY(
const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
BitmapEx& rTarget, basegfx::B2DRange& rFillUnitRange)
{
const attribute::FillGraphicAttribute& rFillGraphicAttribute(
rFillGraphicPrimitive2D.getFillGraphic());
const bool bOffsetXIsUsed(rFillGraphicAttribute.getOffsetX() > 0.0
&& rFillGraphicAttribute.getOffsetX() < 1.0);
const bool bOffsetYIsUsed(rFillGraphicAttribute.getOffsetY() > 0.0
&& rFillGraphicAttribute.getOffsetY() < 1.0);
if (bOffsetXIsUsed)
{
if (rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
{
const Size& rSize(rTarget.GetSizePixel());
const tools::Long w(rSize.Width());
const tools::Long a(
basegfx::fround<tools::Long>(w * (1.0 - rFillGraphicAttribute.getOffsetX())));
if (0 != a && w != a)
{
const tools::Long h(rSize.Height());
const tools::Long b(w - a);
BitmapEx aTarget(Size(w, h * 2), rTarget.getPixelFormat());
aTarget.SetPrefSize(
Size(rTarget.GetPrefSize().Width(), rTarget.GetPrefSize().Height() * 2));
const tools::Rectangle aSrcDst(Point(), rSize);
aTarget.CopyPixel(aSrcDst, // Dst
aSrcDst, // Src
rTarget);
const Size aSizeA(b, h);
aTarget.CopyPixel(tools::Rectangle(Point(0, h), aSizeA), // Dst
tools::Rectangle(Point(a, 0), aSizeA), // Src
rTarget);
const Size aSizeB(a, h);
aTarget.CopyPixel(tools::Rectangle(Point(b, h), aSizeB), // Dst
tools::Rectangle(Point(), aSizeB), // Src
rTarget);
setOffsetXYCreatedBitmap(
const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D&>(
rFillGraphicPrimitive2D),
aTarget);
}
}
if (!rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
{
rTarget = rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap();
rFillUnitRange.expand(basegfx::B2DPoint(
rFillUnitRange.getMinX(), rFillUnitRange.getMaxY() + rFillUnitRange.getHeight()));
}
}
else if (bOffsetYIsUsed)
{
if (rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
{
const Size& rSize(rTarget.GetSizePixel());
const tools::Long h(rSize.Height());
const tools::Long a(
basegfx::fround<tools::Long>(h * (1.0 - rFillGraphicAttribute.getOffsetY())));
if (0 != a && h != a)
{
const tools::Long w(rSize.Width());
const tools::Long b(h - a);
BitmapEx aTarget(Size(w * 2, h), rTarget.getPixelFormat());
aTarget.SetPrefSize(
Size(rTarget.GetPrefSize().Width() * 2, rTarget.GetPrefSize().Height()));
const tools::Rectangle aSrcDst(Point(), rSize);
aTarget.CopyPixel(aSrcDst, // Dst
aSrcDst, // Src
rTarget);
const Size aSizeA(w, b);
aTarget.CopyPixel(tools::Rectangle(Point(w, 0), aSizeA), // Dst
tools::Rectangle(Point(0, a), aSizeA), // Src
rTarget);
const Size aSizeB(w, a);
aTarget.CopyPixel(tools::Rectangle(Point(w, b), aSizeB), // Dst
tools::Rectangle(Point(), aSizeB), // Src
rTarget);
setOffsetXYCreatedBitmap(
const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D&>(
rFillGraphicPrimitive2D),
aTarget);
}
}
if (!rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
{
rTarget = rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap();
rFillUnitRange.expand(basegfx::B2DPoint(
rFillUnitRange.getMaxX() + rFillUnitRange.getWidth(), rFillUnitRange.getMinY()));
}
}
}
bool prepareBitmapForDirectRender(
const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
const drawinglayer::geometry::ViewInformation2D& rViewInformation2D, BitmapEx& rTarget,
basegfx::B2DRange& rFillUnitRange, double fBigDiscreteArea)
{
const attribute::FillGraphicAttribute& rFillGraphicAttribute(
rFillGraphicPrimitive2D.getFillGraphic());
const Graphic& rGraphic(rFillGraphicAttribute.getGraphic());
if (rFillGraphicAttribute.isDefault() || rGraphic.IsNone())
{
// default attributes or GraphicType::NONE, so no fill .-> done
return false;
}
if (!rFillGraphicAttribute.getTiling())
{
// If no tiling used, the Graphic will need to be painted just once. This
// is perfectly done using the decomposition, so use it.
// What we want to do here is to optimize tiled paint, for two reasons:
// (a) speed: draw one tile, repeat -> obvious
// (b) correctness: not so obvious, but since in AAed paint the same edge
// of touching polygons both AAed do *not* sum up, but get blended by
// multiplication (0.5 * 0.5 -> 0.25) the connection will stay visible,
// not only with filled polygons, but also with bitmaps
// Signal that paint is needed
return true;
}
if (rFillUnitRange.isEmpty())
{
// no fill range definition, no fill, done
return false;
}
const basegfx::B2DHomMatrix aLocalTransform(rViewInformation2D.getObjectToViewTransformation()
* rFillGraphicPrimitive2D.getTransformation());
const basegfx::B2DRange& rDiscreteViewPort(rViewInformation2D.getDiscreteViewport());
if (!rDiscreteViewPort.isEmpty())
{
// calculate discrete covered pixel area
basegfx::B2DRange aDiscreteRange(basegfx::B2DRange::getUnitB2DRange());
aDiscreteRange.transform(aLocalTransform);
if (!aDiscreteRange.overlaps(rDiscreteViewPort))
{
// we have a Viewport and visible range of geometry is outside -> not visible, done
return false;
}
}
if (GraphicType::Bitmap == rGraphic.GetType() && rGraphic.IsAnimated())
{
// Need to prepare specialized AnimatedGraphicPrimitive2D,
// cannot handle here. Signal that paint is needed
return true;
}
if (GraphicType::Bitmap == rGraphic.GetType() && !rGraphic.getVectorGraphicData())
{
// bitmap graphic, always handle locally, so get bitmap data independent
// if it'sie or it's discrete display size
rTarget = rGraphic.GetBitmapEx();
}
else
{
// Vector Graphic Data fill, including metafile:
// We can know about discrete pixel size here, calculate and use it.
// To do so, using Vectors is sufficient to get the lengths. It is
// not necessary to transform the whole target coordinate system.
const basegfx::B2DVector aDiscreteXAxis(
aLocalTransform
* basegfx::B2DVector(rFillUnitRange.getMaxX() - rFillUnitRange.getMinX(), 0.0));
const basegfx::B2DVector aDiscreteYAxis(
aLocalTransform
* basegfx::B2DVector(0.0, rFillUnitRange.getMaxY() - rFillUnitRange.getMinY()));
// get and ensure minimal size
const double fDiscreteWidth(std::max(1.0, aDiscreteXAxis.getLength()));
const double fDiscreteHeight(std::max(1.0, aDiscreteYAxis.getLength()));
// compare with a big visualization size in discrete pixels
const double fTargetDiscreteArea(fDiscreteWidth * fDiscreteHeight);
if (fTargetDiscreteArea > fBigDiscreteArea)
{
// When the vector data is visualized big it is better to not handle here
// but use decomposition fallback which then will visualize the vector data
// directly -> better quality, acceptable number of tile repeat(s)
// signal that paint is needed
return true;
}
else
{
// If visualized small, the amount of repeated fills gets expensive, so
// in that case use a Bitmap and the Brush technique below.
// The Bitmap may be created here exactly for the needed target size
// (using local D2DBitmapPixelProcessor2D and the vector data),
// but since we have a HW renderer and re-use of system-dependent data
// at BitmapEx is possible, just get the default fallback Bitmap from the
// vector data to continue. Trust the existing converters for now to
// do something with good quality.
rTarget = rGraphic.GetBitmapEx();
}
}
if (rTarget.IsEmpty() || rTarget.GetSizePixel().IsEmpty())
{
// no pixel data, done
return false;
}
// react if OffsetX/OffsetY of the FillGraphicAttribute is used
takeCareOfOffsetXY(rFillGraphicPrimitive2D, rTarget, rFillUnitRange);
#ifdef DBG_UTIL
// allow to check bitmap data, e.g. control OffsetX/OffsetY stuff
static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
if (bDoSaveForVisualControl)
{
static const OUString sDumpPath(
OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
if (!sDumpPath.isEmpty())
{
SvFileStream aNew(sDumpPath + "test_getreplacement.png",
StreamMode::WRITE | StreamMode::TRUNC);
vcl::PngImageWriter aPNGWriter(aNew);
aPNGWriter.write(rTarget);
}
}
#endif
// signal to render it
return true;
}
void calculateDiscreteVisibleRange(
basegfx::B2DRange& rDiscreteVisibleRange, const basegfx::B2DRange& rContentRange,
const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
{
if (rContentRange.isEmpty())
{
// no content, done
rDiscreteVisibleRange.reset();
return;
}
basegfx::B2DRange aDiscreteRange(rContentRange);
aDiscreteRange.transform(rViewInformation2D.getObjectToViewTransformation());
const basegfx::B2DRange& rDiscreteViewPort(rViewInformation2D.getDiscreteViewport());
rDiscreteVisibleRange = aDiscreteRange;
if (!rDiscreteViewPort.isEmpty())
{
rDiscreteVisibleRange.intersect(rDiscreteViewPort);
}
}
} // end of namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */