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 
10 #include <sal/config.h>
11 
12 #include <memory>
13 
14 #include <config_features.h>
15 
16 #include <math.h>
17 #include <rtl/math.hxx>
18 #include <sal/log.hxx>
19 
20 #include <comphelper/processfactory.hxx>
21 #include <comphelper/random.hxx>
22 #include <cppuhelper/bootstrap.hxx>
23 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
24 #include <com/sun/star/lang/XInitialization.hpp>
25 #include <com/sun/star/registry/XSimpleRegistry.hpp>
26 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
27 #include <com/sun/star/uno/Reference.hxx>
28 #include <com/sun/star/uno/Sequence.hxx>
29 #include <com/sun/star/container/XNameAccess.hpp>
30 #include <o3tl/safeint.hxx>
31 #include <osl/time.h>
32 #include <vcl/gradient.hxx>
33 #include <vcl/vclmain.hxx>
34 #include <vcl/layout.hxx>
35 #include <vcl/ptrstyle.hxx>
36 #include <salhelper/thread.hxx>
37 
38 #include <tools/diagnose_ex.h>
39 #include <tools/urlobj.hxx>
40 #include <tools/stream.hxx>
41 #include <vcl/svapp.hxx>
42 #include <vcl/wrkwin.hxx>
43 #include <vcl/virdev.hxx>
44 #include <vcl/graphicfilter.hxx>
45 #include <vcl/toolkit/button.hxx>
46 #include <vcl/toolkit/combobox.hxx>
47 #include <vcl/toolbox.hxx>
48 #include <vcl/pngwrite.hxx>
49 #include <vcl/toolkit/floatwin.hxx>
50 #include <vcl/help.hxx>
51 #include <vcl/menu.hxx>
52 #include <vcl/ImageTree.hxx>
53 #include <vcl/BitmapEmbossGreyFilter.hxx>
54 
55 #include <bitmap/BitmapWriteAccess.hxx>
56 
57 #include <basegfx/numeric/ftools.hxx>
58 #include <basegfx/matrix/b2dhommatrix.hxx>
59 
60 #define FIXME_SELF_INTERSECTING_WORKING 0
61 #define FIXME_BOUNCE_BUTTON 0
62 #define THUMB_REPEAT_FACTOR 10
63 
64 using namespace com::sun::star;
65 
66 namespace {
getTimeNow()67     double getTimeNow()
68     {
69         TimeValue aValue;
70         osl_getSystemTime(&aValue);
71         return static_cast<double>(aValue.Seconds) * 1000 +
72             static_cast<double>(aValue.Nanosec) / (1000*1000);
73     }
74 
75 }
76 
77 namespace {
78 
79 enum RenderStyle {
80     RENDER_THUMB,    // small view <n> to a page
81     RENDER_EXPANDED, // expanded view of this renderer
82 };
83 
84 class DemoRenderer
85 {
86     Bitmap   maIntroBW;
87     BitmapEx maIntro;
88 
89     int mnSegmentsX;
90     int mnSegmentsY;
91 
92     struct RenderContext {
93         RenderStyle   meStyle;
94         bool          mbVDev;
95         DemoRenderer *mpDemoRenderer;
96         Size          maSize;
97     };
98     struct RegionRenderer {
99     public:
RegionRenderer__anonae1f6d930211::DemoRenderer::RegionRenderer100         RegionRenderer() :
101             sumTime(0),
102             countTime(0)
103         { }
~RegionRenderer__anonae1f6d930211::DemoRenderer::RegionRenderer104         virtual ~RegionRenderer() {}
105         virtual OUString getName() = 0;
106         virtual sal_uInt16 getAccelerator() = 0;
107         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
108                                   const RenderContext &rCtx) = 0;
109         // repeating count for profiling (to exceed the poor time resolution on Windows)
110         virtual sal_uInt16 getTestRepeatCount() = 0;
111 #define RENDER_DETAILS(name,key,repeat) \
112         virtual OUString getName() override \
113             { return SAL_STRINGIFY(name); } \
114         virtual sal_uInt16 getAccelerator() override \
115             { return key; } \
116         virtual sal_uInt16 getTestRepeatCount() override \
117             { return repeat; }
118 
119         double sumTime;
120         int countTime;
121     };
122 
123     std::vector< RegionRenderer * > maRenderers;
124     sal_Int32  mnSelectedRenderer;
125     sal_Int32  iterCount;
126 
127     void     InitRenderers();
128 
129 public:
DemoRenderer()130     DemoRenderer() : mnSegmentsX(0)
131                    , mnSegmentsY(0)
132                    , mnSelectedRenderer(-1)
133                    , iterCount(0)
134 #if FIXME_BOUNCE_BUTTON
135                    , mpButton(NULL)
136                    , mpButtonWin(NULL)
137                    , mnBounceX(1)
138                    , mnBounceY(1)
139 #endif
140     {
141         if (!Application::LoadBrandBitmap("intro", maIntro))
142             Application::Abort("Failed to load intro image");
143 
144         maIntroBW = maIntro.GetBitmap();
145 
146         BitmapEx aTmpBmpEx(maIntroBW);
147         BitmapFilter::Filter(aTmpBmpEx, BitmapEmbossGreyFilter(0, 0));
148         maIntroBW = aTmpBmpEx.GetBitmap();
149 
150         InitRenderers();
151         mnSegmentsY = rtl::math::round(std::sqrt(maRenderers.size()), 0,
152                                        rtl_math_RoundingMode_Down);
153         mnSegmentsX = (maRenderers.size() + mnSegmentsY - 1)/mnSegmentsY;
154     }
155 
156     OUString getRendererList();
157     double   getAndResetBenchmark(RenderStyle style);
158     void     selectRenderer(std::u16string_view rName);
159     int      selectNextRenderer();
160     void     setIterCount(sal_Int32 iterCount);
161     sal_Int32 getIterCount() const;
162     void     addTime(int i, double t);
163 
164     Size maSize;
SetSizePixel(const Size & rSize)165     void SetSizePixel(const Size &rSize) { maSize = rSize; }
GetSizePixel() const166     const Size& GetSizePixel() const            { return maSize;  }
167 
168 
169 // more of a 'Window' concept - push upwards ?
170 #if FIXME_BOUNCE_BUTTON
171     // Bouncing windows on click ...
172     PushButton     *mpButton;
173     FloatingWindow *mpButtonWin;
174     AutoTimer       maBounce;
175     int             mnBounceX, mnBounceY;
176     DECL_LINK(BounceTimerCb, Timer*, void);
177 #endif
178 
179     bool MouseButtonDown(const MouseEvent& rMEvt);
180     void KeyInput(const KeyEvent& rKEvt);
181 
partition(const tools::Rectangle & rRect,int nX,int nY)182     static std::vector<tools::Rectangle> partition(const tools::Rectangle &rRect, int nX, int nY)
183     {
184         std::vector<tools::Rectangle> aRegions = partition(rRect.GetSize(), nX, nY);
185         for (auto & region : aRegions)
186             region.Move(rRect.Left(), rRect.Top());
187 
188         return aRegions;
189     }
190 
partition(const RenderContext & rCtx,int nX,int nY)191     static std::vector<tools::Rectangle> partition(const RenderContext &rCtx, int nX, int nY)
192     {
193         return partition(rCtx.maSize, nX, nY);
194     }
195 
partition(Size aSize,int nX,int nY)196     static std::vector<tools::Rectangle> partition(Size aSize, int nX, int nY)
197     {
198         tools::Rectangle r;
199         std::vector<tools::Rectangle> aRegions;
200 
201         // Make small cleared area for these guys
202         tools::Long nBorderSize = std::min(aSize.Height() / 32, aSize.Width() / 32);
203         tools::Long nBoxWidth = (aSize.Width() - nBorderSize*(nX+1)) / nX;
204         tools::Long nBoxHeight = (aSize.Height() - nBorderSize*(nY+1)) / nY;
205         for (int y = 0; y < nY; y++)
206         {
207             for (int x = 0; x < nX; x++)
208             {
209                 r.SetPos(Point(nBorderSize + (nBorderSize + nBoxWidth) * x,
210                                nBorderSize + (nBorderSize + nBoxHeight) * y));
211                 r.SetSize(Size(nBoxWidth, nBoxHeight));
212                 aRegions.push_back(r);
213             }
214         }
215 
216         return aRegions;
217     }
218 
clearRects(OutputDevice & rDev,std::vector<tools::Rectangle> & rRects)219     static void clearRects(OutputDevice &rDev, std::vector<tools::Rectangle> &rRects)
220     {
221         for (size_t i = 0; i < rRects.size(); i++)
222         {
223             // knock up a nice little border
224             rDev.SetLineColor(COL_GRAY);
225             rDev.SetFillColor(COL_LIGHTGRAY);
226             if (i % 2)
227             {
228                 int nBorderSize = rRects[i].GetWidth() / 5;
229                 rDev.DrawRect(rRects[i], nBorderSize, nBorderSize);
230             }
231             else
232                 rDev.DrawRect(rRects[i]);
233         }
234     }
235 
drawBackground(OutputDevice & rDev,const tools::Rectangle & r)236     static void drawBackground(OutputDevice &rDev, const tools::Rectangle& r)
237     {
238         rDev.Erase();
239         Gradient aGradient;
240         aGradient.SetStartColor(COL_BLUE);
241         aGradient.SetEndColor(COL_GREEN);
242         aGradient.SetStyle(GradientStyle::Linear);
243         rDev.DrawGradient(r, aGradient);
244     }
245 
246     struct DrawLines : public RegionRenderer
247     {
248         RENDER_DETAILS(lines,KEY_L,100)
RenderRegion__anonae1f6d930211::DemoRenderer::DrawLines249         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
250                                   const RenderContext &rCtx) override
251         {
252             if (rCtx.meStyle == RENDER_EXPANDED)
253             {
254                 AntialiasingFlags nOldAA = rDev.GetAntialiasing();
255                 rDev.SetAntialiasing(AntialiasingFlags::Enable);
256 
257                 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx, 4, 4));
258                 DemoRenderer::clearRects(rDev, aRegions);
259 
260 #if 0 // FIXME: get this through to the backend ...
261                 double nTransparency[] = {
262                     1.0, 1.0, 1.0, 1.0,
263                     0.8, 0.8, 0.8, 0.8,
264                     0.5, 0.5, 0.5, 0.5,
265                     0.1, 0.1, 0.1, 0.1
266                 };
267 #endif
268                 drawing::LineCap const eLineCaps[] = {
269                     drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT,
270                     drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT,
271                     drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT,
272                     drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT
273                 };
274                 basegfx::B2DLineJoin const eJoins[] = {
275                     basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round,
276                     basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round,
277                     basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round,
278                     basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round
279                 };
280                 double const aLineWidths[] = {
281                     10.0, 15.0, 20.0, 10.0,
282                     10.0, 15.0, 20.0, 10.0,
283                     10.0, 15.0, 20.0, 10.0,
284                      0.1,  1.0, 10.0, 50.0
285                 };
286                 for (size_t i = 0; i < aRegions.size(); i++)
287                 {
288                     // Half of them not-anti-aliased ..
289                     if (i >= aRegions.size()/2)
290                         rDev.SetAntialiasing(nOldAA);
291 
292                     static const struct {
293                         double nX, nY;
294                     } aPoints[] = {
295                         { 0.2, 0.2 }, { 0.8, 0.3 }, { 0.7, 0.8 }
296                     };
297                     rDev.SetLineColor(COL_BLACK);
298                     basegfx::B2DPolygon aPoly;
299                     tools::Rectangle aSub(aRegions[i]);
300                     for (size_t j = 0; j < SAL_N_ELEMENTS(aPoints); j++)
301                     {
302                         aPoly.append(basegfx::B2DPoint(aSub.Left() + aSub.GetWidth() * aPoints[j].nX,
303                                                        aSub.Top()  + aSub.GetHeight() * aPoints[j].nY));
304                     }
305                     rDev.DrawPolyLine(aPoly, aLineWidths[i], eJoins[i], eLineCaps[i]);
306                 }
307             }
308             else
309             {
310                 rDev.SetFillColor(COL_LIGHTRED);
311                 rDev.SetLineColor(COL_BLACK);
312                 rDev.DrawRect(r);
313 
314                 for(tools::Long i=0; i<r.GetHeight(); i+=15)
315                     rDev.DrawLine(Point(r.Left(), r.Top()+i), Point(r.Right(), r.Bottom()-i));
316                 for(tools::Long i=0; i<r.GetWidth(); i+=15)
317                     rDev.DrawLine(Point(r.Left()+i, r.Bottom()), Point(r.Right()-i, r.Top()));
318 
319                 // Should draw a white-line across the middle
320                 Color aLastPixel(COL_WHITE);
321                 Point aCenter((r.Left() + r.Right())/2 - 4,
322                               (r.Top() + r.Bottom())/2 - 4);
323                 for(int i=0; i<8; i++)
324                 {
325                     rDev.DrawPixel(aCenter, aLastPixel);
326                     aLastPixel = rDev.GetPixel(aCenter);
327                     aCenter.Move(1,1);
328                 }
329             }
330         }
331     };
332 
333     struct DrawText : public RegionRenderer
334     {
335         RENDER_DETAILS(text,KEY_T,1)
336 
RenderRegion__anonae1f6d930211::DemoRenderer::DrawText337         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
338                                   const RenderContext &rCtx) override
339         {
340             if (rCtx.meStyle == RENDER_EXPANDED)
341             {
342                 std::vector<tools::Rectangle> aToplevelRegions(
343                     DemoRenderer::partition(rCtx, 1, 3));
344                 std::vector<tools::Rectangle> aSubRegions(
345                     DemoRenderer::partition(aToplevelRegions[0], 4, 2));
346                 tools::Rectangle aBottom(aToplevelRegions[1].TopLeft(),
347                                   aToplevelRegions[2].BottomRight());
348                 DemoRenderer::clearRects(rDev,aSubRegions);
349                 static struct {
350                     bool mbClip;
351                     bool mbArabicText;
352                     bool mbRotate;
353                 } const aRenderData[] = {
354                     { false, false, false },
355                     { false, true,  false },
356                     { false, true,  true },
357                     { false, false, true },
358                     { true,  false, true },
359                     { true,  true,  true },
360                     { true,  true,  false },
361                     { true,  false, false },
362                 };
363 
364                 size_t i = 0;
365                 for (int y = 0; y < 2; y++)
366                 {
367                     for (int x = 0; x < 4; x++)
368                     {
369                         assert(i < SAL_N_ELEMENTS(aRenderData));
370                         drawText(rDev, aSubRegions[i], aRenderData[i].mbClip,
371                                  aRenderData[i].mbArabicText, aRenderData[i].mbRotate);
372                         i++;
373                     }
374                 }
375 
376                 drawComplex(rDev, aBottom);
377             }
378             else
379             {
380                 drawText(rDev, r, false, false, false);
381             }
382         }
383 
drawText__anonae1f6d930211::DemoRenderer::DrawText384         static void drawText (OutputDevice &rDev, tools::Rectangle r, bool bClip, bool bArabicText, bool bRotate)
385         {
386             rDev.SetClipRegion( vcl::Region(r) );
387 
388             const unsigned char pTextUTF8[] = {
389                 0xd9, 0x88, 0xd8, 0xa7, 0xd8, 0xad, 0xd9, 0x90,
390                 0xd8, 0xaf, 0xd9, 0x92, 0x20, 0xd8, 0xa5, 0xd8,
391                 0xab, 0xd9, 0x8d, 0xd9, 0x86, 0xd9, 0x8a, 0xd9,
392                 0x86, 0x20, 0xd8, 0xab, 0xd9, 0x84, 0xd8, 0xa7,
393                 0xd8, 0xab, 0xd8, 0xa9, 0xd9, 0x8c, 0x00
394             };
395             OUString aArabicText( reinterpret_cast<char const *>(pTextUTF8),
396                             SAL_N_ELEMENTS( pTextUTF8 ) - 1,
397                             RTL_TEXTENCODING_UTF8 );
398 
399             OUString aText;
400 
401             // To have more text displayed one after the other (overlapping, and in different colours), then
402             // change this value
403             const int nPrintNumCopies=1;
404 
405             if (bArabicText)
406                 aText = aArabicText;
407             else
408                 aText = "Click any rect to zoom!!!!";
409 
410             std::vector<OUString> aFontNames;
411 
412             static Color const nCols[] = {
413                 COL_BLACK, COL_BLUE, COL_GREEN, COL_CYAN, COL_RED, COL_MAGENTA,
414                 COL_BROWN, COL_GRAY, COL_LIGHTGRAY, COL_LIGHTBLUE, COL_LIGHTGREEN,
415                 COL_LIGHTCYAN, COL_LIGHTRED, COL_LIGHTMAGENTA, COL_YELLOW, COL_WHITE
416             };
417 
418             // a few fonts to start with
419             const char *pNames[] = {
420                 "Times", "Liberation Sans", "Arial", "Linux Biolinum G", "Linux Libertine Display G"
421               };
422 
423             for (size_t i = 0; i < SAL_N_ELEMENTS(pNames); i++)
424                 aFontNames.push_back(OUString::createFromAscii(pNames[i]));
425 
426             if (bClip && !bRotate)
427             {
428                 // only show the first quarter of the text
429                 tools::Rectangle aRect( r.TopLeft(), Size( r.GetWidth()/2, r.GetHeight()/2 ) );
430                 rDev.SetClipRegion( vcl::Region( aRect ) );
431             }
432 
433             for (int i = 1; i < nPrintNumCopies+1; i++)
434             {
435                 int nFontHeight=0, nFontIndex=0, nFontColorIndex=0;
436 
437                 if (nPrintNumCopies == 1)
438                 {
439                     float const nFontMagnitude = 0.25f;
440                     // random font size to avoid buffering
441                     nFontHeight = 1 + nFontMagnitude * (0.9 + comphelper::rng::uniform_real_distribution(0.0, std::nextafter(0.1, DBL_MAX))) * (r.Bottom() - r.Top());
442                     nFontIndex=0;
443                     nFontColorIndex=0;
444                 }
445                 else
446                 {
447                     // random font size to avoid buffering
448                     nFontHeight = 1 + i * (0.9 + comphelper::rng::uniform_real_distribution(0.0, std::nextafter(0.1, DBL_MAX))) * (r.Top() - r.Bottom()) / nPrintNumCopies;
449                     nFontIndex = (i % aFontNames.size());
450                     nFontColorIndex=(i % aFontNames.size());
451                 }
452 
453                 rDev.SetTextColor(nCols[nFontColorIndex]);
454                 vcl::Font aFont( aFontNames[nFontIndex], Size(0, nFontHeight ));
455 
456                 if (bRotate)
457                 {
458                     tools::Rectangle aFontRect = r;
459 
460                     int nHeight = r.GetHeight();
461 
462                     // move the text to the bottom of the bounding rect before rotating
463                     aFontRect.AdjustTop(nHeight/2 );
464                     aFontRect.AdjustBottom(nHeight );
465 
466                     aFont.SetOrientation(450_deg10); // 45 degrees
467 
468                     rDev.SetFont(aFont);
469                     rDev.DrawText(aFontRect, aText);
470 
471                     if (bClip)
472                     {
473                         tools::Rectangle aClipRect( Point( r.Left(), r.Top() + ( r.GetHeight()/2 ) ) , Size( r.GetWidth()/2, r.GetHeight()/2 ) );
474                         rDev.SetClipRegion( vcl::Region( aClipRect ) );
475                     }
476                     else
477                         rDev.SetClipRegion( vcl::Region(r) );
478                 }
479                 else
480                 {
481                     rDev.SetFont(aFont);
482                     rDev.DrawText(r, aText);
483                 }
484             }
485 
486             rDev.SetClipRegion();
487         }
488 
drawComplex__anonae1f6d930211::DemoRenderer::DrawText489         static void drawComplex (OutputDevice &rDev, tools::Rectangle r)
490         {
491             const unsigned char pInvalid[] = { 0xfe, 0x1f, 0 };
492             const unsigned char pDiacritic1[] = { 0x61, 0xcc, 0x8a, 0xcc, 0x8c, 0 };
493             const unsigned char pDiacritic2[] = { 0x61, 0xcc, 0x88, 0xcc, 0x86, 0 };
494             const unsigned char pDiacritic3[] = { 0x61, 0xcc, 0x8b, 0xcc, 0x87, 0 };
495             const unsigned char pJustification[] = {
496                 0x64, 0x20, 0xc3, 0xa1, 0xc3, 0xa9, 0x77, 0xc4, 0x8d,
497                 0xc5, 0xa1, 0xc3, 0xbd, 0xc5, 0x99, 0x20, 0xc4, 0x9b, 0
498             };
499             const unsigned char pEmojis[] = {
500                 0xf0, 0x9f, 0x8d, 0x80, 0xf0, 0x9f, 0x91, 0x98,
501                 0xf0, 0x9f, 0x92, 0x8a, 0xf0, 0x9f, 0x92, 0x99,
502                 0xf0, 0x9f, 0x92, 0xa4, 0xf0, 0x9f, 0x94, 0x90, 0
503             };
504             const unsigned char pThreeBowlG[] = {
505                 0xe2, 0x9a, 0x82, 0xe2, 0x99, 0xa8, 0xc4, 0x9e, 0
506             };
507             const unsigned char pWavesAndDomino[] = {
508                 0xe2, 0x99, 0x92, 0xf0, 0x9f, 0x81, 0xa0,
509                 0xf0, 0x9f, 0x82, 0x93, 0
510             };
511             const unsigned char pSpadesAndBits[] = {
512                 0xf0, 0x9f, 0x82, 0xa1, 0xc2, 0xa2, 0xc2, 0xa2, 0
513             };
514 
515             static struct {
516                 const char *mpFont;
517                 const char *mpString;
518             } const aRuns[] = {
519 #define SET(font,string) { font, reinterpret_cast<const char *>(string) }
520                 {"sans", "a"},           // logical font - no 'sans' font.
521                 {"opensymbol", "#$%"},   // font fallback - $ is missing.
522                 SET("sans", pInvalid),      // unicode invalid character
523                 // tdf#96266 - stacking diacritics
524                 SET("carlito", pDiacritic1),
525                 SET("carlito", pDiacritic2),
526                 SET("carlito", pDiacritic3),
527                 SET("liberation sans", pDiacritic1),
528                 SET("liberation sans", pDiacritic2),
529                 SET("liberation sans", pDiacritic3),
530                 SET("liberation sans", pDiacritic3),
531 
532                 // tdf#95222 - justification issue
533                 // - FIXME: replicate justification
534                 SET("gentium basic", pJustification),
535 
536                 // tdf#97319 - Unicode beyond BMP; SMP & Plane 2
537                 SET("symbola", pEmojis),
538                 SET("symbola", pThreeBowlG),
539                 SET("symbola", pWavesAndDomino),
540                 SET("symbola", pSpadesAndBits),
541             };
542 
543             // Nice clean white background
544             rDev.DrawWallpaper(r, Wallpaper(COL_WHITE));
545             rDev.SetClipRegion(vcl::Region(r));
546 
547             Point aPos(r.Left(), r.Top()+20);
548 
549             tools::Long nMaxTextHeight = 0;
550             for (size_t i = 0; i < SAL_N_ELEMENTS(aRuns); ++i)
551             {
552                 // Legend
553                 vcl::Font aIndexFont("sans", Size(0,20));
554                 aIndexFont.SetColor( COL_BLACK);
555                 tools::Rectangle aTextRect;
556                 rDev.SetFont(aIndexFont);
557                 OUString aText = OUString::number(i) + ".";
558                 rDev.DrawText(aPos, aText);
559                 if (rDev.GetTextBoundRect(aTextRect, aText))
560                     aPos.Move(aTextRect.GetWidth() + 8, 0);
561 
562                 // Text
563                 FontWeight aWeights[] = { WEIGHT_NORMAL,
564                                           WEIGHT_BOLD,
565                                           WEIGHT_NORMAL };
566                 FontItalic const aItalics[] = { ITALIC_NONE,
567                                           ITALIC_NONE,
568                                           ITALIC_NORMAL };
569                 vcl::Font aFont(OUString::createFromAscii(
570                                     aRuns[i].mpFont),
571                                 Size(0,42));
572                 aFont.SetColor(COL_BLACK);
573                 for (size_t j = 0; j < SAL_N_ELEMENTS(aWeights); ++j)
574                 {
575                     aFont.SetItalic(aItalics[j]);
576                     aFont.SetWeight(aWeights[j]);
577                     rDev.SetFont(aFont);
578 
579                     OUString aString(aRuns[i].mpString,
580                                      strlen(aRuns[i].mpString),
581                                      RTL_TEXTENCODING_UTF8);
582                     tools::Long nNewX = drawStringBox(rDev, aPos, aString,
583                                                nMaxTextHeight);
584 
585                     aPos.setX( nNewX );
586 
587                     if (aPos.X() >= r.Right())
588                     {
589                         aPos = Point(r.Left(), aPos.Y() + nMaxTextHeight + 15);
590                         nMaxTextHeight = 0;
591                         if(j>0)
592                             j--; // re-render the last point.
593                     }
594                     if (aPos.Y() > r.Bottom())
595                         break;
596                 }
597                 if (aPos.Y() > r.Bottom())
598                     break;
599             }
600 
601             rDev.SetClipRegion();
602         }
603         // render text, bbox, DX arrays etc.
drawStringBox__anonae1f6d930211::DemoRenderer::DrawText604         static tools::Long drawStringBox(OutputDevice &rDev, Point aPos,
605                            const OUString &aText,
606                            tools::Long &nMaxTextHeight)
607         {
608             rDev.Push();
609             {
610                 tools::Rectangle aTextRect;
611 
612                 rDev.DrawText(aPos,aText);
613 
614                 if (rDev.GetTextBoundRect(aTextRect, aText))
615                 {
616                     aTextRect.Move(aPos.X(), aPos.Y());
617                     rDev.SetFillColor();
618                     rDev.SetLineColor(COL_BLACK);
619                     rDev.DrawRect(aTextRect);
620                     if (aTextRect.GetHeight() > nMaxTextHeight)
621                         nMaxTextHeight = aTextRect.GetHeight();
622                     // This should intersect with the text
623                     tools::Rectangle aInnerRect(
624                         aTextRect.Left()+1, aTextRect.Top()+1,
625                         aTextRect.Right()-1, aTextRect.Bottom()-1);
626                     rDev.SetLineColor(COL_WHITE);
627                     rDev.SetRasterOp(RasterOp::Xor);
628                     rDev.DrawRect(aInnerRect);
629                     rDev.SetRasterOp(RasterOp::OverPaint);
630                 }
631 
632                 // DX array rendering
633                 std::unique_ptr<tools::Long[]> pItems(new tools::Long[aText.getLength()+10]);
634                 rDev.GetTextArray(aText, pItems.get());
635                 for (tools::Long j = 0; j < aText.getLength(); ++j)
636                 {
637                     Point aTop = aTextRect.TopLeft();
638                     Point aBottom = aTop;
639                     aTop.Move(pItems[j], 0);
640                     aBottom.Move(pItems[j], aTextRect.GetHeight());
641                     rDev.SetLineColor(COL_RED);
642                     rDev.SetRasterOp(RasterOp::Xor);
643                     rDev.DrawLine(aTop,aBottom);
644                     rDev.SetRasterOp(RasterOp::OverPaint);
645                 }
646 
647                 aPos.Move(aTextRect.GetWidth() + 16, 0);
648             }
649             rDev.Pop();
650             return aPos.X();
651         }
652     };
653 
654     struct DrawCheckered : public RegionRenderer
655     {
656         RENDER_DETAILS(checks,KEY_C,20)
RenderRegion__anonae1f6d930211::DemoRenderer::DrawCheckered657         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
658                                   const RenderContext &rCtx) override
659         {
660             if (rCtx.meStyle == RENDER_EXPANDED)
661             {
662                 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx, 2, 2));
663                 for (size_t i = 0; i < aRegions.size(); i++)
664                 {
665                     vcl::Region aRegion;
666                     tools::Rectangle aSub(aRegions[i]);
667                     tools::Rectangle aSmaller(aSub);
668                     aSmaller.Move(10,10);
669                     aSmaller.setWidth(aSmaller.getWidth()-20);
670                     aSmaller.setHeight(aSmaller.getHeight()-24);
671                     switch (i) {
672                     case 0:
673                         aRegion = vcl::Region(aSub);
674                         break;
675                     case 1:
676                         aRegion = vcl::Region(aSmaller);
677                         aRegion.XOr(aSub);
678                         break;
679                     case 2:
680                     {
681                         tools::Polygon aPoly(aSub);
682                         aPoly.Rotate(aSub.Center(), 450_deg10);
683                         aPoly.Clip(aSmaller);
684                         aRegion = vcl::Region(aPoly);
685                         break;
686                     }
687                     case 3:
688                     {
689                         tools::PolyPolygon aPolyPoly;
690                         sal_Int32 nTW = aSub.GetWidth()/6;
691                         sal_Int32 nTH = aSub.GetHeight()/6;
692                         tools::Rectangle aTiny(Point(4, 4), Size(nTW*2, nTH*2));
693                         aPolyPoly.Insert( tools::Polygon(aTiny));
694                         aTiny.Move(nTW*3, nTH*3);
695                         aPolyPoly.Insert( tools::Polygon(aTiny));
696                         aTiny.Move(nTW, nTH);
697                         aPolyPoly.Insert( tools::Polygon(aTiny));
698 
699                         aRegion = vcl::Region(aPolyPoly);
700                         break;
701                     }
702                     } // switch
703                     rDev.SetClipRegion(aRegion);
704                     rDev.DrawCheckered(aSub.TopLeft(), aSub.GetSize());
705                     rDev.SetClipRegion();
706                 }
707             }
708             else
709             {
710                 rDev.DrawCheckered(r.TopLeft(), r.GetSize());
711             }
712         }
713     };
714 
715     struct DrawPoly : public RegionRenderer
716     {
717         RENDER_DETAILS(poly,KEY_P,20)
718         DrawCheckered maCheckered;
RenderRegion__anonae1f6d930211::DemoRenderer::DrawPoly719         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
720                                   const RenderContext &rCtx) override
721         {
722             maCheckered.RenderRegion(rDev, r, rCtx);
723 
724             tools::Long nDx = r.GetWidth()/20;
725             tools::Long nDy = r.GetHeight()/20;
726             tools::Rectangle aShrunk(r);
727             aShrunk.Move(nDx, nDy);
728             aShrunk.SetSize(Size(r.GetWidth()-nDx*2,
729                                  r.GetHeight()-nDy*2));
730             tools::Polygon aPoly(aShrunk);
731             tools::PolyPolygon aPPoly(aPoly);
732             rDev.SetLineColor(COL_RED);
733             rDev.SetFillColor(COL_RED);
734             // This hits the optional 'drawPolyPolygon' code-path
735             rDev.DrawTransparent(aPPoly, 64);
736         }
737     };
738 
739     struct DrawEllipse : public RegionRenderer
740     {
741         RENDER_DETAILS(ellipse,KEY_E,500)
doInvert__anonae1f6d930211::DemoRenderer::DrawEllipse742         static void doInvert(OutputDevice &rDev, const tools::Rectangle &r,
743                       InvertFlags nFlags)
744         {
745             rDev.Invert(r, nFlags);
746             if (r.GetWidth() > 10 && r.GetHeight() > 10)
747             {
748                 tools::Rectangle aSmall(r.Center()-Point(4,4), Size(8,8));
749                 rDev.Invert(aSmall,nFlags);
750             }
751         }
RenderRegion__anonae1f6d930211::DemoRenderer::DrawEllipse752         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
753                                   const RenderContext &rCtx) override
754         {
755             rDev.SetLineColor(COL_RED);
756             rDev.SetFillColor(COL_GREEN);
757             rDev.DrawEllipse(r);
758 
759             if (rCtx.meStyle == RENDER_EXPANDED)
760             {
761                 auto aRegions = partition(rCtx, 2, 2);
762                 doInvert(rDev, aRegions[0], InvertFlags::NONE);
763                 rDev.DrawText(aRegions[0], "InvertFlags::NONE");
764                 doInvert(rDev, aRegions[1], InvertFlags::N50);
765                 rDev.DrawText(aRegions[1], "InvertFlags::N50");
766                 doInvert(rDev, aRegions[3], InvertFlags::TrackFrame);
767                 rDev.DrawText(aRegions[3], "InvertFlags::TrackFrame");
768             }
769         }
770     };
771 
772     struct DrawGradient : public RegionRenderer
773     {
774         RENDER_DETAILS(gradient,KEY_G,50)
RenderRegion__anonae1f6d930211::DemoRenderer::DrawGradient775         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
776                                   const RenderContext &rCtx) override
777         {
778             if (rCtx.meStyle == RENDER_EXPANDED)
779             {
780                 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx,5, 4));
781                 static Color const nStartCols[] = {
782                     COL_RED, COL_RED, COL_RED, COL_GREEN, COL_GREEN,
783                     COL_BLUE, COL_BLUE, COL_BLUE, COL_CYAN, COL_CYAN,
784                     COL_BLACK, COL_LIGHTGRAY, COL_WHITE, COL_BLUE, COL_CYAN,
785                     COL_WHITE, COL_WHITE, COL_WHITE, COL_BLACK, COL_BLACK
786                 };
787                 static Color const nEndCols[] = {
788                     COL_WHITE, COL_WHITE, COL_WHITE, COL_BLACK, COL_BLACK,
789                     COL_RED, COL_RED, COL_RED, COL_GREEN, COL_GREEN,
790                     COL_GRAY, COL_GRAY, COL_LIGHTGRAY, COL_LIGHTBLUE, COL_LIGHTCYAN,
791                     COL_BLUE, COL_BLUE, COL_BLUE, COL_CYAN, COL_CYAN
792                 };
793                 GradientStyle eStyles[] = {
794                     GradientStyle::Linear, GradientStyle::Axial, GradientStyle::Radial, GradientStyle::Elliptical, GradientStyle::Square,
795                     GradientStyle::Rect, GradientStyle::FORCE_EQUAL_SIZE, GradientStyle::Linear, GradientStyle::Radial, GradientStyle::Linear,
796                     GradientStyle::Linear, GradientStyle::Axial, GradientStyle::Radial, GradientStyle::Elliptical, GradientStyle::Square,
797                     GradientStyle::Rect, GradientStyle::FORCE_EQUAL_SIZE, GradientStyle::Linear, GradientStyle::Radial, GradientStyle::Linear
798                 };
799                 sal_uInt16 nAngles[] = {
800                     0, 0, 0, 0, 0,
801                     15, 30, 45, 60, 75,
802                     90, 120, 135, 160, 180,
803                     0, 0, 0, 0, 0
804                 };
805                 sal_uInt16 nBorders[] = {
806                     0, 0, 0, 0, 0,
807                     1, 10, 100, 10, 1,
808                     0, 0, 0, 0, 0,
809                     1, 10, 20, 10, 1,
810                     0, 0, 0, 0, 0
811                 };
812                 DemoRenderer::clearRects(rDev, aRegions);
813                 assert(aRegions.size() <= SAL_N_ELEMENTS(nStartCols));
814                 assert(aRegions.size() <= SAL_N_ELEMENTS(nEndCols));
815                 assert(aRegions.size() <= SAL_N_ELEMENTS(eStyles));
816                 assert(aRegions.size() <= SAL_N_ELEMENTS(nAngles));
817                 assert(aRegions.size() <= SAL_N_ELEMENTS(nBorders));
818                 for (size_t i = 0; i < aRegions.size(); i++)
819                 {
820                     tools::Rectangle aSub = aRegions[i];
821                     Gradient aGradient;
822                     aGradient.SetStartColor(nStartCols[i]);
823                     aGradient.SetEndColor(nEndCols[i]);
824                     aGradient.SetStyle(eStyles[i]);
825                     aGradient.SetAngle(Degree10(nAngles[i]));
826                     aGradient.SetBorder(nBorders[i]);
827                     rDev.DrawGradient(aSub, aGradient);
828                 }
829             }
830             else
831             {
832                 Gradient aGradient;
833                 aGradient.SetStartColor(COL_YELLOW);
834                 aGradient.SetEndColor(COL_RED);
835                 aGradient.SetStyle(GradientStyle::Rect);
836                 aGradient.SetBorder(r.GetSize().Width()/20);
837                 rDev.DrawGradient(r, aGradient);
838             }
839         }
840     };
841 
842     struct DrawBitmap : public RegionRenderer
843     {
844         RENDER_DETAILS(bitmap,KEY_B,10)
845 
846         // Simulate Page Borders rendering - which ultimately should
847         // be done with a shader / gradient
SimulateBorderStretch__anonae1f6d930211::DemoRenderer::DrawBitmap848         static void SimulateBorderStretch(OutputDevice &rDev, const tools::Rectangle& r)
849         {
850             BitmapEx aPageShadowMask("sw/res/page-shadow-mask.png");
851 
852             BitmapEx aRight(aPageShadowMask);
853             sal_Int32 nSlice = (aPageShadowMask.GetSizePixel().Width() - 3) / 4;
854             // a width x 1 slice
855             aRight.Crop(tools::Rectangle(Point((nSlice * 3) + 3, (nSlice * 2) + 1),
856                                   Size(nSlice, 1)));
857             AlphaMask aAlphaMask(aRight.GetBitmap());
858             Bitmap aBlockColor(aAlphaMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
859             aBlockColor.Erase(COL_RED);
860             BitmapEx aShadowStretch(aBlockColor, aAlphaMask);
861 
862             Point aRenderPt(r.TopLeft());
863 
864             tools::Long aSizes[] = { 200, 100, 200, 100, 50, 5, 2 };
865 
866             // and yes - we really do this in the page border rendering code ...
867             for (size_t i = 0; i < SAL_N_ELEMENTS(aSizes); i++)
868             {
869                 aShadowStretch.Scale(Size(aShadowStretch.GetSizePixel().Width(), aSizes[i]),
870                                      BmpScaleFlag::Fast);
871 
872                 rDev.DrawBitmapEx(aRenderPt, aShadowStretch);
873                 aRenderPt.Move(aShadowStretch.GetSizePixel().Width() + 4, 0);
874             }
875 
876             AlphaMask aWholeMask(aPageShadowMask.GetBitmap());
877             aBlockColor = Bitmap(aPageShadowMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
878             aBlockColor.Erase(COL_GREEN);
879             BitmapEx aWhole(aBlockColor, aWholeMask);
880 
881             aRenderPt = r.Center();
882             aRenderPt.Move(nSlice+1, 0);
883 
884             // An offset background for alpha rendering
885             rDev.SetFillColor(COL_BLUE);
886             tools::Rectangle aSurround(r.Center(), aPageShadowMask.GetSizePixel());
887             rDev.DrawRect(aSurround);
888             rDev.DrawBitmapEx(aRenderPt, aWhole);
889         }
890 
RenderRegion__anonae1f6d930211::DemoRenderer::DrawBitmap891         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
892                                   const RenderContext &rCtx) override
893         {
894             Bitmap aBitmap(rCtx.mpDemoRenderer->maIntroBW);
895             aBitmap.Scale(r.GetSize(), BmpScaleFlag::BestQuality);
896             rDev.DrawBitmap(r.TopLeft(), aBitmap);
897 
898             SimulateBorderStretch(rDev, r);
899         }
900     };
901 
902     struct DrawBitmapEx : public RegionRenderer
903     {
904         RENDER_DETAILS(bitmapex,KEY_X,2)
905         DrawCheckered maCheckered;
RenderRegion__anonae1f6d930211::DemoRenderer::DrawBitmapEx906         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
907                                   const RenderContext &rCtx) override
908         {
909             maCheckered.RenderRegion(rDev, r, rCtx);
910 
911             BitmapEx aBitmap(rCtx.mpDemoRenderer->maIntro);
912             aBitmap.Scale(r.GetSize(), BmpScaleFlag::BestQuality);
913             AlphaMask aSemiTransp(aBitmap.GetSizePixel());
914             aSemiTransp.Erase(64);
915             rDev.DrawBitmapEx(r.TopLeft(), BitmapEx(aBitmap.GetBitmap(),
916                                                     aSemiTransp));
917         }
918     };
919 
920     struct DrawPolyPolygons : public RegionRenderer
921     {
922         RENDER_DETAILS(polypoly,KEY_N,100)
RenderRegion__anonae1f6d930211::DemoRenderer::DrawPolyPolygons923         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
924                                   const RenderContext &) override
925         {
926             static struct {
927                 double nX, nY;
928             } const aPoints[] = { { 0.1, 0.1 }, { 0.9, 0.9 },
929 #if FIXME_SELF_INTERSECTING_WORKING
930                             { 0.9, 0.1 }, { 0.1, 0.9 },
931                             { 0.1, 0.1 }
932 #else
933                             { 0.1, 0.9 }, { 0.5, 0.5 },
934                             { 0.9, 0.1 }, { 0.1, 0.1 }
935 #endif
936             };
937 
938             tools::PolyPolygon aPolyPoly;
939             // Render 4x polygons & aggregate into another PolyPolygon
940             for (int x = 0; x < 2; x++)
941             {
942                 for (int y = 0; y < 2; y++)
943                 {
944                     tools::Rectangle aSubRect(r);
945                     aSubRect.Move(x * r.GetWidth()/3, y * r.GetHeight()/3);
946                     aSubRect.SetSize(Size(r.GetWidth()/2, r.GetHeight()/4));
947                     tools::Polygon aPoly(SAL_N_ELEMENTS(aPoints));
948                     for (size_t v = 0; v < SAL_N_ELEMENTS(aPoints); v++)
949                     {
950                         aPoly.SetPoint(Point(aSubRect.Left() +
951                                              aSubRect.GetWidth() * aPoints[v].nX,
952                                              aSubRect.Top() +
953                                              aSubRect.GetHeight() * aPoints[v].nY),
954                                        v);
955                     }
956                     rDev.SetLineColor(COL_YELLOW);
957                     rDev.SetFillColor(COL_BLACK);
958                     rDev.DrawPolygon(aPoly);
959 
960                     // now move and add to the polypolygon
961                     aPoly.Move(0, r.GetHeight()/2);
962                     aPolyPoly.Insert(aPoly);
963                 }
964             }
965             rDev.SetLineColor(COL_LIGHTRED);
966             rDev.SetFillColor(COL_GREEN);
967             rDev.DrawTransparent(aPolyPoly, 50);
968         }
969     };
970 
971     struct DrawClipped : public RegionRenderer
972     {
973         RENDER_DETAILS(clip,KEY_D,10)
RenderRegion__anonae1f6d930211::DemoRenderer::DrawClipped974         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
975                                   const RenderContext &) override
976         {
977             std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(r, 2, 2));
978             const int nLimits[] = { 4, -100 };
979             for (int i = 0; i < 2; ++i)
980             {
981                 sal_uInt16 nHue = 0;
982                 rDev.Push(PushFlags::CLIPREGION);
983                 tools::Rectangle aOuter = aRegions[i];
984                 tools::Rectangle aInner = aOuter;
985                 while (aInner.GetWidth() > nLimits[i] && aInner.GetHeight() > nLimits[i])
986                 {
987                     aInner.expand(-1);
988                     rDev.SetClipRegion(vcl::Region(aInner));
989                     rDev.SetFillColor(Color::HSBtoRGB(nHue, 75, 100));
990                     nHue = (nHue + 97) % 360;
991                     rDev.DrawRect(aOuter);
992                 }
993                 rDev.Pop();
994             }
995 
996             {
997                 sal_uInt16 nHue = 0;
998                 tools::Rectangle aOuter = aRegions[2];
999                 std::vector<tools::Rectangle> aPieces(DemoRenderer::partition(aOuter, 2, 2));
1000                 for (int j = 0; j < std::min(aOuter.GetWidth(), aOuter.GetHeight())/5; ++j)
1001                 {
1002                     rDev.Push(PushFlags::CLIPREGION);
1003 
1004                     vcl::Region aClipRegion;
1005                     for (int i = 0; i < 4; ++i)
1006                     {
1007                         aPieces[i].expand(-1);
1008                         aPieces[i].Move(2 - i/2, 2 - i/2);
1009                         aClipRegion.Union(aPieces[i]);
1010                     }
1011                     assert (aClipRegion.getRegionBand());
1012                     rDev.SetClipRegion(aClipRegion);
1013                     rDev.SetFillColor(Color::HSBtoRGB(nHue, 75, 75));
1014                     nHue = (nHue + 97) % 360;
1015                     rDev.DrawRect(aOuter);
1016 
1017                     rDev.Pop();
1018                 }
1019             }
1020 
1021             {
1022                 sal_uInt16 nHue = 0;
1023                 tools::Rectangle aOuter = aRegions[3];
1024                 std::vector<tools::Rectangle> aPieces(DemoRenderer::partition(aOuter, 2, 2));
1025                 bool bDone = false;
1026                 for (int j = 0; !bDone; ++j)
1027                 {
1028                     rDev.Push(PushFlags::CLIPREGION);
1029 
1030                     for (int i = 0; i < 4; ++i)
1031                     {
1032                         vcl::Region aClipRegion;
1033                         tools::Polygon aPoly;
1034                         switch (i) {
1035                         case 3:
1036                         case 0: // 45degree rectangle.
1037                             aPoly = tools::Polygon(aPieces[i]);
1038                             aPoly.Rotate(aPieces[i].Center(), 450_deg10);
1039                             break;
1040                         case 1: // arc
1041                             aPoly = tools::Polygon(aPieces[i],
1042                                                    aPieces[i].TopLeft(),
1043                                                    aPieces[i].BottomRight());
1044                             break;
1045                         case 2:
1046                             aPoly = tools::Polygon(aPieces[i],
1047                                                    aPieces[i].GetWidth()/5,
1048                                                    aPieces[i].GetHeight()/5);
1049                             aPoly.Rotate(aPieces[i].Center(), 450_deg10);
1050                             break;
1051                         }
1052                         aClipRegion = vcl::Region(aPoly);
1053                         aPieces[i].expand(-1);
1054                         aPieces[i].Move(2 - i/2, 2 - i/2);
1055 
1056                         bDone = aPieces[i].GetWidth() < 4 ||
1057                                 aPieces[i].GetHeight() < 4;
1058 
1059                         if (!bDone)
1060                         {
1061                             assert (!aClipRegion.getRegionBand());
1062 
1063                             rDev.SetClipRegion(aClipRegion);
1064                             rDev.SetFillColor(Color::HSBtoRGB(nHue, 50, 75));
1065                             nHue = (nHue + 97) % 360;
1066                             rDev.DrawRect(aOuter);
1067                         }
1068                     }
1069 
1070                     rDev.Pop();
1071                 }
1072             }
1073         }
1074     };
1075 
1076     struct DrawToVirtualDevice : public RegionRenderer
1077     {
1078         RENDER_DETAILS(vdev,KEY_V,1)
1079         enum RenderType {
1080             RENDER_AS_BITMAP,
1081             RENDER_AS_OUTDEV,
1082             RENDER_AS_BITMAPEX,
1083             RENDER_AS_ALPHA_OUTDEV
1084         };
1085 
SizeAndRender__anonae1f6d930211::DemoRenderer::DrawToVirtualDevice1086         static void SizeAndRender(OutputDevice &rDev, const tools::Rectangle& r, RenderType eType,
1087                                   const RenderContext &rCtx)
1088         {
1089             ScopedVclPtr<VirtualDevice> pNested;
1090 
1091             if (static_cast<int>(eType) < RENDER_AS_BITMAPEX)
1092                 pNested = VclPtr<VirtualDevice>::Create(rDev).get();
1093             else
1094                 pNested = VclPtr<VirtualDevice>::Create(rDev,DeviceFormat::DEFAULT,DeviceFormat::DEFAULT).get();
1095 
1096             pNested->SetOutputSizePixel(r.GetSize());
1097             tools::Rectangle aWhole(Point(0,0), r.GetSize());
1098 
1099             // mini me
1100             rCtx.mpDemoRenderer->drawToDevice(*pNested, r.GetSize(), true);
1101 
1102             if (eType == RENDER_AS_BITMAP)
1103             {
1104                 Bitmap aBitmap(pNested->GetBitmap(Point(0,0),aWhole.GetSize()));
1105                 rDev.DrawBitmap(r.TopLeft(), aBitmap);
1106             }
1107             else if (eType == RENDER_AS_BITMAPEX)
1108             {
1109                 BitmapEx aBitmapEx(pNested->GetBitmapEx(Point(0,0),aWhole.GetSize()));
1110                 rDev.DrawBitmapEx(r.TopLeft(), aBitmapEx);
1111             }
1112             else if (eType == RENDER_AS_OUTDEV ||
1113                      eType == RENDER_AS_ALPHA_OUTDEV)
1114             {
1115                 rDev.DrawOutDev(r.TopLeft(), r.GetSize(),
1116                                 aWhole.TopLeft(), aWhole.GetSize(),
1117                                 *pNested);
1118             }
1119         }
RenderRegion__anonae1f6d930211::DemoRenderer::DrawToVirtualDevice1120         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1121                                   const RenderContext &rCtx) override
1122         {
1123             // avoid infinite recursion
1124             if (rCtx.mbVDev)
1125                 return;
1126 
1127             if (rCtx.meStyle == RENDER_EXPANDED)
1128             {
1129                 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx,2, 2));
1130                 DemoRenderer::clearRects(rDev, aRegions);
1131 
1132                 RenderType const eRenderTypes[] { RENDER_AS_BITMAP, RENDER_AS_OUTDEV,
1133                                                   RENDER_AS_BITMAPEX, RENDER_AS_ALPHA_OUTDEV };
1134                 for (size_t i = 0; i < aRegions.size(); i++)
1135                     SizeAndRender(rDev, aRegions[i], eRenderTypes[i], rCtx);
1136             }
1137             else
1138                 SizeAndRender(rDev, r, RENDER_AS_BITMAP, rCtx);
1139         }
1140     };
1141 
1142     struct DrawXOR : public RegionRenderer
1143     {
1144         RENDER_DETAILS(xor,KEY_X,1)
1145 
RenderRegion__anonae1f6d930211::DemoRenderer::DrawXOR1146         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1147                                   const RenderContext &rCtx) override
1148         {
1149             // avoid infinite recursion
1150             if (rCtx.mbVDev)
1151                 return;
1152 
1153             rDev.Push();
1154 
1155             AntialiasingFlags nFlags = rDev.GetAntialiasing();
1156             rDev.SetAntialiasing(nFlags & ~AntialiasingFlags::Enable);
1157             rDev.SetRasterOp( RasterOp::Xor );
1158 
1159             rCtx.mpDemoRenderer->drawThumbs(rDev, r, true);
1160 
1161             rDev.Pop();
1162         }
1163     };
1164 
1165     struct DrawIcons : public RegionRenderer
1166     {
1167         RENDER_DETAILS(icons,KEY_I,1)
1168 
1169         std::vector<OUString> maIconNames;
1170         std::vector<BitmapEx> maIcons;
1171         bool bHasLoadedAll;
DrawIcons__anonae1f6d930211::DemoRenderer::DrawIcons1172         DrawIcons() : bHasLoadedAll(false)
1173         {
1174             // a few icons to start with
1175             const char *pNames[] = {
1176                 "cmd/lc_openurl.png",
1177                 "cmd/lc_newdoc.png",
1178                 "cmd/lc_choosemacro.png",
1179                 "cmd/lc_save.png",
1180                 "cmd/lc_saveas.png",
1181                 "cmd/lc_importdialog.png",
1182                 "cmd/lc_sendmail.png",
1183                 "cmd/lc_editdoc.png",
1184                 "cmd/lc_print.png",
1185                 "cmd/lc_combobox.png",
1186                 "cmd/lc_insertformcombo.png",
1187                 "cmd/lc_printpreview.png",
1188                 "cmd/lc_cut.png",
1189                 "cmd/lc_copy.png",
1190                 "cmd/lc_paste.png",
1191                 "cmd/sc_autopilotmenu.png",
1192                 "cmd/lc_formatpaintbrush.png",
1193                 "cmd/lc_undo.png",
1194                 "cmd/lc_redo.png",
1195                 "cmd/lc_marks.png",
1196                 "cmd/lc_fieldnames.png",
1197                 "cmd/lc_hyperlinkdialog.png",
1198                 "cmd/lc_basicshapes.rectangle.png",
1199                 "cmd/lc_basicshapes.round-rectangle.png"
1200             };
1201             for (size_t i = 0; i < SAL_N_ELEMENTS(pNames); i++)
1202             {
1203                 maIconNames.push_back(OUString::createFromAscii(pNames[i]));
1204                 maIcons.emplace_back(maIconNames[i]);
1205             }
1206         }
1207 
LoadAllImages__anonae1f6d930211::DemoRenderer::DrawIcons1208         void LoadAllImages()
1209         {
1210             if (bHasLoadedAll)
1211                 return;
1212             bHasLoadedAll = true;
1213 
1214             css::uno::Reference<css::container::XNameAccess> xRef(ImageTree::get().getNameAccess());
1215             const css::uno::Sequence< OUString > aAllIcons = xRef->getElementNames();
1216 
1217             for (const auto& rIcon : aAllIcons)
1218             {
1219                 if (rIcon.endsWithIgnoreAsciiCase("svg"))
1220                     continue; // too slow to load.
1221                 maIconNames.push_back(rIcon);
1222                 maIcons.emplace_back(rIcon);
1223             }
1224         }
1225 
doDrawIcons__anonae1f6d930211::DemoRenderer::DrawIcons1226         void doDrawIcons(OutputDevice &rDev, tools::Rectangle r, bool bExpanded)
1227         {
1228             tools::Long nMaxH = 0;
1229             Point p(r.LeftCenter());
1230             size_t nToRender = maIcons.size();
1231 
1232             if (!bExpanded && maIcons.size() > 64)
1233                 nToRender = 64;
1234             for (size_t i = 0; i < nToRender; i++)
1235             {
1236                 Size aSize(maIcons[i].GetSizePixel());
1237 //              sAL_DEBUG("Draw icon '" << maIconNames[i] << "'");
1238 
1239                 if (!(i % 4))
1240                     rDev.DrawBitmapEx(p, maIcons[i]);
1241                 else
1242                 {
1243                     basegfx::B2DHomMatrix aTransform;
1244                     aTransform.scale(aSize.Width(), aSize.Height());
1245                     switch (i % 4)
1246                     {
1247                     case 2:
1248                         aTransform.shearX(static_cast<double>((i >> 2) % 8) / 8);
1249                         aTransform.shearY(static_cast<double>((i >> 4) % 8) / 8);
1250                         break;
1251                     case 3:
1252                         aTransform.translate(-aSize.Width()/2, -aSize.Height()/2);
1253                         aTransform.rotate(i);
1254                         if (i & 0x100)
1255                         {
1256                             aTransform.shearX(static_cast<double>((i >> 2) % 8) / 8);
1257                             aTransform.shearY(static_cast<double>((i >> 4) % 8) / 8);
1258                         }
1259                         aTransform.translate(aSize.Width()/2,  aSize.Height()/2);
1260                         break;
1261                     default:
1262                         aTransform.translate(-aSize.Width()/2, -aSize.Height()/2);
1263                         aTransform.rotate(2 * F_2PI * i / nToRender);
1264                         aTransform.translate(aSize.Width()/2,  aSize.Height()/2);
1265                         break;
1266                     }
1267                     aTransform.translate(p.X(), p.Y());
1268                     rDev.DrawTransformedBitmapEx(aTransform, maIcons[i]);
1269                 }
1270 
1271                 // next position
1272                 p.Move(aSize.Width(), 0);
1273                 if (aSize.Height() > nMaxH)
1274                     nMaxH = aSize.Height();
1275                 if (p.X() >= r.Right()) // wrap to next line
1276                 {
1277                     p = Point(r.Left(), p.Y() + nMaxH);
1278                     nMaxH = 0;
1279                 }
1280                 if (p.Y() >= r.Bottom()) // re-start at middle
1281                     p = r.LeftCenter();
1282             }
1283         }
1284 
AlphaRecovery__anonae1f6d930211::DemoRenderer::DrawIcons1285         static BitmapEx AlphaRecovery(OutputDevice &rDev, Point aPt, BitmapEx const &aSrc)
1286         {
1287             // Compositing onto 2x colors beyond our control
1288             ScopedVclPtrInstance< VirtualDevice > aWhite;
1289             ScopedVclPtrInstance< VirtualDevice > aBlack;
1290             aWhite->SetOutputSizePixel(aSrc.GetSizePixel());
1291             aWhite->SetBackground(Wallpaper(COL_WHITE));
1292             aWhite->Erase();
1293             aBlack->SetOutputSizePixel(aSrc.GetSizePixel());
1294             aBlack->SetBackground(Wallpaper(COL_BLACK));
1295             aBlack->Erase();
1296             aWhite->DrawBitmapEx(Point(), aSrc);
1297             aBlack->DrawBitmapEx(Point(), aSrc);
1298 
1299             // Now recover that alpha...
1300             Bitmap aWhiteBmp = aWhite->GetBitmap(Point(),aSrc.GetSizePixel());
1301             Bitmap aBlackBmp = aBlack->GetBitmap(Point(),aSrc.GetSizePixel());
1302             AlphaMask aMask(aSrc.GetSizePixel());
1303             Bitmap aRecovered(aSrc.GetSizePixel(), vcl::PixelFormat::N24_BPP);
1304             {
1305                 AlphaScopedWriteAccess pMaskAcc(aMask);
1306                 BitmapScopedWriteAccess pRecAcc(aRecovered);
1307                 Bitmap::ScopedReadAccess pAccW(aWhiteBmp); // a * pix + (1-a)
1308                 Bitmap::ScopedReadAccess pAccB(aBlackBmp); // a * pix + 0
1309                 int nSizeX = aSrc.GetSizePixel().Width();
1310                 int nSizeY = aSrc.GetSizePixel().Height();
1311                 for (int y = 0; y < nSizeY; y++)
1312                 {
1313                     Scanline pScanlineMask = pMaskAcc->GetScanline( y );
1314                     Scanline pScanlineRec = pRecAcc->GetScanline( y );
1315                     Scanline pScanlineW = pAccW->GetScanline( y );
1316                     Scanline pScanlineB = pAccB->GetScanline( y );
1317                     for (int x = 0; x < nSizeX; x++)
1318                     {
1319                         BitmapColor aColW = pAccW->GetPixelFromData(pScanlineW,x);
1320                         BitmapColor aColB = pAccB->GetPixelFromData(pScanlineB,x);
1321                         tools::Long nAR = static_cast<tools::Long>(aColW.GetRed() - aColB.GetRed()); // (1-a)
1322                         tools::Long nAG = static_cast<tools::Long>(aColW.GetGreen() - aColB.GetGreen()); // (1-a)
1323                         tools::Long nAB = static_cast<tools::Long>(aColW.GetBlue() - aColB.GetBlue()); // (1-a)
1324 
1325 #define CLAMP(a,b,c) (((a)<=(b))?(b):(((a)>=(c))?(c):(a)))
1326 
1327                         // we get the most precision from the largest delta
1328                         tools::Long nInverseAlpha = std::max(nAR, std::max(nAG, nAB)); // (1-a)
1329                         nInverseAlpha = CLAMP(nInverseAlpha, 0, 255);
1330                         tools::Long nAlpha = 255 - nInverseAlpha;
1331 
1332                         pMaskAcc->SetPixelOnData(pScanlineMask,x,BitmapColor(static_cast<sal_Int8>(CLAMP(nInverseAlpha,0,255))));
1333                         // now recover the pixels
1334                         tools::Long nR = (aColW.GetRed() + aColB.GetRed() - nInverseAlpha) * 128;
1335                         tools::Long nG = (aColW.GetGreen() + aColB.GetGreen() - nInverseAlpha) * 128;
1336                         tools::Long nB = (aColW.GetBlue() + aColB.GetBlue() - nInverseAlpha) * 128;
1337                         if (nAlpha == 0)
1338                         { // doesn't matter what's behind transparency
1339                             nR = nG = nB = 0;
1340                         }
1341                         else
1342                         {
1343                             nR /= nAlpha; nG /= nAlpha; nB /= nAlpha;
1344                         }
1345                         pRecAcc->SetPixelOnData(pScanlineRec,x,BitmapColor(
1346                                                 static_cast<sal_uInt8>(CLAMP(nR,0,255)),
1347                                                 static_cast<sal_uInt8>(CLAMP(nG,0,255)),
1348                                                 static_cast<sal_uInt8>(CLAMP(nB,0,255))));
1349 #undef CLAMP
1350                     }
1351                 }
1352             }
1353             rDev.DrawBitmap(aPt, aWhiteBmp);
1354             aPt.Move(aSrc.GetSizePixel().Width(), 0);
1355             rDev.DrawBitmap(aPt, aBlackBmp);
1356             aPt.Move(aSrc.GetSizePixel().Width(), 0);
1357             rDev.DrawBitmap(aPt, aRecovered);
1358             aPt.Move(aSrc.GetSizePixel().Width(), 0);
1359             rDev.DrawBitmap(aPt, aMask.GetBitmap());
1360             aPt.Move(aSrc.GetSizePixel().Width(), 0);
1361 
1362             return BitmapEx(aRecovered, aMask);
1363         }
1364 
RenderRegion__anonae1f6d930211::DemoRenderer::DrawIcons1365         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1366                                   const RenderContext &rCtx) override
1367         {
1368             if (rCtx.meStyle == RENDER_EXPANDED)
1369             {
1370                 LoadAllImages();
1371 
1372                 Point aLocation(0,maIcons[0].GetSizePixel().Height() + 8);
1373                 for (size_t i = 0; i < maIcons.size(); i++)
1374                 {
1375                     BitmapEx aSrc = maIcons[i];
1376 
1377                     // original above
1378                     Point aAbove(aLocation);
1379                     aAbove.Move(0,-aSrc.GetSizePixel().Height() - 4);
1380                     rDev.DrawBitmapEx(aAbove, aSrc);
1381                     aAbove.Move(aSrc.GetSizePixel().Width(),0);
1382                     aAbove.Move(aSrc.GetSizePixel().Width(),0);
1383                     rDev.DrawBitmap(aAbove, aSrc.GetBitmap());
1384                     aAbove.Move(aSrc.GetSizePixel().Width(),0);
1385                     rDev.DrawBitmap(aAbove, aSrc.GetAlpha());
1386 
1387                     // intermediates middle
1388                     BitmapEx aResult = AlphaRecovery(rDev, aLocation, aSrc);
1389 
1390                     // result below
1391                     Point aBelow(aLocation);
1392                     aBelow.Move(0,aResult.GetSizePixel().Height());
1393                     rDev.DrawBitmapEx(aBelow, aResult);
1394 
1395                     // mini convert test.
1396                     aBelow.Move(aResult.GetSizePixel().Width()+4,0);
1397                     rDev.DrawBitmapEx(aBelow, aResult);
1398 
1399                     Bitmap aGrey = aSrc.GetBitmap();
1400                     aGrey.Convert(BmpConversion::N8BitGreys);
1401                     rDev.DrawBitmap(aBelow, aGrey);
1402 
1403                     aBelow.Move(aGrey.GetSizePixel().Width(),0);
1404                     BitmapEx aGreyMask(aSrc.GetBitmap(),
1405                                        aSrc.GetAlpha());
1406                     rDev.DrawBitmapEx(aBelow, aGreyMask);
1407 
1408                     aLocation.Move(aSrc.GetSizePixel().Width()*6,0);
1409                     if (aLocation.X() > r.Right())
1410                         aLocation = Point(0,aLocation.Y()+aSrc.GetSizePixel().Height()*3+4);
1411                 }
1412 
1413                 // now go crazy with random foo
1414                 doDrawIcons(rDev, r, true);
1415             }
1416             else
1417             {
1418                 doDrawIcons(rDev, r, false);
1419             }
1420         }
1421     };
1422 
1423     struct FetchDrawBitmap : public RegionRenderer
1424     {
1425         RENDER_DETAILS(fetchdraw,KEY_F,50)
RenderRegion__anonae1f6d930211::DemoRenderer::FetchDrawBitmap1426         virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1427                                   const RenderContext &) override
1428         {
1429             Bitmap aBitmap(rDev.GetBitmap(Point(0,0),rDev.GetOutputSizePixel()));
1430             aBitmap.Scale(r.GetSize(), BmpScaleFlag::BestQuality);
1431             rDev.DrawBitmap(r.TopLeft(), aBitmap);
1432         }
1433     };
1434 
drawThumbs(vcl::RenderContext & rDev,tools::Rectangle aRect,bool bVDev)1435     void drawThumbs(vcl::RenderContext& rDev, tools::Rectangle aRect, bool bVDev)
1436     {
1437         RenderContext aCtx;
1438         aCtx.meStyle = RENDER_THUMB;
1439         aCtx.mbVDev = bVDev;
1440         aCtx.mpDemoRenderer = this;
1441         aCtx.maSize = aRect.GetSize();
1442         std::vector<tools::Rectangle> aRegions(partition(aRect, mnSegmentsX, mnSegmentsY));
1443         DemoRenderer::clearRects(rDev, aRegions);
1444         for (size_t i = 0; i < maRenderers.size(); i++)
1445         {
1446             RegionRenderer * r = maRenderers[i];
1447 
1448             rDev.SetClipRegion( vcl::Region( aRegions[i] ) );
1449 
1450             // profiling?
1451             if (getIterCount() > 0)
1452             {
1453                 if (!bVDev)
1454                 {
1455                     double nStartTime = getTimeNow();
1456                     for (int j = 0; j < r->getTestRepeatCount() * THUMB_REPEAT_FACTOR; j++)
1457                         r->RenderRegion(rDev, aRegions[i], aCtx);
1458                     addTime(i, (getTimeNow() - nStartTime) / THUMB_REPEAT_FACTOR);
1459                 } else
1460                     for (int j = 0; j < r->getTestRepeatCount(); j++)
1461                         r->RenderRegion(rDev, aRegions[i], aCtx);
1462             }
1463             else
1464                 r->RenderRegion(rDev, aRegions[i], aCtx);
1465 
1466             rDev.SetClipRegion();
1467         }
1468     }
1469 
drawToDevice(vcl::RenderContext & rDev,Size aSize,bool bVDev)1470     void drawToDevice(vcl::RenderContext& rDev, Size aSize, bool bVDev)
1471     {
1472         RenderContext aCtx;
1473         aCtx.mbVDev = bVDev;
1474         aCtx.mpDemoRenderer = this;
1475         aCtx.maSize = aSize;
1476         tools::Rectangle aWholeWin(Point(0,0), rDev.GetOutputSizePixel());
1477 
1478         drawBackground(rDev, aWholeWin);
1479 
1480         if (!bVDev /* want everything in the vdev */ &&
1481             mnSelectedRenderer >= 0 &&
1482             o3tl::make_unsigned(mnSelectedRenderer) < maRenderers.size())
1483         {
1484             aCtx.meStyle = RENDER_EXPANDED;
1485             RegionRenderer * r = maRenderers[mnSelectedRenderer];
1486             // profiling?
1487             if (getIterCount() > 0)
1488             {
1489                 double nStartTime = getTimeNow();
1490                 for (int i = 0; i < r->getTestRepeatCount(); i++)
1491                     r->RenderRegion(rDev, aWholeWin, aCtx);
1492                 addTime(mnSelectedRenderer, getTimeNow() - nStartTime);
1493             } else
1494                 r->RenderRegion(rDev, aWholeWin, aCtx);
1495         }
1496         else
1497             drawThumbs(rDev, aWholeWin, bVDev);
1498     }
1499     std::vector<VclPtr<vcl::Window> > maInvalidates;
addInvalidate(vcl::Window * pWindow)1500     void addInvalidate(vcl::Window *pWindow) { maInvalidates.emplace_back(pWindow); };
removeInvalidate(vcl::Window * pWindow)1501     void removeInvalidate(vcl::Window *pWindow)
1502     {
1503         auto aIt = std::find(maInvalidates.begin(), maInvalidates.end(), pWindow);
1504         if (aIt != maInvalidates.end())
1505             maInvalidates.erase(aIt);
1506     }
Invalidate()1507     void Invalidate()
1508     {
1509         for (auto const& invalidate : maInvalidates)
1510             invalidate->Invalidate();
1511     }
1512 };
1513 
1514 }
1515 
1516 #if FIXME_BOUNCE_BUTTON
IMPL_LINK_NOARG(DemoRenderer,BounceTimerCb,Timer *,void)1517 IMPL_LINK_NOARG(DemoRenderer,BounceTimerCb,Timer*,void)
1518 {
1519     mpButton->Check(mnBounceX>0);
1520     mpButton->SetPressed(mnBounceY>0);
1521 
1522     Point aCur = mpButtonWin->GetPosPixel();
1523     static const int nMovePix = 10;
1524     aCur.Move(mnBounceX * nMovePix, mnBounceX * nMovePix);
1525     Size aWinSize = GetSizePixel();
1526     if (aCur.X() <= 0 || aCur.X() >= aWinSize.Width())
1527         mnBounceX *= -1;
1528     if (aCur.Y() <= 0 || aCur.Y() >= aWinSize.Height())
1529         mnBounceX *= -1;
1530     mpButtonWin->SetPosPixel(aCur);
1531 
1532     // All smoke and mirrors to test sub-region invalidation underneath
1533     Rectangle aRect(aCur, mpButtonWin->GetSizePixel());
1534     Invalidate(aRect);
1535 }
1536 #endif
1537 
KeyInput(const KeyEvent & rKEvt)1538 void DemoRenderer::KeyInput(const KeyEvent &rKEvt)
1539 {
1540     sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
1541 
1542     // click to zoom out
1543     if (mnSelectedRenderer >= 0)
1544     {
1545         if (nCode == KEY_ESCAPE || nCode == KEY_BACKSPACE)
1546         {
1547             mnSelectedRenderer = -1;
1548             Invalidate();
1549             return;
1550         }
1551     }
1552     else
1553     {
1554         for (size_t i = 0; i < maRenderers.size(); i++)
1555         {
1556             if (nCode == maRenderers[i]->getAccelerator())
1557             {
1558                 mnSelectedRenderer = i;
1559                 Invalidate();
1560                 return;
1561             }
1562         }
1563     }
1564 }
1565 
MouseButtonDown(const MouseEvent & rMEvt)1566 bool DemoRenderer::MouseButtonDown(const MouseEvent& rMEvt)
1567 {
1568     // click to zoom out
1569     if (mnSelectedRenderer >= 0)
1570     {
1571         mnSelectedRenderer = -1;
1572         Invalidate();
1573         return true;
1574     }
1575 
1576     // click on a region to zoom into it
1577     std::vector<tools::Rectangle> aRegions(partition(GetSizePixel(), mnSegmentsX, mnSegmentsY));
1578     for (size_t i = 0; i < aRegions.size(); i++)
1579     {
1580         if (aRegions[i].IsInside(rMEvt.GetPosPixel()))
1581         {
1582             mnSelectedRenderer = i;
1583             Invalidate();
1584             return true;
1585         }
1586     }
1587 
1588 #if FIXME_BOUNCE_BUTTON
1589     // otherwise bounce floating windows
1590     if (!mpButton)
1591     {
1592         mpButtonWin = VclPtr<FloatingWindow>::Create(this);
1593         mpButton = VclPtr<PushButton>::Create(mpButtonWin);
1594         mpButton->SetSymbol(SymbolType::HELP);
1595         mpButton->SetText("PushButton demo");
1596         mpButton->SetPosSizePixel(Point(0,0), mpButton->GetOptimalSize());
1597         mpButton->Show();
1598         mpButtonWin->SetPosSizePixel(Point(0,0), mpButton->GetOptimalSize());
1599         mpButtonWin->Show();
1600         mnBounceX = 1; mnBounceX = 1;
1601         maBounce.SetInvokeHandler(LINK(this,DemoRenderer,BounceTimerCb));
1602         maBounce.SetTimeout(55);
1603         maBounce.Start();
1604     }
1605     else
1606     {
1607         maBounce.Stop();
1608         delete mpButtonWin;
1609         mpButtonWin = NULL;
1610         mpButton = NULL;
1611     }
1612 #endif
1613     return false;
1614 }
1615 
InitRenderers()1616 void DemoRenderer::InitRenderers()
1617 {
1618     maRenderers.push_back(new DrawLines);
1619     maRenderers.push_back(new DrawText);
1620     maRenderers.push_back(new DrawPoly);
1621     maRenderers.push_back(new DrawEllipse);
1622     maRenderers.push_back(new DrawCheckered);
1623     maRenderers.push_back(new DrawBitmapEx);
1624     maRenderers.push_back(new DrawBitmap);
1625     maRenderers.push_back(new DrawGradient);
1626     maRenderers.push_back(new DrawPolyPolygons);
1627     maRenderers.push_back(new DrawClipped);
1628     maRenderers.push_back(new DrawToVirtualDevice);
1629     maRenderers.push_back(new DrawXOR);
1630     maRenderers.push_back(new DrawIcons());
1631     maRenderers.push_back(new FetchDrawBitmap);
1632 }
1633 
getRendererList()1634 OUString DemoRenderer::getRendererList()
1635 {
1636     OUStringBuffer aBuf;
1637     for (size_t i = 0; i < maRenderers.size(); i++)
1638     {
1639         aBuf.append(maRenderers[i]->getName());
1640         aBuf.append(' ');
1641     }
1642     return aBuf.makeStringAndClear();
1643 }
1644 
getAndResetBenchmark(const RenderStyle style)1645 double DemoRenderer::getAndResetBenchmark(const RenderStyle style)
1646 {
1647     double geomean = 1.0;
1648     fprintf(stderr, "Rendering: %s, Times (ms):\n", style == RENDER_THUMB ? "THUMB": "EXPANDED");
1649     for (size_t i = 0; i < maRenderers.size(); i++)
1650     {
1651         double avgtime = maRenderers[i]->sumTime / maRenderers[i]->countTime;
1652         geomean *= avgtime;
1653         fprintf(stderr, "%s: %f (iteration: %d*%d*%d)\n",
1654                 OUStringToOString(maRenderers[i]->getName(),
1655                 RTL_TEXTENCODING_UTF8).getStr(), avgtime,
1656                 maRenderers[i]->countTime, maRenderers[i]->getTestRepeatCount(),
1657                 (style == RENDER_THUMB) ? THUMB_REPEAT_FACTOR : 1);
1658         maRenderers[i]->sumTime = 0;
1659         maRenderers[i]->countTime = 0;
1660     }
1661     geomean = pow(geomean, 1.0/maRenderers.size());
1662     fprintf(stderr, "GEOMEAN_%s: %f\n", style == RENDER_THUMB ? "THUMB": "EXPANDED", geomean);
1663     return geomean;
1664 }
1665 
setIterCount(sal_Int32 i)1666 void DemoRenderer::setIterCount(sal_Int32 i)
1667 {
1668     iterCount = i;
1669 }
1670 
getIterCount() const1671 sal_Int32 DemoRenderer::getIterCount() const
1672 {
1673     return iterCount;
1674 }
1675 
addTime(int i,double t)1676 void DemoRenderer::addTime(int i, double t)
1677 {
1678     maRenderers[i]->sumTime += t / maRenderers[i]->getTestRepeatCount();
1679     maRenderers[i]->countTime++;
1680 }
1681 
selectRenderer(std::u16string_view rName)1682 void DemoRenderer::selectRenderer(std::u16string_view rName )
1683 {
1684     for (size_t i = 0; i < maRenderers.size(); i++)
1685     {
1686         if (maRenderers[i]->getName() == rName)
1687         {
1688             mnSelectedRenderer = i;
1689             Invalidate();
1690             return;
1691         }
1692     }
1693 }
1694 
selectNextRenderer()1695 int DemoRenderer::selectNextRenderer()
1696 {
1697     mnSelectedRenderer++;
1698     if (mnSelectedRenderer == static_cast<signed>(maRenderers.size()))
1699         mnSelectedRenderer = -1;
1700     Invalidate();
1701     return mnSelectedRenderer;
1702 }
1703 
1704 namespace {
1705 
1706 class DemoWin : public WorkWindow
1707 {
1708     DemoRenderer &mrRenderer;
1709     bool underTesting;
1710     bool testThreads;
1711 
1712     class RenderThread final : public salhelper::Thread {
1713         DemoWin  &mrWin;
1714         sal_uInt32 const mnDelaySecs = 0;
1715     public:
RenderThread(DemoWin & rWin,sal_uInt32 nDelaySecs)1716         RenderThread(DemoWin &rWin, sal_uInt32 nDelaySecs)
1717             : Thread("vcldemo render thread")
1718             , mrWin(rWin)
1719             , mnDelaySecs(nDelaySecs)
1720         {
1721             launch();
1722         }
~RenderThread()1723         virtual ~RenderThread() override
1724         {
1725             join();
1726         }
execute()1727         virtual void execute() override
1728         {
1729             wait(std::chrono::seconds(mnDelaySecs));
1730 
1731             SolarMutexGuard aGuard;
1732             fprintf (stderr, "render from a different thread\n");
1733             mrWin.Invalidate();
1734         }
1735     };
1736     rtl::Reference<RenderThread> mxThread;
1737 
1738 public:
DemoWin(DemoRenderer & rRenderer,bool bThreads)1739     DemoWin(DemoRenderer &rRenderer, bool bThreads) :
1740         WorkWindow(nullptr, WB_APP | WB_STDWORK),
1741         mrRenderer(rRenderer),
1742         testThreads(bThreads)
1743     {
1744         mrRenderer.addInvalidate(this);
1745         underTesting = false;
1746     }
~DemoWin()1747     virtual ~DemoWin() override
1748     {
1749         disposeOnce();
1750     }
dispose()1751     virtual void dispose() override
1752     {
1753         mxThread.clear();
1754         mrRenderer.removeInvalidate(this);
1755         WorkWindow::dispose();
1756     }
MouseButtonDown(const MouseEvent & rMEvt)1757     virtual void MouseButtonDown(const MouseEvent& rMEvt) override
1758     {
1759         mrRenderer.SetSizePixel(GetSizePixel());
1760         if (mrRenderer.MouseButtonDown(rMEvt))
1761             return;
1762 
1763         if (testThreads)
1764         { // render this window asynchronously in a new thread
1765             sal_uInt32 nDelaySecs = 0;
1766             if (rMEvt.GetButtons() & MOUSE_RIGHT)
1767                 nDelaySecs = 5;
1768             mxThread = new RenderThread(*this, nDelaySecs);
1769         }
1770         else
1771         { // spawn another window
1772             VclPtrInstance<DemoWin> pNewWin(mrRenderer, testThreads);
1773             pNewWin->SetText("Another interactive VCL demo window");
1774             pNewWin->Show();
1775         }
1776     }
KeyInput(const KeyEvent & rKEvt)1777     virtual void KeyInput(const KeyEvent& rKEvt) override
1778     {
1779         mrRenderer.SetSizePixel(GetSizePixel());
1780         mrRenderer.KeyInput(rKEvt);
1781     }
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle & rRect)1782     virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override
1783     {
1784         mrRenderer.SetSizePixel(GetSizePixel());
1785         fprintf(stderr, "DemoWin::Paint(%" SAL_PRIdINT64 ",%" SAL_PRIdINT64 ",%" SAL_PRIdINT64 ",%" SAL_PRIdINT64 ")\n", sal_Int64(rRect.getX()), sal_Int64(rRect.getY()), sal_Int64(rRect.getWidth()), sal_Int64(rRect.getHeight()));
1786         if (mrRenderer.getIterCount() == 0)
1787             mrRenderer.drawToDevice(rRenderContext, GetSizePixel(), false);
1788         else
1789             TestAndQuit(rRenderContext);
1790     }
1791 
TestAndQuit(vcl::RenderContext & rRenderContext)1792     void TestAndQuit(vcl::RenderContext& rRenderContext)
1793     {
1794         if (underTesting)
1795             return;
1796         underTesting = true;
1797         for (sal_Int32 i = 0; i < mrRenderer.getIterCount(); i++)
1798         {
1799             while (mrRenderer.selectNextRenderer() > -1)
1800             {
1801                 mrRenderer.drawToDevice(rRenderContext, GetSizePixel(), false);
1802             }
1803         }
1804 
1805         double expandedGEOMEAN = mrRenderer.getAndResetBenchmark(RENDER_EXPANDED);
1806 
1807         for (sal_Int32 i = 0; i < mrRenderer.getIterCount(); i++)
1808             mrRenderer.drawToDevice(rRenderContext, GetSizePixel(), false);
1809 
1810         double thumbGEOMEAN = mrRenderer.getAndResetBenchmark(RENDER_THUMB);
1811 
1812         fprintf(stderr, "GEOMEAN_TOTAL: %f\n", pow(thumbGEOMEAN * expandedGEOMEAN, 0.5));
1813         Application::Quit();
1814     }
1815 };
1816 
1817 struct PointerData {
1818     PointerStyle eStyle;
1819     const char * name;
1820 };
1821 
1822 }
1823 
1824 const PointerData gvPointerData [] = {
1825     { PointerStyle::Null, "Null" },
1826     { PointerStyle::Magnify, "Magnify" },
1827     { PointerStyle::Fill, "Fill" },
1828     { PointerStyle::MoveData, "MoveData" },
1829     { PointerStyle::CopyData, "CopyData" },
1830     { PointerStyle::MoveFile, "MoveFile" },
1831     { PointerStyle::CopyFile, "CopyFile" },
1832     { PointerStyle::MoveFiles, "MoveFiles" },
1833     { PointerStyle::CopyFiles, "CopyFiles" },
1834     { PointerStyle::NotAllowed, "NotAllowed" },
1835     { PointerStyle::Rotate, "Rotate" },
1836     { PointerStyle::HShear, "HShear" },
1837     { PointerStyle::VShear, "VShear" },
1838     { PointerStyle::DrawLine, "DrawLine" },
1839     { PointerStyle::DrawRect, "DrawRect" },
1840     { PointerStyle::DrawPolygon, "DrawPolygon" },
1841     { PointerStyle::DrawBezier, "DrawBezier" },
1842     { PointerStyle::DrawArc, "DrawArc" },
1843     { PointerStyle::DrawPie, "DrawPie" },
1844     { PointerStyle::DrawCircleCut, "DrawCircleCut" },
1845     { PointerStyle::DrawEllipse, "DrawEllipse" },
1846     { PointerStyle::DrawConnect, "DrawConnect" },
1847     { PointerStyle::DrawText, "DrawText" },
1848     { PointerStyle::Mirror, "Mirror" },
1849     { PointerStyle::Crook, "Crook" },
1850     { PointerStyle::Crop, "Crop" },
1851     { PointerStyle::MovePoint, "MovePoint" },
1852     { PointerStyle::MoveBezierWeight, "MoveBezierWeight" },
1853     { PointerStyle::DrawFreehand, "DrawFreehand" },
1854     { PointerStyle::DrawCaption, "DrawCaption" },
1855     { PointerStyle::LinkData, "LinkData" },
1856     { PointerStyle::MoveDataLink, "MoveDataLink" },
1857     { PointerStyle::CopyDataLink, "CopyDataLink" },
1858     { PointerStyle::LinkFile, "LinkFile" },
1859     { PointerStyle::MoveFileLink, "MoveFileLink" },
1860     { PointerStyle::CopyFileLink, "CopyFileLink" },
1861     { PointerStyle::Chart, "Chart" },
1862     { PointerStyle::Detective, "Detective" },
1863     { PointerStyle::PivotCol, "PivotCol" },
1864     { PointerStyle::PivotRow, "PivotRow" },
1865     { PointerStyle::PivotField, "PivotField" },
1866     { PointerStyle::PivotDelete, "PivotDelete" },
1867     { PointerStyle::Chain, "Chain" },
1868     { PointerStyle::ChainNotAllowed, "ChainNotAllowed" },
1869     { PointerStyle::AutoScrollN, "AutoScrollN" },
1870     { PointerStyle::AutoScrollS, "AutoScrollS" },
1871     { PointerStyle::AutoScrollW, "AutoScrollW" },
1872     { PointerStyle::AutoScrollE, "AutoScrollE" },
1873     { PointerStyle::AutoScrollNW, "AutoScrollNW" },
1874     { PointerStyle::AutoScrollNE, "AutoScrollNE" },
1875     { PointerStyle::AutoScrollSW, "AutoScrollSW" },
1876     { PointerStyle::AutoScrollSE, "AutoScrollSE" },
1877     { PointerStyle::AutoScrollNS, "AutoScrollNS" },
1878     { PointerStyle::AutoScrollWE, "AutoScrollWE" },
1879     { PointerStyle::AutoScrollNSWE, "AutoScrollNSWE" },
1880     { PointerStyle::TextVertical, "TextVertical" },
1881     { PointerStyle::TabSelectS, "TabSelectS" },
1882     { PointerStyle::TabSelectE, "TabSelectE" },
1883     { PointerStyle::TabSelectSE, "TabSelectSE" },
1884     { PointerStyle::TabSelectW, "TabSelectW" },
1885     { PointerStyle::TabSelectSW, "TabSelectSW" },
1886     { PointerStyle::HideWhitespace, "HideWhitespace" },
1887     { PointerStyle::ShowWhitespace, "ShowWhitespace" },
1888     { PointerStyle::FatCross, "FatCross" },
1889 };
1890 
1891 namespace {
1892 
1893 class DemoWidgets : public WorkWindow
1894 {
1895     VclPtr<MenuBar> mpBar;
1896     VclPtr<VclBox> mpBox;
1897     VclPtr<ToolBox> mpToolbox;
1898     VclPtr<PushButton> mpButton;
1899     std::vector<VclPtr<VclHBox>> mvCursorBoxes;
1900     std::vector<VclPtr<PushButton>> mvCursorButtons;
1901 
1902     DECL_LINK(CursorButtonClick, Button*, void);
1903 
1904 public:
DemoWidgets()1905     DemoWidgets() :
1906         WorkWindow(nullptr, WB_APP | WB_STDWORK),
1907         mpBox(VclPtrInstance<VclVBox>(this, false, 3)),
1908         mpToolbox(VclPtrInstance<ToolBox>(mpBox.get())),
1909         mpButton(VclPtrInstance<PushButton>(mpBox.get()))
1910     {
1911         SetText("VCL widget demo");
1912 
1913         Wallpaper aWallpaper(BitmapEx("sfx2/res/128x128_writer_doc-p.png"));
1914         aWallpaper.SetStyle(WallpaperStyle::BottomRight);
1915         aWallpaper.SetColor(COL_RED);
1916 
1917         mpBox->SetBackground(aWallpaper);
1918         mpBox->Show();
1919 
1920         Help::EnableBalloonHelp();
1921         mpToolbox->SetHelpText("Help text");
1922         mpToolbox->InsertItem(ToolBoxItemId(0), "Toolbar item");
1923         mpToolbox->SetQuickHelpText(ToolBoxItemId(0), "This is a tooltip popup");
1924         mpToolbox->InsertSeparator();
1925         mpToolbox->Show();
1926 
1927         mpButton->SetText("Click me; go on");
1928         mpButton->Show();
1929 
1930         int i = 0;
1931         VclHBox* pCurrentCursorHBox = nullptr;
1932         constexpr int numButtonsPerRow = 9;
1933         for (auto & rData : gvPointerData)
1934         {
1935             if (i % numButtonsPerRow == 0)
1936             {
1937                 mvCursorBoxes.push_back(VclPtrInstance<VclHBox>(mpBox.get(), true, numButtonsPerRow));
1938                 pCurrentCursorHBox = mvCursorBoxes.back().get();
1939                 pCurrentCursorHBox->Show();
1940             }
1941             mvCursorButtons.emplace_back(VclPtrInstance<PushButton>(pCurrentCursorHBox));
1942             PushButton& rButton = *mvCursorButtons.back();
1943             rButton.SetText(OUString::createFromAscii(rData.name));
1944             rButton.SetClickHdl(LINK(this,DemoWidgets,CursorButtonClick));
1945             rButton.Show();
1946             ++i;
1947         }
1948 
1949         mpBar = VclPtr<MenuBar>::Create();
1950         mpBar->InsertItem(0,"File");
1951         VclPtrInstance<PopupMenu> pPopup;
1952         pPopup->InsertItem(0,"Item");
1953         mpBar->SetPopupMenu(0, pPopup);
1954         SetMenuBar(mpBar);
1955 
1956         Show();
1957     }
~DemoWidgets()1958     virtual ~DemoWidgets() override { disposeOnce(); }
dispose()1959     virtual void dispose() override
1960     {
1961         for (auto & p : mvCursorButtons)
1962             p.disposeAndClear();
1963         mvCursorButtons.clear();
1964         for (auto & p : mvCursorBoxes)
1965             p.disposeAndClear();
1966         mvCursorBoxes.clear();
1967         mpToolbox.disposeAndClear();
1968         mpButton.disposeAndClear();
1969         mpBox.disposeAndClear();
1970         mpBar.disposeAndClear();
1971         WorkWindow::dispose();
1972     }
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)1973     virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override
1974     {
1975         tools::Rectangle aWholeSize(Point(0, 0),GetOutputSizePixel());
1976         vcl::Region aClip(aWholeSize);
1977         tools::Rectangle aExclude(tools::Rectangle(Point(50,50),Size(100,100)));
1978         aClip.Exclude(aExclude);
1979 
1980         Wallpaper aWallpaper(COL_GREEN);
1981 
1982         rRenderContext.Push(PushFlags::CLIPREGION);
1983         rRenderContext.IntersectClipRegion(aClip);
1984         rRenderContext.DrawWallpaper(aWholeSize, aWallpaper);
1985         rRenderContext.Pop();
1986 
1987         ScopedVclPtrInstance< VirtualDevice > pDev(*GetOutDev());
1988         pDev->EnableRTL(IsRTLEnabled());
1989         pDev->SetOutputSizePixel(aExclude.GetSize());
1990 
1991         tools::Rectangle aSubRect(aWholeSize);
1992         aSubRect.Move(-aExclude.Left(), -aExclude.Top());
1993         pDev->DrawWallpaper(aSubRect, aWallpaper );
1994 
1995         rRenderContext.DrawOutDev(aExclude.TopLeft(), aExclude.GetSize(),
1996                    Point( 0, 0 ), aExclude.GetSize(), *pDev );
1997     }
1998 };
1999 
2000 }
2001 
IMPL_LINK(DemoWidgets,CursorButtonClick,Button *,pButton,void)2002 IMPL_LINK(DemoWidgets, CursorButtonClick, Button*, pButton, void)
2003 {
2004     for (size_t i=0; i<SAL_N_ELEMENTS(gvPointerData); ++i)
2005     {
2006         if (mvCursorButtons[i].get() == pButton)
2007         {
2008             mpBox->SetPointer( gvPointerData[i].eStyle );
2009             return;
2010         }
2011     }
2012     assert(false);
2013 }
2014 
2015 namespace {
2016 
2017 class DemoPopup : public FloatingWindow
2018 {
2019  public:
DemoPopup()2020     DemoPopup() : FloatingWindow( nullptr, WB_SYSTEMWINDOW|WB_TOOLTIPWIN)
2021     {
2022         SetType( WindowType::HELPTEXTWINDOW );
2023 
2024         SetOutputSizePixel( Size( 300, 30 ) );
2025         SetBackground(Wallpaper(COL_YELLOW));
2026 
2027         Show( true, ShowFlags::NoActivate );
2028         PaintImmediately();
2029     }
2030 
Paint(vcl::RenderContext & rRenderContext,const tools::Rectangle &)2031     virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override
2032     {
2033         Size aSize = GetOutputSizePixel();
2034         tools::Rectangle aTextRect(Point(6, 6), aSize);
2035 
2036         SetTextColor(COL_BLACK);
2037         SetTextAlign(ALIGN_TOP);
2038         rRenderContext.DrawText(aTextRect, "This is a standalone help text test",
2039                  DrawTextFlags::MultiLine|DrawTextFlags::WordBreak|
2040                  DrawTextFlags::Left|DrawTextFlags::Top);
2041 
2042         rRenderContext.SetLineColor(COL_BLACK);
2043         rRenderContext.SetFillColor();
2044         rRenderContext.DrawRect( tools::Rectangle( Point(), aSize ) );
2045         aSize.AdjustWidth( -2 );
2046         aSize.AdjustHeight( -2 );
2047         Color aColor( rRenderContext.GetLineColor() );
2048         rRenderContext.SetLineColor( COL_GRAY );
2049         rRenderContext.DrawRect( tools::Rectangle( Point( 1, 1 ), aSize ) );
2050         rRenderContext.SetLineColor( aColor );
2051     }
2052 
MouseButtonDown(const MouseEvent &)2053     virtual void MouseButtonDown( const MouseEvent & ) override
2054     {
2055         Application::Quit();
2056     }
2057 };
2058 
2059 }
2060 
2061 namespace {
renderFonts()2062     void renderFonts()
2063     {
2064         ScopedVclPtrInstance<VirtualDevice> xDevice;
2065         Size aSize(1024, 1024);
2066         xDevice->SetOutputSizePixel(aSize);
2067 
2068 #if 0
2069         for (auto & aFontName : aFontNames)
2070         {
2071             vcl::Font aFont(aFontName, Size(0,96));
2072 
2073             aFont.Set(COL_BLACK);
2074             xDevice->SetFont(aFont);
2075             xDevice->Erase();
2076 
2077             FontMetric aMetric = xDevice->GetFontMetric(aFont);
2078 
2079             FontCharMapRef xMap;
2080             if (xDevice->GetFontCharMap(xMap))
2081             {
2082                 ... iterate through glyphs ...
2083             }
2084 
2085 
2086     bool                        GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr, int nIndex,
2087                                                     int nLen, int nBase, MetricVector& rVector );
2088 
2089 include/vcl/outdev.hxx:typedef std::vector< Rectangle > MetricVector;
2090 include/vcl/outdev.hxx:                                          MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr );
2091 include/vcl/outdev.hxx:                                          MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr,
2092 include/vcl/outdev.hxx:                                              MetricVector* pVector, OUString* pDisplayText, vcl::ITextLayout& _rLayout );
2093 include/vcl/outdev.hxx:                                              DrawTextFlags nStyle = DrawTextFlags::Mnemonic, MetricVector* pVector = nullp
2094 
2095     bool                        GetTextBoundRect( Rectangle& rRect,
2096                                                   const OUString& rStr, sal_Int32 nBase = 0, sal_Int32 nIndex = 0, sal_Int32 nLen = -1,
2097                                                   sal_uLong nLayoutWidth = 0, const long* pDXArray = nullptr ) const;
2098 
2099 
2100     void                        DrawText( const Point& rStartPt, const OUString& rStr,
2101                                           sal_Int32 nIndex = 0, sal_Int32 nLen = -1,
2102                                           MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr );
2103 
2104     void                        DrawText( const Rectangle& rRect,
2105                                           const OUString& rStr, DrawTextFlags nStyle = DrawTextFlags::NONE,
2106                                           MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr,
2107                                           vcl::ITextLayout* _pTextLayout = nullptr );
2108 
2109     Rectangle                   GetTextRect( const Rectangle& rRect,
2110                                              const OUString& rStr, DrawTextFlags nStyle = DrawTextFlags::WordBreak,
2111                                              TextRectInfo* pInfo = nullptr,
2112                                              const vcl::ITextLayout* _pTextLayout = nullptr ) const;
2113 
2114         }
2115 #endif
2116 
2117     }
2118 };
2119 
2120 namespace {
2121 
2122 class DemoApp : public Application
2123 {
showHelp(DemoRenderer & rRenderer)2124     static int showHelp(DemoRenderer &rRenderer)
2125     {
2126         fprintf(stderr,"vcldemo - a VCL test app\n");
2127         fprintf(stderr,"  --help             - print this text\n");
2128         fprintf(stderr,"  --show <renderer>  - start with a given renderer, options are:\n");
2129         OUString aRenderers(rRenderer.getRendererList());
2130         fprintf(stderr,"         %s\n",
2131                 OUStringToOString(aRenderers, RTL_TEXTENCODING_UTF8).getStr());
2132         fprintf(stderr,"  --test <iterCount> - create benchmark data\n");
2133         fprintf(stderr,"  --widgets          - launch the widget test.\n");
2134         fprintf(stderr,"  --popup            - launch the popup test.\n");
2135         fprintf(stderr,"  --threads          - render from multiple threads.\n");
2136         fprintf(stderr,"  --font <fontname>  - run the font render test.\n");
2137         fprintf(stderr, "\n");
2138         return 0;
2139     }
2140 
2141 public:
DemoApp()2142     DemoApp() {}
2143 
Main()2144     virtual int Main() override
2145     {
2146         try
2147         {
2148             bool bWidgets = false;
2149             bool bThreads = false;
2150             bool bPopup = false;
2151             DemoRenderer aRenderer;
2152             std::vector<OUString> aFontNames;
2153 
2154             for (sal_uInt16 i = 0; i < GetCommandLineParamCount(); ++i)
2155             {
2156                 bool bLast = i == GetCommandLineParamCount() - 1;
2157                 OUString aArg = GetCommandLineParam(i);
2158                 if (aArg == "--help" || aArg == "-h")
2159                     return showHelp(aRenderer);
2160                 if (aArg == "--show")
2161                 {
2162                     if (bLast)
2163                         return showHelp(aRenderer);
2164                     else
2165                         aRenderer.selectRenderer(GetCommandLineParam(++i));
2166                 }
2167                 else if (aArg == "--test")
2168                 {
2169                     if (bLast)
2170                         return showHelp(aRenderer);
2171                     else
2172                         aRenderer.setIterCount(GetCommandLineParam(++i).toInt32());
2173                 }
2174                 else if (aArg == "--widgets")
2175                     bWidgets = true;
2176                 else if (aArg == "--popup")
2177                     bPopup = true;
2178                 else if (aArg == "--threads")
2179                     bThreads = true;
2180                 else if (aArg == "--font" && !bLast)
2181                     aFontNames.push_back(GetCommandLineParam(++i));
2182                 else if (aArg.startsWith("--"))
2183                 {
2184                     fprintf(stderr,"Unknown argument '%s'\n",
2185                             OUStringToOString(aArg, RTL_TEXTENCODING_UTF8).getStr());
2186                     return showHelp(aRenderer);
2187                 }
2188             }
2189 
2190             ScopedVclPtrInstance<DemoWin> aMainWin(aRenderer, bThreads);
2191             VclPtr<DemoWidgets> xWidgets;
2192             VclPtr<DemoPopup> xPopup;
2193 
2194             aMainWin->SetText("Interactive VCL demo #1");
2195             if (bWidgets)
2196                 xWidgets = VclPtr< DemoWidgets >::Create ();
2197             else if (bPopup)
2198                 xPopup = VclPtrInstance< DemoPopup> ();
2199             else if (!aFontNames.empty())
2200                 renderFonts();
2201             else
2202                 aMainWin->Show();
2203 
2204             Application::Execute();
2205 
2206             xWidgets.disposeAndClear();
2207             xPopup.disposeAndClear();
2208         }
2209         catch (const css::uno::Exception&)
2210         {
2211             TOOLS_WARN_EXCEPTION("vcl.app", "Fatal");
2212             return 1;
2213         }
2214         catch (const std::exception& e)
2215         {
2216             SAL_WARN("vcl.app", "Fatal: " << e.what());
2217             return 1;
2218         }
2219         return 0;
2220     }
2221 
2222 protected:
Init()2223     void Init() override
2224     {
2225         try
2226         {
2227             uno::Reference<uno::XComponentContext> xComponentContext
2228                 = ::cppu::defaultBootstrap_InitialComponentContext();
2229             uno::Reference<lang::XMultiServiceFactory> xMSF;
2230             xMSF.set(xComponentContext->getServiceManager(), uno::UNO_QUERY);
2231             if(!xMSF.is())
2232                 Application::Abort("Bootstrap failure - no service manager");
2233 
2234             ::comphelper::setProcessServiceFactory(xMSF);
2235         }
2236         catch (const uno::Exception &e)
2237         {
2238             Application::Abort("Bootstrap exception " + e.Message);
2239         }
2240     }
DeInit()2241     void DeInit() override
2242     {
2243         uno::Reference< lang::XComponent >(
2244             comphelper::getProcessComponentContext(),
2245         uno::UNO_QUERY_THROW)-> dispose();
2246         ::comphelper::setProcessServiceFactory(nullptr);
2247     }
2248 };
2249 
2250 }
2251 
createApplication()2252 void vclmain::createApplication()
2253 {
2254 #ifdef _WIN32
2255     _putenv_s("LIBO_VCL_DEMO", "1");
2256 #else
2257     setenv("LIBO_VCL_DEMO", "1", 0);
2258 #endif
2259     static DemoApp aApp;
2260 }
2261 
2262 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2263