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 "PresenterPaneBorderPainter.hxx"
21 #include "PresenterCanvasHelper.hxx"
22 #include "PresenterGeometryHelper.hxx"
23 #include "PresenterTheme.hxx"
24 #include <com/sun/star/awt/Point.hpp>
25 #include <com/sun/star/awt/Rectangle.hpp>
26 #include <com/sun/star/drawing/XPresenterHelper.hpp>
27 #include <com/sun/star/rendering/CompositeOperation.hpp>
28 #include <com/sun/star/rendering/FillRule.hpp>
29 #include <com/sun/star/rendering/TextDirection.hpp>
30 #include <com/sun/star/rendering/XSpriteCanvas.hpp>
31 #include <map>
32 #include <memory>
33 #include <vector>
34 
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::uno;
37 
38 namespace sdext::presenter {
39 
40 namespace {
41     class BorderSize
42     {
43     public:
44         BorderSize();
45         sal_Int32 mnLeft;
46         sal_Int32 mnTop;
47         sal_Int32 mnRight;
48         sal_Int32 mnBottom;
49     };
50 
51     class RendererPaneStyle
52     {
53     public:
54         RendererPaneStyle (
55             const std::shared_ptr<PresenterTheme>& rpTheme,
56             const OUString& rsStyleName);
57 
58         awt::Rectangle AddBorder (
59             const awt::Rectangle& rBox,
60             drawing::framework::BorderType eBorderType) const;
61         awt::Rectangle RemoveBorder (
62             const awt::Rectangle& rBox,
63             drawing::framework::BorderType eBorderType) const;
64         Reference<rendering::XCanvasFont> GetFont (
65             const Reference<rendering::XCanvas>& rxCanvas) const;
66 
67         SharedBitmapDescriptor mpTopLeft;
68         SharedBitmapDescriptor mpTop;
69         SharedBitmapDescriptor mpTopRight;
70         SharedBitmapDescriptor mpLeft;
71         SharedBitmapDescriptor mpRight;
72         SharedBitmapDescriptor mpBottomLeft;
73         SharedBitmapDescriptor mpBottom;
74         SharedBitmapDescriptor mpBottomRight;
75         SharedBitmapDescriptor mpBottomCallout;
76         SharedBitmapDescriptor mpEmpty;
77         PresenterTheme::SharedFontDescriptor mpFont;
78         sal_Int32 mnFontXOffset;
79         sal_Int32 mnFontYOffset;
80         enum class Anchor { Left, Right, Center };
81         Anchor meFontAnchor;
82         BorderSize maInnerBorderSize;
83         BorderSize maOuterBorderSize;
84         BorderSize maTotalBorderSize;
85     private:
86         void UpdateBorderSizes();
87         SharedBitmapDescriptor GetBitmap(
88             const std::shared_ptr<PresenterTheme>& rpTheme,
89             const OUString& rsStyleName,
90             const OUString& rsBitmapName);
91     };
92 }
93 
94 class  PresenterPaneBorderPainter::Renderer
95 {
96 public:
97     Renderer (
98         const Reference<XComponentContext>& rxContext,
99         const std::shared_ptr<PresenterTheme>& rpTheme);
100 
101     void SetCanvas (const Reference<rendering::XCanvas>& rxCanvas);
102     void PaintBorder (
103         const OUString& rsTitle,
104         const awt::Rectangle& rBBox,
105         const awt::Rectangle& rUpdateBox,
106         const OUString& rsPaneURL);
107     void PaintTitle (
108         const OUString& rsTitle,
109         const std::shared_ptr<RendererPaneStyle>& rpStyle,
110         const awt::Rectangle& rUpdateBox,
111         const awt::Rectangle& rOuterBox,
112         const awt::Rectangle& rInnerBox);
113     void SetupClipping (
114         const awt::Rectangle& rUpdateBox,
115         const awt::Rectangle& rOuterBox,
116         const OUString& rsPaneStyleName);
117     std::shared_ptr<RendererPaneStyle> GetRendererPaneStyle (const OUString& rsResourceURL);
118     void SetCalloutAnchor (
119         const awt::Point& rCalloutAnchor);
120 
121 private:
122     std::shared_ptr<PresenterTheme> mpTheme;
123     typedef ::std::map<OUString, std::shared_ptr<RendererPaneStyle> > RendererPaneStyleContainer;
124     RendererPaneStyleContainer maRendererPaneStyles;
125     Reference<rendering::XCanvas> mxCanvas;
126     Reference<drawing::XPresenterHelper> mxPresenterHelper;
127     css::rendering::ViewState maViewState;
128     Reference<rendering::XPolyPolygon2D> mxViewStateClip;
129     bool mbHasCallout;
130     awt::Point maCalloutAnchor;
131 
132     void PaintBitmap(
133         const awt::Rectangle& rBox,
134         const awt::Rectangle& rUpdateBox,
135         const sal_Int32 nXPosition,
136         const sal_Int32 nYPosition,
137         const sal_Int32 nStartOffset,
138         const sal_Int32 nEndOffset,
139         const bool bExpand,
140         const SharedBitmapDescriptor& rpBitmap);
141 };
142 
143 // ===== PresenterPaneBorderPainter ===========================================
144 
PresenterPaneBorderPainter(const Reference<XComponentContext> & rxContext)145 PresenterPaneBorderPainter::PresenterPaneBorderPainter (
146     const Reference<XComponentContext>& rxContext)
147     : PresenterPaneBorderPainterInterfaceBase(m_aMutex),
148       mxContext(rxContext),
149       mpTheme(),
150       mpRenderer()
151 {
152 }
153 
~PresenterPaneBorderPainter()154 PresenterPaneBorderPainter::~PresenterPaneBorderPainter()
155 {
156 }
157 
158 //----- XPaneBorderPainter ----------------------------------------------------
159 
addBorder(const OUString & rsPaneBorderStyleName,const css::awt::Rectangle & rRectangle,drawing::framework::BorderType eBorderType)160 awt::Rectangle SAL_CALL PresenterPaneBorderPainter::addBorder (
161     const OUString& rsPaneBorderStyleName,
162     const css::awt::Rectangle& rRectangle,
163     drawing::framework::BorderType eBorderType)
164 {
165     ThrowIfDisposed();
166 
167     ProvideTheme();
168 
169     return AddBorder(rsPaneBorderStyleName, rRectangle, eBorderType);
170 }
171 
removeBorder(const OUString & rsPaneBorderStyleName,const css::awt::Rectangle & rRectangle,drawing::framework::BorderType eBorderType)172 awt::Rectangle SAL_CALL PresenterPaneBorderPainter::removeBorder (
173     const OUString& rsPaneBorderStyleName,
174     const css::awt::Rectangle& rRectangle,
175     drawing::framework::BorderType eBorderType)
176 {
177     ThrowIfDisposed();
178 
179     ProvideTheme();
180 
181     return RemoveBorder(rsPaneBorderStyleName, rRectangle, eBorderType);
182 }
183 
paintBorder(const OUString & rsPaneBorderStyleName,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas,const css::awt::Rectangle & rOuterBorderRectangle,const css::awt::Rectangle & rRepaintArea,const OUString & rsTitle)184 void SAL_CALL PresenterPaneBorderPainter::paintBorder (
185     const OUString& rsPaneBorderStyleName,
186     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
187     const css::awt::Rectangle& rOuterBorderRectangle,
188     const css::awt::Rectangle& rRepaintArea,
189     const OUString& rsTitle)
190 {
191     ThrowIfDisposed();
192 
193     // Early reject paints completely outside the repaint area.
194     if (rRepaintArea.X >= rOuterBorderRectangle.X+rOuterBorderRectangle.Width
195         || rRepaintArea.Y >= rOuterBorderRectangle.Y+rOuterBorderRectangle.Height
196         || rRepaintArea.X+rRepaintArea.Width <= rOuterBorderRectangle.X
197         || rRepaintArea.Y+rRepaintArea.Height <= rOuterBorderRectangle.Y)
198     {
199         return;
200     }
201     ProvideTheme(rxCanvas);
202 
203     if (mpRenderer == nullptr)
204         return;
205 
206     mpRenderer->SetCanvas(rxCanvas);
207     mpRenderer->SetupClipping(
208         rRepaintArea,
209         rOuterBorderRectangle,
210         rsPaneBorderStyleName);
211     mpRenderer->PaintBorder(
212         rsTitle,
213         rOuterBorderRectangle,
214         rRepaintArea,
215         rsPaneBorderStyleName);
216 }
217 
paintBorderWithCallout(const OUString & rsPaneBorderStyleName,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas,const css::awt::Rectangle & rOuterBorderRectangle,const css::awt::Rectangle & rRepaintArea,const OUString & rsTitle,const css::awt::Point & rCalloutAnchor)218 void SAL_CALL PresenterPaneBorderPainter::paintBorderWithCallout (
219     const OUString& rsPaneBorderStyleName,
220     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
221     const css::awt::Rectangle& rOuterBorderRectangle,
222     const css::awt::Rectangle& rRepaintArea,
223     const OUString& rsTitle,
224     const css::awt::Point& rCalloutAnchor)
225 {
226     ThrowIfDisposed();
227 
228     // Early reject paints completely outside the repaint area.
229     if (rRepaintArea.X >= rOuterBorderRectangle.X+rOuterBorderRectangle.Width
230         || rRepaintArea.Y >= rOuterBorderRectangle.Y+rOuterBorderRectangle.Height
231         || rRepaintArea.X+rRepaintArea.Width <= rOuterBorderRectangle.X
232         || rRepaintArea.Y+rRepaintArea.Height <= rOuterBorderRectangle.Y)
233     {
234         return;
235     }
236     ProvideTheme(rxCanvas);
237 
238     if (mpRenderer == nullptr)
239         return;
240 
241     mpRenderer->SetCanvas(rxCanvas);
242     mpRenderer->SetupClipping(
243         rRepaintArea,
244         rOuterBorderRectangle,
245         rsPaneBorderStyleName);
246     mpRenderer->SetCalloutAnchor(rCalloutAnchor);
247     mpRenderer->PaintBorder(
248         rsTitle,
249         rOuterBorderRectangle,
250         rRepaintArea,
251         rsPaneBorderStyleName);
252 }
253 
getCalloutOffset(const OUString & rsPaneBorderStyleName)254 awt::Point SAL_CALL PresenterPaneBorderPainter::getCalloutOffset (
255     const OUString& rsPaneBorderStyleName)
256 {
257     ThrowIfDisposed();
258     ProvideTheme();
259     if (mpRenderer != nullptr)
260     {
261         const std::shared_ptr<RendererPaneStyle> pRendererPaneStyle(
262             mpRenderer->GetRendererPaneStyle(rsPaneBorderStyleName));
263         if (pRendererPaneStyle != nullptr && pRendererPaneStyle->mpBottomCallout)
264         {
265             return awt::Point (
266                 0,
267                 pRendererPaneStyle->mpBottomCallout->mnHeight
268                     - pRendererPaneStyle->mpBottomCallout->mnYHotSpot);
269         }
270     }
271 
272     return awt::Point(0,0);
273 }
274 
275 
ProvideTheme(const Reference<rendering::XCanvas> & rxCanvas)276 bool PresenterPaneBorderPainter::ProvideTheme (const Reference<rendering::XCanvas>& rxCanvas)
277 {
278     bool bModified (false);
279 
280     if ( ! mxContext.is())
281         return false;
282 
283     if (mpTheme != nullptr)
284     {
285         // Check if the theme already has a canvas.
286         if ( ! mpTheme->HasCanvas())
287         {
288             mpTheme->ProvideCanvas(rxCanvas);
289             bModified = true;
290         }
291     }
292     else
293     {
294         mpTheme = std::make_shared<PresenterTheme>(mxContext, rxCanvas);
295         bModified = true;
296     }
297 
298     if (bModified)
299     {
300         if (mpRenderer == nullptr)
301             mpRenderer.reset(new Renderer(mxContext, mpTheme));
302         else
303             mpRenderer->SetCanvas(rxCanvas);
304     }
305 
306     return bModified;
307 }
308 
ProvideTheme()309 void PresenterPaneBorderPainter::ProvideTheme()
310 {
311     if (mpTheme == nullptr)
312     {
313         // Create a theme without bitmaps (no canvas => no bitmaps).
314         ProvideTheme(nullptr);
315     }
316         // When there already is a theme then without a canvas we can not
317         // add anything new.
318 }
319 
SetTheme(const std::shared_ptr<PresenterTheme> & rpTheme)320 void PresenterPaneBorderPainter::SetTheme (const std::shared_ptr<PresenterTheme>& rpTheme)
321 {
322     mpTheme = rpTheme;
323     if (mpRenderer == nullptr)
324         mpRenderer.reset(new Renderer(mxContext, mpTheme));
325 }
326 
AddBorder(const OUString & rsPaneURL,const awt::Rectangle & rInnerBox,const css::drawing::framework::BorderType eBorderType) const327 awt::Rectangle PresenterPaneBorderPainter::AddBorder (
328     const OUString& rsPaneURL,
329     const awt::Rectangle& rInnerBox,
330     const css::drawing::framework::BorderType eBorderType) const
331 {
332     if (mpRenderer != nullptr)
333     {
334         const std::shared_ptr<RendererPaneStyle> pRendererPaneStyle(mpRenderer->GetRendererPaneStyle(rsPaneURL));
335         if (pRendererPaneStyle != nullptr)
336             return pRendererPaneStyle->AddBorder(rInnerBox, eBorderType);
337     }
338     return rInnerBox;
339 }
340 
RemoveBorder(const OUString & rsPaneURL,const css::awt::Rectangle & rOuterBox,const css::drawing::framework::BorderType eBorderType) const341 awt::Rectangle PresenterPaneBorderPainter::RemoveBorder (
342     const OUString& rsPaneURL,
343     const css::awt::Rectangle& rOuterBox,
344     const css::drawing::framework::BorderType eBorderType) const
345 {
346     if (mpRenderer != nullptr)
347     {
348         const std::shared_ptr<RendererPaneStyle> pRendererPaneStyle(mpRenderer->GetRendererPaneStyle(rsPaneURL));
349         if (pRendererPaneStyle != nullptr)
350             return pRendererPaneStyle->RemoveBorder(rOuterBox, eBorderType);
351     }
352     return rOuterBox;
353 }
354 
ThrowIfDisposed() const355 void PresenterPaneBorderPainter::ThrowIfDisposed() const
356 {
357     if (rBHelper.bDisposed || rBHelper.bInDispose)
358     {
359         throw lang::DisposedException (
360             "PresenterPaneBorderPainter object has already been disposed",
361             const_cast<uno::XWeak*>(static_cast<const uno::XWeak*>(this)));
362     }
363 }
364 
365 //===== PresenterPaneBorderPainter::Renderer =====================================
366 
Renderer(const Reference<XComponentContext> & rxContext,const std::shared_ptr<PresenterTheme> & rpTheme)367 PresenterPaneBorderPainter::Renderer::Renderer (
368     const Reference<XComponentContext>& rxContext,
369     const std::shared_ptr<PresenterTheme>& rpTheme)
370     : mpTheme(rpTheme),
371       maRendererPaneStyles(),
372       mxCanvas(),
373       mxPresenterHelper(),
374       maViewState(geometry::AffineMatrix2D(1,0,0, 0,1,0), nullptr),
375       mxViewStateClip(),
376       mbHasCallout(false),
377       maCalloutAnchor()
378 {
379     Reference<lang::XMultiComponentFactory> xFactory (rxContext->getServiceManager());
380     if (xFactory.is())
381     {
382         mxPresenterHelper.set(
383             xFactory->createInstanceWithContext(
384                 "com.sun.star.comp.Draw.PresenterHelper",
385                 rxContext),
386             UNO_QUERY_THROW);
387     }
388 }
389 
SetCanvas(const Reference<rendering::XCanvas> & rxCanvas)390 void PresenterPaneBorderPainter::Renderer::SetCanvas (const Reference<rendering::XCanvas>& rxCanvas)
391 {
392     if (mxCanvas != rxCanvas)
393     {
394         mxCanvas = rxCanvas;
395     }
396 }
397 
PaintBorder(const OUString & rsTitle,const awt::Rectangle & rBBox,const awt::Rectangle & rUpdateBox,const OUString & rsPaneURL)398 void PresenterPaneBorderPainter::Renderer::PaintBorder (
399     const OUString& rsTitle,
400     const awt::Rectangle& rBBox,
401     const awt::Rectangle& rUpdateBox,
402     const OUString& rsPaneURL)
403 {
404     if ( ! mxCanvas.is())
405         return;
406 
407     // Create the outer and inner border of the, ahm, border.
408     std::shared_ptr<RendererPaneStyle> pStyle (GetRendererPaneStyle(rsPaneURL));
409     if (pStyle == nullptr)
410         return;
411 
412     awt::Rectangle aOuterBox (rBBox);
413     awt::Rectangle aCenterBox (
414         pStyle->RemoveBorder(aOuterBox, drawing::framework::BorderType_OUTER_BORDER));
415     awt::Rectangle aInnerBox (
416         pStyle->RemoveBorder(aOuterBox, drawing::framework::BorderType_TOTAL_BORDER));
417 
418     // Prepare references for all used bitmaps.
419     SharedBitmapDescriptor pTop (pStyle->mpTop);
420     SharedBitmapDescriptor pTopLeft (pStyle->mpTopLeft);
421     SharedBitmapDescriptor pTopRight (pStyle->mpTopRight);
422     SharedBitmapDescriptor pLeft (pStyle->mpLeft);
423     SharedBitmapDescriptor pRight (pStyle->mpRight);
424     SharedBitmapDescriptor pBottomLeft (pStyle->mpBottomLeft);
425     SharedBitmapDescriptor pBottomRight (pStyle->mpBottomRight);
426     SharedBitmapDescriptor pBottom (pStyle->mpBottom);
427 
428     // Paint the sides.
429     PaintBitmap(aCenterBox, rUpdateBox, 0,-1,
430         pTopLeft->mnXOffset, pTopRight->mnXOffset, true, pTop);
431     PaintBitmap(aCenterBox, rUpdateBox, -1,0,
432         pTopLeft->mnYOffset, pBottomLeft->mnYOffset, true, pLeft);
433     PaintBitmap(aCenterBox, rUpdateBox, +1,0,
434         pTopRight->mnYOffset, pBottomRight->mnYOffset, true, pRight);
435     if (mbHasCallout && pStyle->mpBottomCallout->GetNormalBitmap().is())
436     {
437         const sal_Int32 nCalloutWidth (pStyle->mpBottomCallout->mnWidth);
438         sal_Int32 nCalloutX (maCalloutAnchor.X - pStyle->mpBottomCallout->mnXHotSpot
439             - (aCenterBox.X - aOuterBox.X));
440         if (nCalloutX < pBottomLeft->mnXOffset + aCenterBox.X)
441             nCalloutX = pBottomLeft->mnXOffset + aCenterBox.X;
442         if (nCalloutX > pBottomRight->mnXOffset + aCenterBox.X + aCenterBox.Width)
443             nCalloutX = pBottomRight->mnXOffset + aCenterBox.X + aCenterBox.Width;
444         // Paint bottom callout.
445         PaintBitmap(aCenterBox, rUpdateBox, 0,+1, nCalloutX,0, false, pStyle->mpBottomCallout);
446         // Paint regular bottom bitmap left and right.
447         PaintBitmap(aCenterBox, rUpdateBox, 0,+1,
448             pBottomLeft->mnXOffset, nCalloutX-aCenterBox.Width, true, pBottom);
449         PaintBitmap(aCenterBox, rUpdateBox, 0,+1,
450             nCalloutX+nCalloutWidth, pBottomRight->mnXOffset, true, pBottom);
451     }
452     else
453     {
454         // Stretch the bottom bitmap over the full width.
455         PaintBitmap(aCenterBox, rUpdateBox, 0,+1,
456             pBottomLeft->mnXOffset, pBottomRight->mnXOffset, true, pBottom);
457     }
458 
459     // Paint the corners.
460     PaintBitmap(aCenterBox, rUpdateBox, -1,-1, 0,0, false, pTopLeft);
461     PaintBitmap(aCenterBox, rUpdateBox, +1,-1, 0,0, false, pTopRight);
462     PaintBitmap(aCenterBox, rUpdateBox, -1,+1, 0,0, false, pBottomLeft);
463     PaintBitmap(aCenterBox, rUpdateBox, +1,+1, 0,0, false, pBottomRight);
464 
465     // Paint the title.
466     PaintTitle(rsTitle, pStyle, rUpdateBox, aOuterBox, aInnerBox);
467 
468     // In a double buffering environment request to make the changes visible.
469     Reference<rendering::XSpriteCanvas> xSpriteCanvas (mxCanvas, UNO_QUERY);
470     if (xSpriteCanvas.is())
471         xSpriteCanvas->updateScreen(false);
472 }
473 
PaintTitle(const OUString & rsTitle,const std::shared_ptr<RendererPaneStyle> & rpStyle,const awt::Rectangle & rUpdateBox,const awt::Rectangle & rOuterBox,const awt::Rectangle & rInnerBox)474 void PresenterPaneBorderPainter::Renderer::PaintTitle (
475     const OUString& rsTitle,
476     const std::shared_ptr<RendererPaneStyle>& rpStyle,
477     const awt::Rectangle& rUpdateBox,
478     const awt::Rectangle& rOuterBox,
479     const awt::Rectangle& rInnerBox)
480 {
481     if ( ! mxCanvas.is())
482         return;
483 
484     if (rsTitle.isEmpty())
485         return;
486 
487     Reference<rendering::XCanvasFont> xFont (rpStyle->GetFont(mxCanvas));
488     if ( ! xFont.is())
489         return;
490 
491     rendering::StringContext aContext (
492         rsTitle,
493         0,
494         rsTitle.getLength());
495     Reference<rendering::XTextLayout> xLayout (xFont->createTextLayout(
496         aContext,
497         rendering::TextDirection::WEAK_LEFT_TO_RIGHT,
498         0));
499     if ( ! xLayout.is())
500         return;
501 
502     /// this is responsible of the texts above the slide windows
503     geometry::RealRectangle2D aBox (xLayout->queryTextBounds());
504     const double nTextHeight = aBox.Y2 - aBox.Y1;
505     const double nTextWidth = aBox.X1 + aBox.X2;
506     const sal_Int32 nTitleBarHeight = rInnerBox.Y - rOuterBox.Y - 1;
507     double nY = rOuterBox.Y + (nTitleBarHeight - nTextHeight) / 2 - aBox.Y1;
508     if (nY >= rInnerBox.Y)
509         nY = rInnerBox.Y - 1;
510     double nX;
511     switch (rpStyle->meFontAnchor)
512     {
513     case RendererPaneStyle::Anchor::Left:
514         nX = rInnerBox.X;
515         break;
516     case RendererPaneStyle::Anchor::Right:
517         nX = rInnerBox.X + rInnerBox.Width - nTextWidth;
518         break;
519     default: // RendererPaneStyle::Anchor::Center
520         nX = rInnerBox.X + (rInnerBox.Width - nTextWidth)/2;
521         break;
522     }
523     nX += rpStyle->mnFontXOffset;
524     nY += rpStyle->mnFontYOffset;
525 
526     if (rUpdateBox.X >= nX+nTextWidth
527         || rUpdateBox.Y >= nY+nTextHeight
528         || rUpdateBox.X+rUpdateBox.Width <= nX
529         || rUpdateBox.Y+rUpdateBox.Height <= nY)
530     {
531         return;
532     }
533 
534     rendering::RenderState aRenderState(
535         geometry::AffineMatrix2D(1,0,nX, 0,1,nY),
536         nullptr,
537         Sequence<double>(4),
538         rendering::CompositeOperation::SOURCE);
539 
540     PresenterCanvasHelper::SetDeviceColor(
541             aRenderState,
542             rpStyle->mpFont->mnColor);
543 
544     mxCanvas->drawTextLayout (
545             xLayout,
546             maViewState,
547             aRenderState);
548 }
549 
550 std::shared_ptr<RendererPaneStyle>
GetRendererPaneStyle(const OUString & rsResourceURL)551     PresenterPaneBorderPainter::Renderer::GetRendererPaneStyle (const OUString& rsResourceURL)
552 {
553     OSL_ASSERT(mpTheme != nullptr);
554 
555     RendererPaneStyleContainer::const_iterator iStyle (maRendererPaneStyles.find(rsResourceURL));
556     if (iStyle == maRendererPaneStyles.end())
557     {
558         OUString sPaneStyleName ("DefaultRendererPaneStyle");
559 
560         // Get pane layout name for resource URL.
561         const OUString sStyleName (mpTheme->GetStyleName(rsResourceURL));
562         if (!sStyleName.isEmpty())
563             sPaneStyleName = sStyleName;
564 
565         // Create a new pane style object and initialize it with bitmaps.
566         auto pStyle = std::make_shared<RendererPaneStyle>(mpTheme,sPaneStyleName);
567         iStyle = maRendererPaneStyles.emplace(rsResourceURL, pStyle).first;
568     }
569     if (iStyle != maRendererPaneStyles.end())
570         return iStyle->second;
571     else
572         return std::shared_ptr<RendererPaneStyle>();
573 }
574 
SetCalloutAnchor(const awt::Point & rCalloutAnchor)575 void PresenterPaneBorderPainter::Renderer::SetCalloutAnchor (
576     const awt::Point& rCalloutAnchor)
577 {
578     mbHasCallout = true;
579     maCalloutAnchor = rCalloutAnchor;
580 }
581 
PaintBitmap(const awt::Rectangle & rBox,const awt::Rectangle & rUpdateBox,const sal_Int32 nXPosition,const sal_Int32 nYPosition,const sal_Int32 nStartOffset,const sal_Int32 nEndOffset,const bool bExpand,const SharedBitmapDescriptor & rpBitmap)582 void PresenterPaneBorderPainter::Renderer::PaintBitmap(
583     const awt::Rectangle& rBox,
584     const awt::Rectangle& rUpdateBox,
585     const sal_Int32 nXPosition,
586     const sal_Int32 nYPosition,
587     const sal_Int32 nStartOffset,
588     const sal_Int32 nEndOffset,
589     const bool bExpand,
590     const SharedBitmapDescriptor& rpBitmap)
591 {
592     bool bUseCanvas (mxCanvas.is());
593     if ( ! bUseCanvas)
594         return;
595 
596     if (rpBitmap->mnWidth<=0 || rpBitmap->mnHeight<=0)
597         return;
598 
599     Reference<rendering::XBitmap> xBitmap = rpBitmap->GetNormalBitmap();
600     if ( ! xBitmap.is())
601         return;
602 
603     // Calculate position, and for side bitmaps, the size.
604     sal_Int32 nX = 0;
605     sal_Int32 nY = 0;
606     sal_Int32 nW = rpBitmap->mnWidth;
607     sal_Int32 nH = rpBitmap->mnHeight;
608     if (nXPosition < 0)
609     {
610         nX = rBox.X - rpBitmap->mnWidth + rpBitmap->mnXOffset;
611     }
612     else if (nXPosition > 0)
613     {
614         nX = rBox.X + rBox.Width + rpBitmap->mnXOffset;
615     }
616     else
617     {
618         nX = rBox.X + nStartOffset;
619         if (bExpand)
620             nW = rBox.Width - nStartOffset + nEndOffset;
621     }
622 
623     if (nYPosition < 0)
624     {
625         nY = rBox.Y - rpBitmap->mnHeight + rpBitmap->mnYOffset;
626     }
627     else if (nYPosition > 0)
628     {
629         nY = rBox.Y + rBox.Height + rpBitmap->mnYOffset;
630     }
631     else
632     {
633         nY = rBox.Y + nStartOffset;
634         if (bExpand)
635             nH = rBox.Height - nStartOffset + nEndOffset;
636     }
637 
638     // Do not paint when bitmap area does not intersect with update box.
639     if (nX >= rUpdateBox.X + rUpdateBox.Width
640         || nX+nW <= rUpdateBox.X
641         || nY >= rUpdateBox.Y + rUpdateBox.Height
642         || nY+nH <= rUpdateBox.Y)
643     {
644         return;
645     }
646 
647     /*
648     Reference<rendering::XBitmap> xMaskedBitmap (
649         PresenterBitmapHelper::FillMaskedWithColor (
650             mxCanvas,
651             Reference<rendering::XIntegerBitmap>(xBitmap, UNO_QUERY),
652             rBitmap.mxMaskBitmap,
653             0x00ff0000,
654             rBackgroundBitmap.maReplacementColor));
655     if (xMaskedBitmap.is())
656         xBitmap = xMaskedBitmap;
657     else if (rBitmap.mxMaskBitmap.is() && mxPresenterHelper.is())
658     {
659         const static sal_Int32 nOutsideMaskColor (0x00ff0000);
660         Reference<rendering::XIntegerBitmap> xMask (
661             mxPresenterHelper->createMask(
662                 mxCanvas,
663                 rBitmap.mxMaskBitmap,
664                 nOutsideMaskColor,
665                 false));
666         xBitmap = mxPresenterHelper->applyBitmapMaskWithColor(
667             mxCanvas,
668             Reference<rendering::XIntegerBitmap>(xBitmap, UNO_QUERY),
669             xMask,
670             rBackgroundBitmap.maReplacementColor);
671     }
672     */
673     rendering::RenderState aRenderState (
674         geometry::AffineMatrix2D(
675             double(nW)/rpBitmap->mnWidth, 0, nX,
676             0, double(nH)/rpBitmap->mnHeight, nY),
677         nullptr,
678         Sequence<double>(4),
679         rendering::CompositeOperation::OVER);
680 
681     if (xBitmap.is())
682         mxCanvas->drawBitmap(
683             xBitmap,
684             maViewState,
685             aRenderState);
686 }
687 
SetupClipping(const awt::Rectangle & rUpdateBox,const awt::Rectangle & rOuterBox,const OUString & rsPaneStyleName)688 void PresenterPaneBorderPainter::Renderer::SetupClipping (
689     const awt::Rectangle& rUpdateBox,
690     const awt::Rectangle& rOuterBox,
691     const OUString& rsPaneStyleName)
692 {
693     mxViewStateClip = nullptr;
694     maViewState.Clip = nullptr;
695 
696     if ( ! mxCanvas.is())
697         return;
698 
699     std::shared_ptr<RendererPaneStyle> pStyle (GetRendererPaneStyle(rsPaneStyleName));
700     if (pStyle == nullptr)
701     {
702         mxViewStateClip = PresenterGeometryHelper::CreatePolygon(
703             rUpdateBox,
704             mxCanvas->getDevice());
705     }
706     else
707     {
708         awt::Rectangle aInnerBox (
709             pStyle->RemoveBorder(rOuterBox, drawing::framework::BorderType_TOTAL_BORDER));
710         ::std::vector<awt::Rectangle> aRectangles;
711         aRectangles.push_back(PresenterGeometryHelper::Intersection(rUpdateBox, rOuterBox));
712         aRectangles.push_back(PresenterGeometryHelper::Intersection(rUpdateBox, aInnerBox));
713         mxViewStateClip = PresenterGeometryHelper::CreatePolygon(
714             aRectangles,
715             mxCanvas->getDevice());
716         if (mxViewStateClip.is())
717             mxViewStateClip->setFillRule(rendering::FillRule_EVEN_ODD);
718     }
719     maViewState.Clip = mxViewStateClip;
720 }
721 
722 namespace {
723 
724 //===== BorderSize ============================================================
725 
BorderSize()726 BorderSize::BorderSize()
727     : mnLeft(0),
728       mnTop(0),
729       mnRight(0),
730       mnBottom(0)
731 {
732 }
733 
734 //===== RendererPaneStyle  ============================================================
735 
RendererPaneStyle(const std::shared_ptr<PresenterTheme> & rpTheme,const OUString & rsStyleName)736 RendererPaneStyle::RendererPaneStyle (
737     const std::shared_ptr<PresenterTheme>& rpTheme,
738     const OUString& rsStyleName)
739     : mpTopLeft(),
740       mpTop(),
741       mpTopRight(),
742       mpLeft(),
743       mpRight(),
744       mpBottomLeft(),
745       mpBottom(),
746       mpBottomRight(),
747       mpBottomCallout(),
748       mpEmpty(std::make_shared<PresenterBitmapDescriptor>()),
749       mpFont(),
750       mnFontXOffset(0),
751       mnFontYOffset(0),
752       meFontAnchor(Anchor::Center),
753       maInnerBorderSize(),
754       maOuterBorderSize(),
755       maTotalBorderSize()
756 {
757     if (rpTheme == nullptr)
758         return;
759 
760     mpTopLeft = GetBitmap(rpTheme, rsStyleName, "TopLeft");
761     mpTop = GetBitmap(rpTheme, rsStyleName, "Top");
762     mpTopRight = GetBitmap(rpTheme, rsStyleName, "TopRight");
763     mpLeft = GetBitmap(rpTheme, rsStyleName,"Left");
764     mpRight = GetBitmap(rpTheme, rsStyleName, "Right");
765     mpBottomLeft = GetBitmap(rpTheme, rsStyleName, "BottomLeft");
766     mpBottom = GetBitmap(rpTheme, rsStyleName, "Bottom");
767     mpBottomRight = GetBitmap(rpTheme, rsStyleName, "BottomRight");
768     mpBottomCallout = GetBitmap(rpTheme, rsStyleName, "BottomCallout");
769 
770     // Get font description.
771     mpFont = rpTheme->GetFont(rsStyleName);
772 
773     OUString sAnchor ("Left");
774     if (mpFont)
775     {
776         sAnchor = mpFont->msAnchor;
777         mnFontXOffset = mpFont->mnXOffset;
778         mnFontYOffset = mpFont->mnYOffset;
779     }
780 
781     if ( sAnchor == "Left" )
782         meFontAnchor = Anchor::Left;
783     else if ( sAnchor == "Right" )
784         meFontAnchor = Anchor::Right;
785     else
786         meFontAnchor = Anchor::Center;
787 
788     // Get border sizes.
789     try
790     {
791         ::std::vector<sal_Int32> aInnerBorder (rpTheme->GetBorderSize(rsStyleName, false));
792         OSL_ASSERT(aInnerBorder.size()==4);
793         maInnerBorderSize.mnLeft = aInnerBorder[0];
794         maInnerBorderSize.mnTop = aInnerBorder[1];
795         maInnerBorderSize.mnRight = aInnerBorder[2];
796         maInnerBorderSize.mnBottom = aInnerBorder[3];
797 
798         ::std::vector<sal_Int32> aOuterBorder (rpTheme->GetBorderSize(rsStyleName, true));
799         OSL_ASSERT(aOuterBorder.size()==4);
800         maOuterBorderSize.mnLeft = aOuterBorder[0];
801         maOuterBorderSize.mnTop = aOuterBorder[1];
802         maOuterBorderSize.mnRight = aOuterBorder[2];
803         maOuterBorderSize.mnBottom = aOuterBorder[3];
804     }
805     catch(beans::UnknownPropertyException&)
806     {
807         OSL_ASSERT(false);
808     }
809 
810     UpdateBorderSizes();
811 }
812 
AddBorder(const awt::Rectangle & rBox,const drawing::framework::BorderType eBorderType) const813 awt::Rectangle RendererPaneStyle::AddBorder (
814     const awt::Rectangle& rBox,
815     const drawing::framework::BorderType eBorderType) const
816 {
817     const BorderSize* pBorderSize = nullptr;
818     switch (eBorderType)
819     {
820         case drawing::framework::BorderType_INNER_BORDER:
821             pBorderSize = &maInnerBorderSize;
822             break;
823         case drawing::framework::BorderType_OUTER_BORDER:
824             pBorderSize = &maOuterBorderSize;
825             break;
826         case drawing::framework::BorderType_TOTAL_BORDER:
827             pBorderSize = &maTotalBorderSize;
828             break;
829         default:
830             return rBox;
831     }
832     return awt::Rectangle (
833         rBox.X - pBorderSize->mnLeft,
834         rBox.Y - pBorderSize->mnTop,
835         rBox.Width + pBorderSize->mnLeft + pBorderSize->mnRight,
836         rBox.Height + pBorderSize->mnTop + pBorderSize->mnBottom);
837 }
838 
RemoveBorder(const awt::Rectangle & rBox,const css::drawing::framework::BorderType eBorderType) const839 awt::Rectangle RendererPaneStyle::RemoveBorder (
840     const awt::Rectangle& rBox,
841     const css::drawing::framework::BorderType eBorderType) const
842 {
843     const BorderSize* pBorderSize = nullptr;
844     switch (eBorderType)
845     {
846         case drawing::framework::BorderType_INNER_BORDER:
847             pBorderSize = &maInnerBorderSize;
848             break;
849         case drawing::framework::BorderType_OUTER_BORDER:
850             pBorderSize = &maOuterBorderSize;
851             break;
852         case drawing::framework::BorderType_TOTAL_BORDER:
853             pBorderSize = &maTotalBorderSize;
854             break;
855         default:
856             return rBox;
857     }
858     return awt::Rectangle (
859         rBox.X + pBorderSize->mnLeft,
860         rBox.Y + pBorderSize->mnTop,
861         rBox.Width - pBorderSize->mnLeft - pBorderSize->mnRight,
862         rBox.Height - pBorderSize->mnTop - pBorderSize->mnBottom);
863 }
864 
GetFont(const Reference<rendering::XCanvas> & rxCanvas) const865 Reference<rendering::XCanvasFont> RendererPaneStyle::GetFont (
866     const Reference<rendering::XCanvas>& rxCanvas) const
867 {
868     if (mpFont)
869     {
870         mpFont->PrepareFont(rxCanvas);
871         return mpFont->mxFont;
872     }
873     return Reference<rendering::XCanvasFont>();
874 }
875 
UpdateBorderSizes()876 void RendererPaneStyle::UpdateBorderSizes()
877 {
878     maTotalBorderSize.mnLeft = maInnerBorderSize.mnLeft + maOuterBorderSize.mnLeft;
879     maTotalBorderSize.mnTop = maInnerBorderSize.mnTop + maOuterBorderSize.mnTop;
880     maTotalBorderSize.mnRight = maInnerBorderSize.mnRight + maOuterBorderSize.mnRight;
881     maTotalBorderSize.mnBottom = maInnerBorderSize.mnBottom + maOuterBorderSize.mnBottom;
882 }
883 
GetBitmap(const std::shared_ptr<PresenterTheme> & rpTheme,const OUString & rsStyleName,const OUString & rsBitmapName)884 SharedBitmapDescriptor RendererPaneStyle::GetBitmap(
885     const std::shared_ptr<PresenterTheme>& rpTheme,
886     const OUString& rsStyleName,
887     const OUString& rsBitmapName)
888 {
889     SharedBitmapDescriptor pDescriptor (rpTheme->GetBitmap(rsStyleName, rsBitmapName));
890     if (pDescriptor)
891         return pDescriptor;
892     else
893         return mpEmpty;
894 }
895 
896 } // end of anonymous namespace
897 
898 } // end of namespace ::sd::presenter
899 
900 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
901