LibreOffice Module canvas (master)  1
spritehelper.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 
28 #include <rtl/math.hxx>
29 #include <tools/diagnose_ex.h>
30 #include <vcl/alpha.hxx>
31 #include <vcl/bitmapex.hxx>
32 #include <vcl/canvastools.hxx>
33 #include <vcl/outdev.hxx>
35 #include <vcl/skia/SkiaHelper.hxx>
36 
37 #include <canvas/canvastools.hxx>
38 #include <config_features.h>
39 
40 #include "impltools.hxx"
41 #include "spritehelper.hxx"
42 
43 using namespace ::com::sun::star;
44 
45 
46 namespace vclcanvas
47 {
49  mpBackBuffer(),
50  mpBackBufferMask(),
51  maContent(),
52  mbShowSpriteBounds(false)
53  {
54  }
55 
56  void SpriteHelper::init( const geometry::RealSize2D& rSpriteSize,
57  const ::canvas::SpriteSurface::Reference& rOwningSpriteCanvas,
58  const BackBufferSharedPtr& rBackBuffer,
59  const BackBufferSharedPtr& rBackBufferMask,
60  bool bShowSpriteBounds )
61  {
62  ENSURE_OR_THROW( rOwningSpriteCanvas && rBackBuffer && rBackBufferMask,
63  "SpriteHelper::init(): Invalid sprite canvas or back buffer" );
64 
65  mpBackBuffer = rBackBuffer;
66  mpBackBufferMask = rBackBufferMask;
67  mbShowSpriteBounds = bShowSpriteBounds;
68 
69  init( rSpriteSize, rOwningSpriteCanvas );
70  }
71 
73  {
74  mpBackBuffer.reset();
75  mpBackBufferMask.reset();
76 
77  // forward to parent
78  CanvasCustomSpriteHelper::disposing();
79  }
80 
81  void SpriteHelper::redraw( OutputDevice& rTargetSurface,
82  const ::basegfx::B2DPoint& rPos,
83  bool& io_bSurfacesDirty,
84  bool bBufferedUpdate ) const
85  {
86  (void)bBufferedUpdate; // not used on every platform
87 
88  if( !mpBackBuffer ||
90  {
91  return; // we're disposed
92  }
93 
94  // log output pos in device pixel
95  SAL_INFO("canvas.vcl", "SpriteHelper::redraw(): output pos is (" <<
96  rPos.getX() << "," << rPos.getY() << ")");
97 
98  const double fAlpha( getAlpha() );
99 
100  if( !isActive() || ::basegfx::fTools::equalZero( fAlpha ) )
101  return;
102 
103  const ::basegfx::B2DVector& rOrigOutputSize( getSizePixel() );
104 
105  // might get changed below (e.g. adapted for
106  // transformations). IMPORTANT: both position and size are
107  // rounded to integer values. From now on, only those
108  // rounded values are used, to keep clip and content in
109  // sync.
110  ::Size aOutputSize( vcl::unotools::sizeFromB2DSize( rOrigOutputSize ) );
111  ::Point aOutPos( vcl::unotools::pointFromB2DPoint( rPos ) );
112 
113 
114  // TODO(F3): Support for alpha-VDev
115 
116  // Do we have to update our bitmaps (necessary if virdev
117  // was painted to, or transformation changed)?
118  const bool bNeedBitmapUpdate( io_bSurfacesDirty ||
120  maContent->IsEmpty() );
121 
122  // updating content of sprite cache - surface is no
123  // longer dirty in relation to our cache
124  io_bSurfacesDirty = false;
126 
127  if( bNeedBitmapUpdate )
128  {
129  const Point aEmptyPoint;
130  BitmapEx aBmp( mpBackBuffer->getOutDev().GetBitmapEx( aEmptyPoint,
131  aOutputSize ) );
132 
133  if( isContentFullyOpaque() )
134  {
135  // optimized case: content canvas is fully
136  // opaque. Note: since we retrieved aBmp directly
137  // from an OutDev, it's already a 'display bitmap'
138  // on windows.
139  maContent = aBmp;
140  }
141  else
142  {
143  // sprite content might contain alpha, create
144  // BmpEx, then.
145  BitmapEx aMask( mpBackBufferMask->getOutDev().GetBitmapEx( aEmptyPoint,
146  aOutputSize ) );
147 
148  // bitmasks are much faster than alphamasks on some platforms
149  // so convert to bitmask if useful
150  bool convertTo1Bpp = aMask.GetBitCount() != 1;
151 #ifdef MACOSX
152  convertTo1Bpp = false;
153 #endif
155  convertTo1Bpp = false;
156 
157  if( convertTo1Bpp )
158  {
159  OSL_FAIL("CanvasCustomSprite::redraw(): Mask bitmap is not "
160  "monochrome (performance!)");
161  BitmapEx aMaskEx(aMask);
163  aMask = aMaskEx.GetBitmap();
164  }
165 
166  // Note: since we retrieved aBmp and aMask
167  // directly from an OutDev, it's already a
168  // 'display bitmap' on windows.
169  if( aMask.GetBitCount() == 1 )
170  maContent = BitmapEx( aBmp.GetBitmap(), aMask.GetBitmap() );
171  else
172  maContent = BitmapEx( aBmp.GetBitmap(), AlphaMask( aMask.GetBitmap()) );
173  }
174  }
175 
177 
178  // check whether matrix is "easy" to handle - pure
179  // translations or scales are handled by OutputDevice
180  // alone
181  const bool bIdentityTransform( aTransform.isIdentity() );
182 
183  // make transformation absolute (put sprite to final
184  // output position). Need to happen here, as we also have
185  // to translate the clip polygon
186  aTransform.translate( aOutPos.X(),
187  aOutPos.Y() );
188 
189  if( !bIdentityTransform )
190  {
191  if (!::basegfx::fTools::equalZero( aTransform.get(0,1) ) ||
192  !::basegfx::fTools::equalZero( aTransform.get(1,0) ))
193  {
194  // "complex" transformation, employ affine
195  // transformator
196 
197  // modify output position, to account for the fact
198  // that transformBitmap() always normalizes its output
199  // bitmap into the smallest enclosing box.
200  ::basegfx::B2DRectangle aDestRect;
203  0,
204  rOrigOutputSize.getX(),
205  rOrigOutputSize.getY()),
206  aTransform );
207 
208  aOutPos.setX( ::basegfx::fround( aDestRect.getMinX() ) );
209  aOutPos.setY( ::basegfx::fround( aDestRect.getMinY() ) );
210 
211  // TODO(P3): Use optimized bitmap transformation here.
212 
213  // actually re-create the bitmap ONLY if necessary
214  if( bNeedBitmapUpdate )
216  aTransform );
217 
218  aOutputSize = maContent->GetSizePixel();
219  }
220  else
221  {
222  // relatively 'simplistic' transformation -
223  // retrieve scale and translational offset
224  aOutputSize.setWidth (
225  ::basegfx::fround( rOrigOutputSize.getX() * aTransform.get(0,0) ) );
226  aOutputSize.setHeight(
227  ::basegfx::fround( rOrigOutputSize.getY() * aTransform.get(1,1) ) );
228 
229  aOutPos.setX( ::basegfx::fround( aTransform.get(0,2) ) );
230  aOutPos.setY( ::basegfx::fround( aTransform.get(1,2) ) );
231  }
232  }
233 
234  // transformBitmap() might return empty bitmaps, for tiny
235  // scales.
236  if( !(*maContent) )
237  return;
238 
239  rTargetSurface.Push( PushFlags::CLIPREGION );
240 
241  // apply clip (if any)
242  if( getClip().is() )
243  {
244  ::basegfx::B2DPolyPolygon aClipPoly(
246  getClip() ));
247 
248  if( aClipPoly.count() )
249  {
250  // aTransform already contains the
251  // translational component, moving the clip to
252  // the final sprite output position.
253  aClipPoly.transform( aTransform );
254 
255  if( mbShowSpriteBounds )
256  {
257  // Paint green sprite clip area
258  rTargetSurface.SetLineColor( Color( 0,255,0 ) );
259  rTargetSurface.SetFillColor();
260 
261  rTargetSurface.DrawPolyPolygon(::tools::PolyPolygon(aClipPoly)); // #i76339#
262  }
263 
264  vcl::Region aClipRegion( aClipPoly );
265  rTargetSurface.SetClipRegion( aClipRegion );
266  }
267  }
268 
269  if( ::rtl::math::approxEqual(fAlpha, 1.0) )
270  {
271  // no alpha modulation -> just copy to output
272  if( maContent->IsTransparent() )
273  rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize, *maContent );
274  else
275  rTargetSurface.DrawBitmap( aOutPos, aOutputSize, maContent->GetBitmap() );
276  }
277  else
278  {
279  // TODO(P3): Switch to OutputDevice::DrawTransparent()
280  // here
281 
282  // draw semi-transparent
283  sal_uInt8 nColor( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) );
284  AlphaMask aAlpha( maContent->GetSizePixel(),
285  &nColor );
286 
287  // mask out fully transparent areas
288  if( maContent->IsTransparent() )
289  aAlpha.Replace( maContent->GetMask(), 255 );
290 
291  // alpha-blend to output
292  rTargetSurface.DrawBitmapEx( aOutPos, aOutputSize,
293  BitmapEx( maContent->GetBitmap(),
294  aAlpha ) );
295  }
296 
297  rTargetSurface.Pop();
298 
299  if( !mbShowSpriteBounds )
300  return;
301 
302  ::tools::PolyPolygon aMarkerPoly(
304  ::basegfx::B2DRectangle(aOutPos.X(),
305  aOutPos.Y(),
306  aOutPos.X() + aOutputSize.Width()-1,
307  aOutPos.Y() + aOutputSize.Height()-1) ) );
308 
309  // Paint little red sprite area markers
310  rTargetSurface.SetLineColor( COL_RED );
311  rTargetSurface.SetFillColor();
312 
313  for( int i=0; i<aMarkerPoly.Count(); ++i )
314  {
315  rTargetSurface.DrawPolyLine( aMarkerPoly.GetObject(static_cast<sal_uInt16>(i)) );
316  }
317 
318  // paint sprite prio
319  vcl::Font aVCLFont;
320  aVCLFont.SetFontHeight( std::min(::tools::Long(20),aOutputSize.Height()) );
321  aVCLFont.SetColor( COL_RED );
322 
323  rTargetSurface.SetTextAlign(ALIGN_TOP);
324  rTargetSurface.SetTextColor( COL_RED );
325  rTargetSurface.SetFont( aVCLFont );
326 
327  OUString text( ::rtl::math::doubleToUString( getPriority(),
328  rtl_math_StringFormat_F,
329  2,'.',nullptr,' ') );
330 
331  rTargetSurface.DrawText( aOutPos+Point(2,2), text );
332  SAL_INFO( "canvas.vcl",
333  "sprite " << this << " has prio " << getPriority());
334  }
335 
336  ::basegfx::B2DPolyPolygon SpriteHelper::polyPolygonFromXPolyPolygon2D( uno::Reference< rendering::XPolyPolygon2D >& xPoly ) const
337  {
338  return ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPoly );
339  }
340 
341 }
342 
343 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
BackBufferSharedPtr mpBackBuffer
void SetClipRegion()
void DrawText(const Point &rStartPt, const OUString &rStr, sal_Int32 nIndex=0, sal_Int32 nLen=-1, MetricVector *pVector=nullptr, OUString *pDisplayText=nullptr, const SalLayoutGlyphs *pLayoutCache=nullptr)
void Replace(const Bitmap &rMask, sal_uInt8 rReplaceTransparency)
const ::basegfx::B2DHomMatrix & getTransformation() const
bool isContentFullyOpaque() const
Returns true, if sprite content bitmap is fully opaque.
void setWidth(tools::Long nWidth)
void DrawBitmapEx(const Point &rDestPt, const BitmapEx &rBitmapEx)
constexpr::Color COL_RED(0x80, 0x00, 0x00)
double getAlpha() const
Retrieve current alpha value.
long Long
void DrawPolyLine(const tools::Polygon &rPoly)
::BitmapEx transformBitmap(const BitmapEx &rBitmap, const ::basegfx::B2DHomMatrix &rTransform)
Definition: impltools.cxx:188
::basegfx::B2DRange & calcTransformedRectBounds(::basegfx::B2DRange &outRect, const ::basegfx::B2DRange &inRect, const ::basegfx::B2DHomMatrix &transformation)
Calc the bounding rectangle of a transformed rectangle.
::Point pointFromB2DPoint(const basegfx::B2DPoint &rPoint)
double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
#define min(a, b)
Definition: dx_winstuff.hxx:48
static bool Filter(BitmapEx &rBmpEx, BitmapFilter const &rFilter)
BackBufferSharedPtr mpBackBufferMask
B2IRange fround(const B2DRange &rRange)
::basegfx::B2DPolyPolygon getBoundMarksPolyPolygon(const ::basegfx::B2DRange &rRange)
Retrieve for small bound marks around each corner of the given rectangle.
exports com.sun.star. text
const css::uno::Reference< css::rendering::XPolyPolygon2D > & getClip() const
Retrieve current clip.
void SetLineColor()
void transformUpdated() const
Notifies that caller is again in sync with current transformation.
int i
static bool equalZero(const double &rfVal)
void SetFillColor()
tools::Long Width() const
bool hasTransformChanged() const
Returns true, if transformation has changed since last transformUpdated() call.
const ::basegfx::B2DVector & getSizePixel() const
std::shared_ptr< BackBuffer > BackBufferSharedPtr
Definition: backbuffer.hxx:54
void SetTextColor(const Color &rColor)
void transform(const basegfx::B2DHomMatrix &rMatrix)
::basegfx::B2DPolyPolygon b2DPolyPolygonFromXPolyPolygon2D(const uno::Reference< rendering::XPolyPolygon2D > &xPoly)
bool isIdentity() const
::Size sizeFromB2DSize(const basegfx::B2DVector &rVec)
void DrawBitmap(const Point &rDestPt, const Bitmap &rBitmap)
void SetColor(const Color &)
sal_uInt16 GetBitCount() const
Bitmap GetBitmap(Color aTransparentReplaceColor) const
mutable::canvas::vcltools::VCLObject< BitmapEx > maContent
Cached bitmap for the current sprite content.
virtual ::basegfx::B2DPolyPolygon polyPolygonFromXPolyPolygon2D(css::uno::Reference< css::rendering::XPolyPolygon2D > &xPoly) const override
Called to convert an API polygon to a basegfx polygon.
#define ENSURE_OR_THROW(c, m)
bool mbShowSpriteBounds
When true, line sprite corners in red.
ALIGN_TOP
double getMinY() const
VCL_DLLPUBLIC bool isVCLSkiaEnabled()
sal_uInt32 count() const
unsigned char sal_uInt8
void SetFont(const vcl::Font &rNewFont)
#define SAL_INFO(area, stream)
void redraw(OutputDevice &rOutDev, const ::basegfx::B2DPoint &rPos, bool &bSurfacesDirty, bool bBufferedUpdate) const
Repaint sprite content to associated sprite canvas.
tools::Long Height() const
void DrawPolyPolygon(const tools::PolyPolygon &rPolyPoly)
bool isActive() const
Retrieve current activation state.
void translate(double fX, double fY)
void SetFontHeight(tools::Long nHeight)
void setHeight(tools::Long nHeight)
double getMinX() const
void Push(PushFlags nFlags=PushFlags::ALL)
void init(const css::geometry::RealSize2D &rSpriteSize, const ::canvas::SpriteSurface::Reference &rOwningSpriteCanvas, const BackBufferSharedPtr &rBackBuffer, const BackBufferSharedPtr &rBackBufferMask, bool bShowSpriteBounds)
Late-init the sprite helper.
void SetTextAlign(TextAlign eAlign)
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo