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 "PresenterCanvasHelper.hxx"
21 
22 #include "PresenterGeometryHelper.hxx"
23 #include <com/sun/star/rendering/CompositeOperation.hpp>
24 #include <osl/diagnose.h>
25 
26 using namespace ::com::sun::star;
27 using namespace ::com::sun::star::uno;
28 
29 namespace sdext::presenter {
30 
PresenterCanvasHelper()31 PresenterCanvasHelper::PresenterCanvasHelper()
32     : maDefaultViewState(
33         geometry::AffineMatrix2D(1,0,0, 0,1,0),
34         nullptr),
35       maDefaultRenderState(
36           geometry::AffineMatrix2D(1,0,0, 0,1,0),
37           nullptr,
38           Sequence<double>(4),
39           rendering::CompositeOperation::SOURCE)
40 {
41 }
42 
~PresenterCanvasHelper()43 PresenterCanvasHelper::~PresenterCanvasHelper()
44 {
45 }
46 
Paint(const SharedBitmapDescriptor & rpBitmap,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas,const css::awt::Rectangle & rRepaintBox,const css::awt::Rectangle & rOuterBoundingBox,const css::awt::Rectangle & rContentBoundingBox) const47 void PresenterCanvasHelper::Paint (
48     const SharedBitmapDescriptor& rpBitmap,
49     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
50     const css::awt::Rectangle& rRepaintBox,
51     const css::awt::Rectangle& rOuterBoundingBox,
52     const css::awt::Rectangle& rContentBoundingBox) const
53 {
54     PaintRectangle(rpBitmap,rxCanvas,rRepaintBox,rOuterBoundingBox,rContentBoundingBox,
55         maDefaultViewState, maDefaultRenderState);
56 }
57 
PaintRectangle(const SharedBitmapDescriptor & rpBitmap,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas,const css::awt::Rectangle & rRepaintBox,const css::awt::Rectangle & rOuterBoundingBox,const css::awt::Rectangle & rContentBoundingBox,const css::rendering::ViewState & rDefaultViewState,const css::rendering::RenderState & rDefaultRenderState)58 void PresenterCanvasHelper::PaintRectangle (
59     const SharedBitmapDescriptor& rpBitmap,
60     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
61     const css::awt::Rectangle& rRepaintBox,
62     const css::awt::Rectangle& rOuterBoundingBox,
63     const css::awt::Rectangle& rContentBoundingBox,
64     const css::rendering::ViewState& rDefaultViewState,
65     const css::rendering::RenderState& rDefaultRenderState)
66 {
67     if (!rpBitmap)
68         return;
69 
70     if ( ! rxCanvas.is() || ! rxCanvas->getDevice().is())
71         return;
72 
73     // Create a clip polypolygon that has the content box as hole.
74     ::std::vector<awt::Rectangle> aRectangles;
75     aRectangles.reserve(2);
76     aRectangles.push_back(
77         PresenterGeometryHelper::Intersection(rRepaintBox, rOuterBoundingBox));
78     if (rContentBoundingBox.Width > 0 && rContentBoundingBox.Height > 0)
79         aRectangles.push_back(
80             PresenterGeometryHelper::Intersection(rRepaintBox, rContentBoundingBox));
81     Reference<rendering::XPolyPolygon2D> xPolyPolygon (
82         PresenterGeometryHelper::CreatePolygon(
83             aRectangles,
84             rxCanvas->getDevice()));
85     if ( ! xPolyPolygon.is())
86         return;
87     xPolyPolygon->setFillRule(rendering::FillRule_EVEN_ODD);
88 
89     if (rpBitmap->GetNormalBitmap().is())
90     {
91         if (rpBitmap->meHorizontalTexturingMode == PresenterBitmapDescriptor::Repeat
92             || rpBitmap->meVerticalTexturingMode == PresenterBitmapDescriptor::Repeat)
93         {
94             PaintTiledBitmap(
95                 rpBitmap->GetNormalBitmap(),
96                 rxCanvas,
97                 rRepaintBox,
98                 xPolyPolygon,
99                 rContentBoundingBox,
100                 rDefaultViewState,
101                 rDefaultRenderState);
102         }
103         else
104         {
105             PaintBitmap(
106                 rpBitmap->GetNormalBitmap(),
107                 awt::Point(rOuterBoundingBox.X, rOuterBoundingBox.Y),
108                 rxCanvas,
109                 rRepaintBox,
110                 xPolyPolygon,
111                 rDefaultViewState,
112                 rDefaultRenderState);
113         }
114     }
115     else
116     {
117         PaintColor(
118             rpBitmap->maReplacementColor,
119             rxCanvas,
120             rRepaintBox,
121             xPolyPolygon,
122             rDefaultViewState,
123             rDefaultRenderState);
124     }
125 }
126 
PaintTiledBitmap(const css::uno::Reference<css::rendering::XBitmap> & rxTexture,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas,const css::awt::Rectangle & rRepaintBox,const css::uno::Reference<css::rendering::XPolyPolygon2D> & rxPolygon,const css::awt::Rectangle & rHole,const css::rendering::ViewState & rDefaultViewState,const css::rendering::RenderState & rDefaultRenderState)127 void PresenterCanvasHelper::PaintTiledBitmap (
128     const css::uno::Reference<css::rendering::XBitmap>& rxTexture,
129     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
130     const css::awt::Rectangle& rRepaintBox,
131     const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxPolygon,
132     const css::awt::Rectangle& rHole,
133     const css::rendering::ViewState& rDefaultViewState,
134     const css::rendering::RenderState& rDefaultRenderState)
135 {
136     if ( ! rxCanvas.is() || ! rxCanvas->getDevice().is())
137         return;
138 
139     if ( ! rxTexture.is())
140         return;
141 
142     if ( ! rxPolygon.is())
143         return;
144 
145     rendering::ViewState aViewState (rDefaultViewState);
146     aViewState.Clip = rxPolygon;
147 
148     // Create a local render state at which the location of the bitmap is
149     // set.
150     rendering::RenderState aRenderState (rDefaultRenderState);
151 
152     // Tile the bitmap over the repaint box.
153     const geometry::IntegerSize2D aBitmapSize (rxTexture->getSize());
154     if( aBitmapSize.Width < 1 || aBitmapSize.Height < 1)
155         return;
156 
157     const sal_Int32 nLeft = (rRepaintBox.X / aBitmapSize.Width) * aBitmapSize.Width;
158     const sal_Int32 nTop = (rRepaintBox.Y / aBitmapSize.Height) * aBitmapSize.Height;
159     const sal_Int32 nRight = ((rRepaintBox.X + rRepaintBox.Width - 1 + aBitmapSize.Width - 1)
160         / aBitmapSize.Width) * aBitmapSize.Width;
161     const sal_Int32 nBottom = ((rRepaintBox.Y + rRepaintBox.Height - 1 + aBitmapSize.Height - 1)
162         / aBitmapSize.Height) * aBitmapSize.Height;
163 
164     for (sal_Int32 nY=nTop; nY<=nBottom; nY+=aBitmapSize.Height)
165         for (sal_Int32 nX=nLeft; nX<=nRight; nX+=aBitmapSize.Width)
166         {
167             if (PresenterGeometryHelper::IsInside(
168                 awt::Rectangle(nX,nY,aBitmapSize.Width,aBitmapSize.Height),
169                 rHole))
170             {
171                 continue;
172             }
173             aRenderState.AffineTransform.m02 = nX;
174             aRenderState.AffineTransform.m12 = nY;
175             rxCanvas->drawBitmap(
176                 rxTexture,
177                 aViewState,
178                 aRenderState);
179         }
180 }
181 
PaintBitmap(const css::uno::Reference<css::rendering::XBitmap> & rxBitmap,const awt::Point & rLocation,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas,const css::awt::Rectangle & rRepaintBox,const css::uno::Reference<css::rendering::XPolyPolygon2D> & rxPolygon,const css::rendering::ViewState & rDefaultViewState,const css::rendering::RenderState & rDefaultRenderState)182 void PresenterCanvasHelper::PaintBitmap (
183     const css::uno::Reference<css::rendering::XBitmap>& rxBitmap,
184     const awt::Point& rLocation,
185     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
186     const css::awt::Rectangle& rRepaintBox,
187     const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxPolygon,
188     const css::rendering::ViewState& rDefaultViewState,
189     const css::rendering::RenderState& rDefaultRenderState)
190 {
191     if ( ! rxCanvas.is() || ! rxCanvas->getDevice().is())
192         return;
193 
194     if ( ! rxBitmap.is())
195         return;
196 
197     if ( ! rxPolygon.is())
198         return;
199 
200     // Set the repaint box as clip rectangle at the view state.
201     rendering::ViewState aViewState (rDefaultViewState);
202     aViewState.Clip = PresenterGeometryHelper::CreatePolygon(rRepaintBox, rxCanvas->getDevice());
203 
204     // Setup the rendering state so that the bitmap is painted top left in
205     // the polygon bounding box.
206     rendering::RenderState aRenderState (rDefaultRenderState);
207     aRenderState.AffineTransform = geometry::AffineMatrix2D(1,0, rLocation.X, 0,1,rLocation.Y);
208     aRenderState.Clip = rxPolygon;
209 
210     rxCanvas->drawBitmap(
211         rxBitmap,
212         aViewState,
213         aRenderState);
214 }
215 
PaintColor(const css::util::Color nColor,const css::uno::Reference<css::rendering::XCanvas> & rxCanvas,const css::awt::Rectangle & rRepaintBox,const css::uno::Reference<css::rendering::XPolyPolygon2D> & rxPolygon,const css::rendering::ViewState & rDefaultViewState,const css::rendering::RenderState & rDefaultRenderState)216 void PresenterCanvasHelper::PaintColor (
217     const css::util::Color nColor,
218     const css::uno::Reference<css::rendering::XCanvas>& rxCanvas,
219     const css::awt::Rectangle& rRepaintBox,
220     const css::uno::Reference<css::rendering::XPolyPolygon2D>& rxPolygon,
221     const css::rendering::ViewState& rDefaultViewState,
222     const css::rendering::RenderState& rDefaultRenderState)
223 {
224     if ( ! rxCanvas.is() || ! rxCanvas->getDevice().is())
225         return;
226 
227     if ( ! rxPolygon.is())
228         return;
229 
230     // Set the repaint box as clip rectangle at the view state.
231     rendering::ViewState aViewState (rDefaultViewState);
232     aViewState.Clip = PresenterGeometryHelper::CreatePolygon(rRepaintBox, rxCanvas->getDevice());
233 
234     // Setup the rendering state to use the given color.
235     rendering::RenderState aRenderState (rDefaultRenderState);
236     SetDeviceColor(aRenderState, nColor);
237 
238     rxCanvas->fillPolyPolygon(
239         rxPolygon,
240         aViewState,
241         aRenderState);
242 }
243 
SetDeviceColor(rendering::RenderState & rRenderState,const util::Color aColor)244 void PresenterCanvasHelper::SetDeviceColor(
245     rendering::RenderState& rRenderState,
246     const util::Color aColor)
247 {
248     // Other component counts then 4 (RGBA) are not accepted (anymore).
249 
250     OSL_ASSERT(rRenderState.DeviceColor.getLength() == 4);
251     if (rRenderState.DeviceColor.getLength() == 4)
252     {
253         rRenderState.DeviceColor[0] = ((aColor >> 16) & 0x0ff) / 255.0;
254         rRenderState.DeviceColor[1] = ((aColor >> 8) & 0x0ff) / 255.0;
255         rRenderState.DeviceColor[2] = ((aColor >> 0) & 0x0ff) / 255.0;
256         rRenderState.DeviceColor[3] = 1.0 - ((aColor >> 24) & 0x0ff) / 255.0;
257     }
258 }
259 
GetTextBoundingBox(const css::uno::Reference<css::rendering::XCanvasFont> & rxFont,const OUString & rsText,const sal_Int8 nTextDirection)260 css::geometry::RealRectangle2D PresenterCanvasHelper::GetTextBoundingBox (
261     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
262     const OUString& rsText,
263     const sal_Int8 nTextDirection)
264 {
265     if (rxFont.is() && !rsText.isEmpty())
266     {
267         rendering::StringContext aContext (rsText, 0, rsText.getLength());
268         Reference<rendering::XTextLayout> xLayout (
269             rxFont->createTextLayout(aContext, nTextDirection, 0));
270         return xLayout->queryTextBounds();
271     }
272     else
273     {
274         return geometry::RealRectangle2D(0,0,0,0);
275     }
276 }
277 
GetTextSize(const css::uno::Reference<css::rendering::XCanvasFont> & rxFont,const OUString & rsText)278 css::geometry::RealSize2D PresenterCanvasHelper::GetTextSize (
279     const css::uno::Reference<css::rendering::XCanvasFont>& rxFont,
280     const OUString& rsText)
281 {
282     const geometry::RealRectangle2D aTextBBox (GetTextBoundingBox(rxFont, rsText));
283     return css::geometry::RealSize2D(aTextBBox.X2 - aTextBBox.X1, aTextBBox.Y2 - aTextBBox.Y1);
284 }
285 
286 } // end of namespace sdext::presenter
287 
288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
289