Avoid potential negative array index access to cached text.
[LibreOffice.git] / vcl / workben / vcldemo.cxx
blob6824b6039360169b5c55a456231bf676207bbc57
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <sal/config.h>
12 #include <memory>
13 #include <thread>
15 #include <config_features.h>
17 #include <math.h>
18 #include <rtl/math.hxx>
19 #include <sal/log.hxx>
21 #include <comphelper/processfactory.hxx>
22 #include <comphelper/random.hxx>
23 #include <cppuhelper/bootstrap.hxx>
24 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
25 #include <com/sun/star/lang/XInitialization.hpp>
26 #include <com/sun/star/registry/XSimpleRegistry.hpp>
27 #include <com/sun/star/ucb/UniversalContentBroker.hpp>
28 #include <com/sun/star/uno/Reference.hxx>
29 #include <com/sun/star/uno/Sequence.hxx>
30 #include <com/sun/star/container/XNameAccess.hpp>
31 #include <o3tl/safeint.hxx>
32 #include <osl/time.h>
33 #include <vcl/gradient.hxx>
34 #include <vcl/vclmain.hxx>
35 #include <vcl/layout.hxx>
36 #include <vcl/ptrstyle.hxx>
37 #include <salhelper/thread.hxx>
39 #include <comphelper/diagnose_ex.hxx>
40 #include <tools/urlobj.hxx>
41 #include <tools/stream.hxx>
42 #include <vcl/svapp.hxx>
43 #include <vcl/wrkwin.hxx>
44 #include <vcl/virdev.hxx>
45 #include <vcl/graphicfilter.hxx>
46 #include <vcl/toolkit/button.hxx>
47 #include <vcl/toolkit/combobox.hxx>
48 #include <vcl/toolbox.hxx>
49 #include <vcl/toolkit/floatwin.hxx>
50 #include <vcl/help.hxx>
51 #include <vcl/kernarray.hxx>
52 #include <vcl/menu.hxx>
53 #include <vcl/ImageTree.hxx>
54 #include <vcl/BitmapEmbossGreyFilter.hxx>
55 #include <vcl/BitmapWriteAccess.hxx>
57 #include <basegfx/numeric/ftools.hxx>
58 #include <basegfx/matrix/b2dhommatrix.hxx>
60 #include <framework/desktop.hxx>
61 #include <i18nlangtag/languagetag.hxx>
62 #include <i18nlangtag/mslangid.hxx>
64 #define FIXME_SELF_INTERSECTING_WORKING 0
65 #define FIXME_BOUNCE_BUTTON 0
66 #define THUMB_REPEAT_FACTOR 10
68 using namespace com::sun::star;
70 namespace {
71 double getTimeNow()
73 TimeValue aValue;
74 osl_getSystemTime(&aValue);
75 return static_cast<double>(aValue.Seconds) * 1000 +
76 static_cast<double>(aValue.Nanosec) / (1000*1000);
81 namespace {
83 enum RenderStyle {
84 RENDER_THUMB, // small view <n> to a page
85 RENDER_EXPANDED, // expanded view of this renderer
88 class DemoRenderer
90 Bitmap maIntroBW;
91 BitmapEx maIntro;
93 int mnSegmentsX;
94 int mnSegmentsY;
96 struct RenderContext {
97 RenderStyle meStyle;
98 bool mbVDev;
99 DemoRenderer *mpDemoRenderer;
100 Size maSize;
102 struct RegionRenderer {
103 public:
104 RegionRenderer() :
105 sumTime(0),
106 countTime(0)
108 virtual ~RegionRenderer() {}
109 virtual OUString getName() = 0;
110 virtual sal_uInt16 getAccelerator() = 0;
111 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
112 const RenderContext &rCtx) = 0;
113 // repeating count for profiling (to exceed the poor time resolution on Windows)
114 virtual sal_uInt16 getTestRepeatCount() = 0;
115 #define RENDER_DETAILS(name,key,repeat) \
116 virtual OUString getName() override \
117 { return SAL_STRINGIFY(name); } \
118 virtual sal_uInt16 getAccelerator() override \
119 { return key; } \
120 virtual sal_uInt16 getTestRepeatCount() override \
121 { return repeat; }
123 double sumTime;
124 int countTime;
127 std::vector< RegionRenderer * > maRenderers;
128 sal_Int32 mnSelectedRenderer;
129 sal_Int32 iterCount;
131 void InitRenderers();
133 public:
134 DemoRenderer() : mnSegmentsX(0)
135 , mnSegmentsY(0)
136 , mnSelectedRenderer(-1)
137 , iterCount(0)
138 #if FIXME_BOUNCE_BUTTON
139 , mpButton(NULL)
140 , mpButtonWin(NULL)
141 , mnBounceX(1)
142 , mnBounceY(1)
143 #endif
145 if (!Application::LoadBrandBitmap(u"intro", maIntro))
146 Application::Abort("Failed to load intro image");
148 maIntroBW = maIntro.GetBitmap();
150 BitmapEx aTmpBmpEx(maIntroBW);
151 BitmapFilter::Filter(aTmpBmpEx, BitmapEmbossGreyFilter(0_deg100, 0_deg100));
152 maIntroBW = aTmpBmpEx.GetBitmap();
154 InitRenderers();
155 mnSegmentsY = rtl::math::round(std::sqrt(maRenderers.size()), 0,
156 rtl_math_RoundingMode_Down);
157 mnSegmentsX = (maRenderers.size() + mnSegmentsY - 1)/mnSegmentsY;
160 OUString getRendererList();
161 double getAndResetBenchmark(RenderStyle style);
162 void selectRenderer(std::u16string_view rName);
163 int selectNextRenderer();
164 void setIterCount(sal_Int32 iterCount);
165 sal_Int32 getIterCount() const;
166 void addTime(int i, double t);
168 Size maSize;
169 void SetSizePixel(const Size &rSize) { maSize = rSize; }
170 const Size& GetSizePixel() const { return maSize; }
173 // more of a 'Window' concept - push upwards ?
174 #if FIXME_BOUNCE_BUTTON
175 // Bouncing windows on click ...
176 PushButton *mpButton;
177 FloatingWindow *mpButtonWin;
178 AutoTimer maBounce;
179 int mnBounceX, mnBounceY;
180 DECL_LINK(BounceTimerCb, Timer*, void);
181 #endif
183 bool MouseButtonDown(const MouseEvent& rMEvt);
184 void KeyInput(const KeyEvent& rKEvt);
186 static std::vector<tools::Rectangle> partition(const tools::Rectangle &rRect, int nX, int nY)
188 std::vector<tools::Rectangle> aRegions = partition(rRect.GetSize(), nX, nY);
189 for (auto & region : aRegions)
190 region.Move(rRect.Left(), rRect.Top());
192 return aRegions;
195 static std::vector<tools::Rectangle> partition(const RenderContext &rCtx, int nX, int nY)
197 return partition(rCtx.maSize, nX, nY);
200 static std::vector<tools::Rectangle> partition(Size aSize, int nX, int nY)
202 tools::Rectangle r;
203 std::vector<tools::Rectangle> aRegions;
205 // Make small cleared area for these guys
206 tools::Long nBorderSize = std::min(aSize.Height() / 32, aSize.Width() / 32);
207 tools::Long nBoxWidth = (aSize.Width() - nBorderSize*(nX+1)) / nX;
208 tools::Long nBoxHeight = (aSize.Height() - nBorderSize*(nY+1)) / nY;
209 for (int y = 0; y < nY; y++)
211 for (int x = 0; x < nX; x++)
213 r.SetPos(Point(nBorderSize + (nBorderSize + nBoxWidth) * x,
214 nBorderSize + (nBorderSize + nBoxHeight) * y));
215 r.SetSize(Size(nBoxWidth, nBoxHeight));
216 aRegions.push_back(r);
220 return aRegions;
223 static void clearRects(OutputDevice &rDev, std::vector<tools::Rectangle> &rRects)
225 for (size_t i = 0; i < rRects.size(); i++)
227 // knock up a nice little border
228 rDev.SetLineColor(COL_GRAY);
229 rDev.SetFillColor(COL_LIGHTGRAY);
230 if (i % 2)
232 int nBorderSize = rRects[i].GetWidth() / 5;
233 rDev.DrawRect(rRects[i], nBorderSize, nBorderSize);
235 else
236 rDev.DrawRect(rRects[i]);
240 static void drawBackground(OutputDevice &rDev, const tools::Rectangle& r)
242 rDev.Erase();
243 Gradient aGradient;
244 aGradient.SetStartColor(COL_BLUE);
245 aGradient.SetEndColor(COL_GREEN);
246 aGradient.SetStyle(css::awt::GradientStyle_LINEAR);
247 rDev.DrawGradient(r, aGradient);
250 struct DrawLines : public RegionRenderer
252 RENDER_DETAILS(lines,KEY_L,100)
253 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
254 const RenderContext &rCtx) override
256 if (rCtx.meStyle == RENDER_EXPANDED)
258 AntialiasingFlags nOldAA = rDev.GetAntialiasing();
259 rDev.SetAntialiasing(AntialiasingFlags::Enable);
261 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx, 4, 4));
262 DemoRenderer::clearRects(rDev, aRegions);
264 #if 0 // FIXME: get this through to the backend ...
265 double nTransparency[] = {
266 1.0, 1.0, 1.0, 1.0,
267 0.8, 0.8, 0.8, 0.8,
268 0.5, 0.5, 0.5, 0.5,
269 0.1, 0.1, 0.1, 0.1
271 #endif
272 drawing::LineCap const eLineCaps[] = {
273 drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT,
274 drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT,
275 drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT,
276 drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT
278 basegfx::B2DLineJoin const eJoins[] = {
279 basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round,
280 basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round,
281 basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round,
282 basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round
284 double const aLineWidths[] = {
285 10.0, 15.0, 20.0, 10.0,
286 10.0, 15.0, 20.0, 10.0,
287 10.0, 15.0, 20.0, 10.0,
288 0.1, 1.0, 10.0, 50.0
290 for (size_t i = 0; i < aRegions.size(); i++)
292 // Half of them not-anti-aliased ..
293 if (i >= aRegions.size()/2)
294 rDev.SetAntialiasing(nOldAA);
296 static const struct {
297 double nX, nY;
298 } aPoints[] = {
299 { 0.2, 0.2 }, { 0.8, 0.3 }, { 0.7, 0.8 }
301 rDev.SetLineColor(COL_BLACK);
302 basegfx::B2DPolygon aPoly;
303 tools::Rectangle aSub(aRegions[i]);
304 for (const auto& rPoint : aPoints)
306 aPoly.append(basegfx::B2DPoint(aSub.Left() + aSub.GetWidth() * rPoint.nX,
307 aSub.Top() + aSub.GetHeight() * rPoint.nY));
309 rDev.DrawPolyLine(aPoly, aLineWidths[i], eJoins[i], eLineCaps[i]);
312 else
314 rDev.SetFillColor(COL_LIGHTRED);
315 rDev.SetLineColor(COL_BLACK);
316 rDev.DrawRect(r);
318 for(tools::Long i=0; i<r.GetHeight(); i+=15)
319 rDev.DrawLine(Point(r.Left(), r.Top()+i), Point(r.Right(), r.Bottom()-i));
320 for(tools::Long i=0; i<r.GetWidth(); i+=15)
321 rDev.DrawLine(Point(r.Left()+i, r.Bottom()), Point(r.Right()-i, r.Top()));
323 // Should draw a white-line across the middle
324 Color aLastPixel(COL_WHITE);
325 Point aCenter((r.Left() + r.Right())/2 - 4,
326 (r.Top() + r.Bottom())/2 - 4);
327 for(int i=0; i<8; i++)
329 rDev.DrawPixel(aCenter, aLastPixel);
330 aLastPixel = rDev.GetPixel(aCenter);
331 aCenter.Move(1,1);
337 struct DrawText : public RegionRenderer
339 RENDER_DETAILS(text,KEY_T,1)
341 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
342 const RenderContext &rCtx) override
344 if (rCtx.meStyle == RENDER_EXPANDED)
346 std::vector<tools::Rectangle> aToplevelRegions(
347 DemoRenderer::partition(rCtx, 1, 3));
348 std::vector<tools::Rectangle> aSubRegions(
349 DemoRenderer::partition(aToplevelRegions[0], 4, 2));
350 tools::Rectangle aBottom(aToplevelRegions[1].TopLeft(),
351 aToplevelRegions[2].BottomRight());
352 DemoRenderer::clearRects(rDev,aSubRegions);
353 static struct {
354 bool mbClip;
355 bool mbArabicText;
356 bool mbRotate;
357 } const aRenderData[] = {
358 { false, false, false },
359 { false, true, false },
360 { false, true, true },
361 { false, false, true },
362 { true, false, true },
363 { true, true, true },
364 { true, true, false },
365 { true, false, false },
368 size_t i = 0;
369 for (int y = 0; y < 2; y++)
371 for (int x = 0; x < 4; x++)
373 assert(i < std::size(aRenderData));
374 drawText(rDev, aSubRegions[i], aRenderData[i].mbClip,
375 aRenderData[i].mbArabicText, aRenderData[i].mbRotate);
376 i++;
380 drawComplex(rDev, aBottom);
382 else
384 drawText(rDev, r, false, false, false);
388 static void drawText (OutputDevice &rDev, tools::Rectangle r, bool bClip, bool bArabicText, bool bRotate)
390 rDev.SetClipRegion( vcl::Region(r) );
392 const unsigned char pTextUTF8[] = {
393 0xd9, 0x88, 0xd8, 0xa7, 0xd8, 0xad, 0xd9, 0x90,
394 0xd8, 0xaf, 0xd9, 0x92, 0x20, 0xd8, 0xa5, 0xd8,
395 0xab, 0xd9, 0x8d, 0xd9, 0x86, 0xd9, 0x8a, 0xd9,
396 0x86, 0x20, 0xd8, 0xab, 0xd9, 0x84, 0xd8, 0xa7,
397 0xd8, 0xab, 0xd8, 0xa9, 0xd9, 0x8c, 0x00
399 OUString aArabicText( reinterpret_cast<char const *>(pTextUTF8),
400 SAL_N_ELEMENTS( pTextUTF8 ) - 1,
401 RTL_TEXTENCODING_UTF8 );
403 OUString aText;
405 // To have more text displayed one after the other (overlapping, and in different colours), then
406 // change this value
407 const int nPrintNumCopies=1;
409 if (bArabicText)
410 aText = aArabicText;
411 else
412 aText = "Click any rect to zoom!!!!";
414 std::vector<OUString> aFontNames;
416 static Color const nCols[] = {
417 COL_BLACK, COL_BLUE, COL_GREEN, COL_CYAN, COL_RED, COL_MAGENTA,
418 COL_BROWN, COL_GRAY, COL_LIGHTGRAY, COL_LIGHTBLUE, COL_LIGHTGREEN,
419 COL_LIGHTCYAN, COL_LIGHTRED, COL_LIGHTMAGENTA, COL_YELLOW, COL_WHITE
422 // a few fonts to start with
423 const char *pNames[] = {
424 "Times", "Liberation Sans", "Arial", "Linux Biolinum G", "Linux Libertine Display G"
427 for (size_t i = 0; i < SAL_N_ELEMENTS(pNames); i++)
428 aFontNames.push_back(OUString::createFromAscii(pNames[i]));
430 if (bClip && !bRotate)
432 // only show the first quarter of the text
433 tools::Rectangle aRect( r.TopLeft(), Size( r.GetWidth()/2, r.GetHeight()/2 ) );
434 rDev.SetClipRegion( vcl::Region( aRect ) );
437 for (int i = 1; i < nPrintNumCopies+1; i++)
439 int nFontHeight=0, nFontIndex=0, nFontColorIndex=0;
441 if (nPrintNumCopies == 1)
443 float const nFontMagnitude = 0.25f;
444 // random font size to avoid buffering
445 nFontHeight = 1 + nFontMagnitude * (0.9 + comphelper::rng::uniform_real_distribution(0.0, std::nextafter(0.1, DBL_MAX))) * (r.Bottom() - r.Top());
446 nFontIndex=0;
447 nFontColorIndex=0;
449 else
451 // random font size to avoid buffering
452 nFontHeight = 1 + i * (0.9 + comphelper::rng::uniform_real_distribution(0.0, std::nextafter(0.1, DBL_MAX))) * (r.Top() - r.Bottom()) / nPrintNumCopies;
453 nFontIndex = (i % aFontNames.size());
454 nFontColorIndex=(i % aFontNames.size());
457 rDev.SetTextColor(nCols[nFontColorIndex]);
458 vcl::Font aFont( aFontNames[nFontIndex], Size(0, nFontHeight ));
460 if (bRotate)
462 tools::Rectangle aFontRect = r;
464 int nHeight = r.GetHeight();
466 // move the text to the bottom of the bounding rect before rotating
467 aFontRect.AdjustTop(nHeight/2 );
468 aFontRect.AdjustBottom(nHeight );
470 aFont.SetOrientation(450_deg10); // 45 degrees
472 rDev.SetFont(aFont);
473 rDev.DrawText(aFontRect, aText);
475 if (bClip)
477 tools::Rectangle aClipRect( Point( r.Left(), r.Top() + ( r.GetHeight()/2 ) ) , Size( r.GetWidth()/2, r.GetHeight()/2 ) );
478 rDev.SetClipRegion( vcl::Region( aClipRect ) );
480 else
481 rDev.SetClipRegion( vcl::Region(r) );
483 else
485 rDev.SetFont(aFont);
486 rDev.DrawText(r, aText);
490 rDev.SetClipRegion();
493 static void drawComplex (OutputDevice &rDev, tools::Rectangle r)
495 const unsigned char pInvalid[] = { 0xfe, 0x1f, 0 };
496 const unsigned char pDiacritic1[] = { 0x61, 0xcc, 0x8a, 0xcc, 0x8c, 0 };
497 const unsigned char pDiacritic2[] = { 0x61, 0xcc, 0x88, 0xcc, 0x86, 0 };
498 const unsigned char pDiacritic3[] = { 0x61, 0xcc, 0x8b, 0xcc, 0x87, 0 };
499 const unsigned char pJustification[] = {
500 0x64, 0x20, 0xc3, 0xa1, 0xc3, 0xa9, 0x77, 0xc4, 0x8d,
501 0xc5, 0xa1, 0xc3, 0xbd, 0xc5, 0x99, 0x20, 0xc4, 0x9b, 0
503 const unsigned char pEmojis[] = {
504 0xf0, 0x9f, 0x8d, 0x80, 0xf0, 0x9f, 0x91, 0x98,
505 0xf0, 0x9f, 0x92, 0x8a, 0xf0, 0x9f, 0x92, 0x99,
506 0xf0, 0x9f, 0x92, 0xa4, 0xf0, 0x9f, 0x94, 0x90, 0
508 const unsigned char pThreeBowlG[] = {
509 0xe2, 0x9a, 0x82, 0xe2, 0x99, 0xa8, 0xc4, 0x9e, 0
511 const unsigned char pWavesAndDomino[] = {
512 0xe2, 0x99, 0x92, 0xf0, 0x9f, 0x81, 0xa0,
513 0xf0, 0x9f, 0x82, 0x93, 0
515 const unsigned char pSpadesAndBits[] = {
516 0xf0, 0x9f, 0x82, 0xa1, 0xc2, 0xa2, 0xc2, 0xa2, 0
519 static struct {
520 const char *mpFont;
521 const char *mpString;
522 } const aRuns[] = {
523 #define SET(font,string) { font, reinterpret_cast<const char *>(string) }
524 {"sans", "a"}, // logical font - no 'sans' font.
525 {"opensymbol", "#$%"}, // font fallback - $ is missing.
526 SET("sans", pInvalid), // unicode invalid character
527 // tdf#96266 - stacking diacritics
528 SET("carlito", pDiacritic1),
529 SET("carlito", pDiacritic2),
530 SET("carlito", pDiacritic3),
531 SET("liberation sans", pDiacritic1),
532 SET("liberation sans", pDiacritic2),
533 SET("liberation sans", pDiacritic3),
534 SET("liberation sans", pDiacritic3),
536 // tdf#95222 - justification issue
537 // - FIXME: replicate justification
538 SET("gentium basic", pJustification),
540 // tdf#97319 - Unicode beyond BMP; SMP & Plane 2
541 SET("symbola", pEmojis),
542 SET("symbola", pThreeBowlG),
543 SET("symbola", pWavesAndDomino),
544 SET("symbola", pSpadesAndBits),
547 // Nice clean white background
548 rDev.DrawWallpaper(r, Wallpaper(COL_WHITE));
549 rDev.SetClipRegion(vcl::Region(r));
551 Point aPos(r.Left(), r.Top()+20);
553 tools::Long nMaxTextHeight = 0;
554 for (size_t i = 0; i < std::size(aRuns); ++i)
556 // Legend
557 vcl::Font aIndexFont("sans", Size(0,20));
558 aIndexFont.SetColor( COL_BLACK);
559 tools::Rectangle aTextRect;
560 rDev.SetFont(aIndexFont);
561 OUString aText = OUString::number(i) + ".";
562 rDev.DrawText(aPos, aText);
563 if (rDev.GetTextBoundRect(aTextRect, aText))
564 aPos.Move(aTextRect.GetWidth() + 8, 0);
566 // Text
567 FontWeight aWeights[] = { WEIGHT_NORMAL,
568 WEIGHT_BOLD,
569 WEIGHT_NORMAL };
570 FontItalic const aItalics[] = { ITALIC_NONE,
571 ITALIC_NONE,
572 ITALIC_NORMAL };
573 vcl::Font aFont(OUString::createFromAscii(
574 aRuns[i].mpFont),
575 Size(0,42));
576 aFont.SetColor(COL_BLACK);
577 for (size_t j = 0; j < std::size(aWeights); ++j)
579 aFont.SetItalic(aItalics[j]);
580 aFont.SetWeight(aWeights[j]);
581 rDev.SetFont(aFont);
583 OUString aString(aRuns[i].mpString,
584 strlen(aRuns[i].mpString),
585 RTL_TEXTENCODING_UTF8);
586 tools::Long nNewX = drawStringBox(rDev, aPos, aString,
587 nMaxTextHeight);
589 aPos.setX( nNewX );
591 if (aPos.X() >= r.Right())
593 aPos = Point(r.Left(), aPos.Y() + nMaxTextHeight + 15);
594 nMaxTextHeight = 0;
595 if(j>0)
596 j--; // re-render the last point.
598 if (aPos.Y() > r.Bottom())
599 break;
601 if (aPos.Y() > r.Bottom())
602 break;
605 rDev.SetClipRegion();
607 // render text, bbox, DX arrays etc.
608 static tools::Long drawStringBox(OutputDevice &rDev, Point aPos,
609 const OUString &aText,
610 tools::Long &nMaxTextHeight)
612 rDev.Push();
614 tools::Rectangle aTextRect;
616 rDev.DrawText(aPos,aText);
618 if (rDev.GetTextBoundRect(aTextRect, aText))
620 aTextRect.Move(aPos.X(), aPos.Y());
621 rDev.SetFillColor();
622 rDev.SetLineColor(COL_BLACK);
623 rDev.DrawRect(aTextRect);
624 if (aTextRect.GetHeight() > nMaxTextHeight)
625 nMaxTextHeight = aTextRect.GetHeight();
626 // This should intersect with the text
627 tools::Rectangle aInnerRect(
628 aTextRect.Left()+1, aTextRect.Top()+1,
629 aTextRect.Right()-1, aTextRect.Bottom()-1);
630 rDev.SetLineColor(COL_WHITE);
631 rDev.SetRasterOp(RasterOp::Xor);
632 rDev.DrawRect(aInnerRect);
633 rDev.SetRasterOp(RasterOp::OverPaint);
636 // DX array rendering
637 KernArray aItems;
638 rDev.GetTextArray(aText, &aItems);
639 for (tools::Long j = 0; j < aText.getLength(); ++j)
641 Point aTop = aTextRect.TopLeft();
642 Point aBottom = aTop;
643 aTop.Move(aItems[j], 0);
644 aBottom.Move(aItems[j], aTextRect.GetHeight());
645 rDev.SetLineColor(COL_RED);
646 rDev.SetRasterOp(RasterOp::Xor);
647 rDev.DrawLine(aTop,aBottom);
648 rDev.SetRasterOp(RasterOp::OverPaint);
651 aPos.Move(aTextRect.GetWidth() + 16, 0);
653 rDev.Pop();
654 return aPos.X();
658 struct DrawCheckered : public RegionRenderer
660 RENDER_DETAILS(checks,KEY_C,20)
661 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
662 const RenderContext &rCtx) override
664 if (rCtx.meStyle == RENDER_EXPANDED)
666 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx, 2, 2));
667 for (size_t i = 0; i < aRegions.size(); i++)
669 vcl::Region aRegion;
670 tools::Rectangle aSub(aRegions[i]);
671 tools::Rectangle aSmaller(aSub);
672 aSmaller.Move(10,10);
673 aSmaller.setWidth(aSmaller.getOpenWidth()-20);
674 aSmaller.setHeight(aSmaller.getOpenHeight()-24);
675 switch (i) {
676 case 0:
677 aRegion = vcl::Region(aSub);
678 break;
679 case 1:
680 aRegion = vcl::Region(aSmaller);
681 aRegion.XOr(aSub);
682 break;
683 case 2:
685 tools::Polygon aPoly(aSub);
686 aPoly.Rotate(aSub.Center(), 450_deg10);
687 aPoly.Clip(aSmaller);
688 aRegion = vcl::Region(aPoly);
689 break;
691 case 3:
693 tools::PolyPolygon aPolyPoly;
694 sal_Int32 nTW = aSub.GetWidth()/6;
695 sal_Int32 nTH = aSub.GetHeight()/6;
696 tools::Rectangle aTiny(Point(4, 4), Size(nTW*2, nTH*2));
697 aPolyPoly.Insert( tools::Polygon(aTiny));
698 aTiny.Move(nTW*3, nTH*3);
699 aPolyPoly.Insert( tools::Polygon(aTiny));
700 aTiny.Move(nTW, nTH);
701 aPolyPoly.Insert( tools::Polygon(aTiny));
703 aRegion = vcl::Region(aPolyPoly);
704 break;
706 } // switch
707 rDev.SetClipRegion(aRegion);
708 rDev.DrawCheckered(aSub.TopLeft(), aSub.GetSize());
709 rDev.SetClipRegion();
712 else
714 rDev.DrawCheckered(r.TopLeft(), r.GetSize());
719 struct DrawPoly : public RegionRenderer
721 RENDER_DETAILS(poly,KEY_P,20)
722 DrawCheckered maCheckered;
723 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
724 const RenderContext &rCtx) override
726 maCheckered.RenderRegion(rDev, r, rCtx);
728 tools::Long nDx = r.GetWidth()/20;
729 tools::Long nDy = r.GetHeight()/20;
730 tools::Rectangle aShrunk(r);
731 aShrunk.Move(nDx, nDy);
732 aShrunk.SetSize(Size(r.GetWidth()-nDx*2,
733 r.GetHeight()-nDy*2));
734 tools::Polygon aPoly(aShrunk);
735 tools::PolyPolygon aPPoly(aPoly);
736 rDev.SetLineColor(COL_RED);
737 rDev.SetFillColor(COL_RED);
738 // This hits the optional 'drawPolyPolygon' code-path
739 rDev.DrawTransparent(aPPoly, 64);
743 struct DrawEllipse : public RegionRenderer
745 RENDER_DETAILS(ellipse,KEY_E,500)
746 static void doInvert(OutputDevice &rDev, const tools::Rectangle &r,
747 InvertFlags nFlags)
749 rDev.Invert(r, nFlags);
750 if (r.GetWidth() > 10 && r.GetHeight() > 10)
752 tools::Rectangle aSmall(r.Center()-Point(4,4), Size(8,8));
753 rDev.Invert(aSmall,nFlags);
756 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
757 const RenderContext &rCtx) override
759 rDev.SetLineColor(COL_RED);
760 rDev.SetFillColor(COL_GREEN);
761 rDev.DrawEllipse(r);
763 if (rCtx.meStyle == RENDER_EXPANDED)
765 auto aRegions = partition(rCtx, 2, 2);
766 doInvert(rDev, aRegions[0], InvertFlags::NONE);
767 rDev.DrawText(aRegions[0], "InvertFlags::NONE");
768 doInvert(rDev, aRegions[1], InvertFlags::N50);
769 rDev.DrawText(aRegions[1], "InvertFlags::N50");
770 doInvert(rDev, aRegions[3], InvertFlags::TrackFrame);
771 rDev.DrawText(aRegions[3], "InvertFlags::TrackFrame");
776 struct DrawGradient : public RegionRenderer
778 RENDER_DETAILS(gradient,KEY_G,50)
779 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
780 const RenderContext &rCtx) override
782 if (rCtx.meStyle == RENDER_EXPANDED)
784 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx,5, 4));
785 static Color const nStartCols[] = {
786 COL_RED, COL_RED, COL_RED, COL_GREEN, COL_GREEN,
787 COL_BLUE, COL_BLUE, COL_BLUE, COL_CYAN, COL_CYAN,
788 COL_BLACK, COL_LIGHTGRAY, COL_WHITE, COL_BLUE, COL_CYAN,
789 COL_WHITE, COL_WHITE, COL_WHITE, COL_BLACK, COL_BLACK
791 static Color const nEndCols[] = {
792 COL_WHITE, COL_WHITE, COL_WHITE, COL_BLACK, COL_BLACK,
793 COL_RED, COL_RED, COL_RED, COL_GREEN, COL_GREEN,
794 COL_GRAY, COL_GRAY, COL_LIGHTGRAY, COL_LIGHTBLUE, COL_LIGHTCYAN,
795 COL_BLUE, COL_BLUE, COL_BLUE, COL_CYAN, COL_CYAN
797 css::awt::GradientStyle eStyles[] = {
798 css::awt::GradientStyle_LINEAR, css::awt::GradientStyle_AXIAL, css::awt::GradientStyle_RADIAL, css::awt::GradientStyle_ELLIPTICAL, css::awt::GradientStyle_SQUARE,
799 css::awt::GradientStyle_RECT, css::awt::GradientStyle::GradientStyle_MAKE_FIXED_SIZE, css::awt::GradientStyle_LINEAR, css::awt::GradientStyle_RADIAL, css::awt::GradientStyle_LINEAR,
800 css::awt::GradientStyle_LINEAR, css::awt::GradientStyle_AXIAL, css::awt::GradientStyle_RADIAL, css::awt::GradientStyle_ELLIPTICAL, css::awt::GradientStyle_SQUARE,
801 css::awt::GradientStyle_RECT, css::awt::GradientStyle::GradientStyle_MAKE_FIXED_SIZE, css::awt::GradientStyle_LINEAR, css::awt::GradientStyle_RADIAL, css::awt::GradientStyle_LINEAR
803 sal_uInt16 nAngles[] = {
804 0, 0, 0, 0, 0,
805 15, 30, 45, 60, 75,
806 90, 120, 135, 160, 180,
807 0, 0, 0, 0, 0
809 sal_uInt16 nBorders[] = {
810 0, 0, 0, 0, 0,
811 1, 10, 100, 10, 1,
812 0, 0, 0, 0, 0,
813 1, 10, 20, 10, 1,
814 0, 0, 0, 0, 0
816 DemoRenderer::clearRects(rDev, aRegions);
817 assert(aRegions.size() <= SAL_N_ELEMENTS(nStartCols));
818 assert(aRegions.size() <= SAL_N_ELEMENTS(nEndCols));
819 assert(aRegions.size() <= SAL_N_ELEMENTS(eStyles));
820 assert(aRegions.size() <= SAL_N_ELEMENTS(nAngles));
821 assert(aRegions.size() <= SAL_N_ELEMENTS(nBorders));
822 for (size_t i = 0; i < aRegions.size(); i++)
824 tools::Rectangle aSub = aRegions[i];
825 Gradient aGradient;
826 aGradient.SetStartColor(nStartCols[i]);
827 aGradient.SetEndColor(nEndCols[i]);
828 aGradient.SetStyle(eStyles[i]);
829 aGradient.SetAngle(Degree10(nAngles[i]));
830 aGradient.SetBorder(nBorders[i]);
831 rDev.DrawGradient(aSub, aGradient);
834 else
836 Gradient aGradient;
837 aGradient.SetStartColor(COL_YELLOW);
838 aGradient.SetEndColor(COL_RED);
839 aGradient.SetStyle(css::awt::GradientStyle_RECT);
840 aGradient.SetBorder(r.GetSize().Width()/20);
841 rDev.DrawGradient(r, aGradient);
846 struct DrawBitmap : public RegionRenderer
848 RENDER_DETAILS(bitmap,KEY_B,10)
850 // Simulate Page Borders rendering - which ultimately should
851 // be done with a shader / gradient
852 static void SimulateBorderStretch(OutputDevice &rDev, const tools::Rectangle& r)
854 BitmapEx aPageShadowMask("sw/res/page-shadow-mask.png");
856 BitmapEx aRight(aPageShadowMask);
857 sal_Int32 nSlice = (aPageShadowMask.GetSizePixel().Width() - 3) / 4;
858 // a width x 1 slice
859 aRight.Crop(tools::Rectangle(Point((nSlice * 3) + 3, (nSlice * 2) + 1),
860 Size(nSlice, 1)));
861 AlphaMask aAlphaMask(aRight.GetBitmap());
862 Bitmap aBlockColor(aAlphaMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
863 aBlockColor.Erase(COL_RED);
864 BitmapEx aShadowStretch(aBlockColor, aAlphaMask);
866 Point aRenderPt(r.TopLeft());
868 tools::Long aSizes[] = { 200, 100, 200, 100, 50, 5, 2 };
870 // and yes - we really do this in the page border rendering code ...
871 for (const auto& rSize : aSizes)
873 aShadowStretch.Scale(Size(aShadowStretch.GetSizePixel().Width(), rSize),
874 BmpScaleFlag::Fast);
876 rDev.DrawBitmapEx(aRenderPt, aShadowStretch);
877 aRenderPt.Move(aShadowStretch.GetSizePixel().Width() + 4, 0);
880 AlphaMask aWholeMask(aPageShadowMask.GetBitmap());
881 aBlockColor = Bitmap(aPageShadowMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
882 aBlockColor.Erase(COL_GREEN);
883 BitmapEx aWhole(aBlockColor, aWholeMask);
885 aRenderPt = r.Center();
886 aRenderPt.Move(nSlice+1, 0);
888 // An offset background for alpha rendering
889 rDev.SetFillColor(COL_BLUE);
890 tools::Rectangle aSurround(r.Center(), aPageShadowMask.GetSizePixel());
891 rDev.DrawRect(aSurround);
892 rDev.DrawBitmapEx(aRenderPt, aWhole);
895 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
896 const RenderContext &rCtx) override
898 Bitmap aBitmap(rCtx.mpDemoRenderer->maIntroBW);
899 aBitmap.Scale(r.GetSize(), BmpScaleFlag::BestQuality);
900 rDev.DrawBitmap(r.TopLeft(), aBitmap);
902 SimulateBorderStretch(rDev, r);
906 struct DrawBitmapEx : public RegionRenderer
908 RENDER_DETAILS(bitmapex,KEY_X,2)
909 DrawCheckered maCheckered;
910 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
911 const RenderContext &rCtx) override
913 maCheckered.RenderRegion(rDev, r, rCtx);
915 BitmapEx aBitmap(rCtx.mpDemoRenderer->maIntro);
916 aBitmap.Scale(r.GetSize(), BmpScaleFlag::BestQuality);
917 AlphaMask aSemiTransp(aBitmap.GetSizePixel());
918 aSemiTransp.Erase(64);
919 rDev.DrawBitmapEx(r.TopLeft(), BitmapEx(aBitmap.GetBitmap(),
920 aSemiTransp));
924 struct DrawPolyPolygons : public RegionRenderer
926 RENDER_DETAILS(polypoly,KEY_N,100)
927 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
928 const RenderContext &) override
930 static struct {
931 double nX, nY;
932 } const aPoints[] = { { 0.1, 0.1 }, { 0.9, 0.9 },
933 #if FIXME_SELF_INTERSECTING_WORKING
934 { 0.9, 0.1 }, { 0.1, 0.9 },
935 { 0.1, 0.1 }
936 #else
937 { 0.1, 0.9 }, { 0.5, 0.5 },
938 { 0.9, 0.1 }, { 0.1, 0.1 }
939 #endif
942 tools::PolyPolygon aPolyPoly;
943 // Render 4x polygons & aggregate into another PolyPolygon
944 for (int x = 0; x < 2; x++)
946 for (int y = 0; y < 2; y++)
948 tools::Rectangle aSubRect(r);
949 aSubRect.Move(x * r.GetWidth()/3, y * r.GetHeight()/3);
950 aSubRect.SetSize(Size(r.GetWidth()/2, r.GetHeight()/4));
951 tools::Polygon aPoly(std::size(aPoints));
952 for (size_t v = 0; v < std::size(aPoints); v++)
954 aPoly.SetPoint(Point(aSubRect.Left() +
955 aSubRect.GetWidth() * aPoints[v].nX,
956 aSubRect.Top() +
957 aSubRect.GetHeight() * aPoints[v].nY),
960 rDev.SetLineColor(COL_YELLOW);
961 rDev.SetFillColor(COL_BLACK);
962 rDev.DrawPolygon(aPoly);
964 // now move and add to the polypolygon
965 aPoly.Move(0, r.GetHeight()/2);
966 aPolyPoly.Insert(aPoly);
969 rDev.SetLineColor(COL_LIGHTRED);
970 rDev.SetFillColor(COL_GREEN);
971 rDev.DrawTransparent(aPolyPoly, 50);
975 struct DrawClipped : public RegionRenderer
977 RENDER_DETAILS(clip,KEY_D,10)
978 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
979 const RenderContext &) override
981 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(r, 2, 2));
982 const int nLimits[] = { 4, -100 };
983 for (int i = 0; i < 2; ++i)
985 sal_uInt16 nHue = 0;
986 rDev.Push(vcl::PushFlags::CLIPREGION);
987 tools::Rectangle aOuter = aRegions[i];
988 tools::Rectangle aInner = aOuter;
989 while (aInner.GetWidth() > nLimits[i] && aInner.GetHeight() > nLimits[i])
991 aInner.expand(-1);
992 rDev.SetClipRegion(vcl::Region(aInner));
993 rDev.SetFillColor(Color::HSBtoRGB(nHue, 75, 100));
994 nHue = (nHue + 97) % 360;
995 rDev.DrawRect(aOuter);
997 rDev.Pop();
1001 sal_uInt16 nHue = 0;
1002 tools::Rectangle aOuter = aRegions[2];
1003 std::vector<tools::Rectangle> aPieces(DemoRenderer::partition(aOuter, 2, 2));
1004 for (int j = 0; j < std::min(aOuter.GetWidth(), aOuter.GetHeight())/5; ++j)
1006 rDev.Push(vcl::PushFlags::CLIPREGION);
1008 vcl::Region aClipRegion;
1009 for (int i = 0; i < 4; ++i)
1011 aPieces[i].expand(-1);
1012 aPieces[i].Move(2 - i/2, 2 - i/2);
1013 aClipRegion.Union(aPieces[i]);
1015 assert (aClipRegion.getRegionBand());
1016 rDev.SetClipRegion(aClipRegion);
1017 rDev.SetFillColor(Color::HSBtoRGB(nHue, 75, 75));
1018 nHue = (nHue + 97) % 360;
1019 rDev.DrawRect(aOuter);
1021 rDev.Pop();
1026 sal_uInt16 nHue = 0;
1027 tools::Rectangle aOuter = aRegions[3];
1028 std::vector<tools::Rectangle> aPieces(DemoRenderer::partition(aOuter, 2, 2));
1029 bool bDone = false;
1030 while (!bDone)
1032 rDev.Push(vcl::PushFlags::CLIPREGION);
1034 for (int i = 0; i < 4; ++i)
1036 vcl::Region aClipRegion;
1037 tools::Polygon aPoly;
1038 switch (i) {
1039 case 3:
1040 case 0: // 45degree rectangle.
1041 aPoly = tools::Polygon(aPieces[i]);
1042 aPoly.Rotate(aPieces[i].Center(), 450_deg10);
1043 break;
1044 case 1: // arc
1045 aPoly = tools::Polygon(aPieces[i],
1046 aPieces[i].TopLeft(),
1047 aPieces[i].BottomRight());
1048 break;
1049 case 2:
1050 aPoly = tools::Polygon(aPieces[i],
1051 aPieces[i].GetWidth()/5,
1052 aPieces[i].GetHeight()/5);
1053 aPoly.Rotate(aPieces[i].Center(), 450_deg10);
1054 break;
1056 aClipRegion = vcl::Region(aPoly);
1057 aPieces[i].expand(-1);
1058 aPieces[i].Move(2 - i/2, 2 - i/2);
1060 bDone = aPieces[i].GetWidth() < 4 ||
1061 aPieces[i].GetHeight() < 4;
1063 if (!bDone)
1065 assert (!aClipRegion.getRegionBand());
1067 rDev.SetClipRegion(aClipRegion);
1068 rDev.SetFillColor(Color::HSBtoRGB(nHue, 50, 75));
1069 nHue = (nHue + 97) % 360;
1070 rDev.DrawRect(aOuter);
1074 rDev.Pop();
1080 struct DrawToVirtualDevice : public RegionRenderer
1082 RENDER_DETAILS(vdev,KEY_V,1)
1083 enum RenderType {
1084 RENDER_AS_BITMAP,
1085 RENDER_AS_OUTDEV,
1086 RENDER_AS_BITMAPEX,
1087 RENDER_AS_ALPHA_OUTDEV
1090 static void SizeAndRender(OutputDevice &rDev, const tools::Rectangle& r, RenderType eType,
1091 const RenderContext &rCtx)
1093 ScopedVclPtr<VirtualDevice> pNested;
1095 if (static_cast<int>(eType) < RENDER_AS_BITMAPEX)
1096 pNested = VclPtr<VirtualDevice>::Create(rDev).get();
1097 else
1098 pNested = VclPtr<VirtualDevice>::Create(rDev,DeviceFormat::WITH_ALPHA).get();
1100 pNested->SetOutputSizePixel(r.GetSize());
1101 tools::Rectangle aWhole(Point(0,0), r.GetSize());
1103 // mini me
1104 rCtx.mpDemoRenderer->drawToDevice(*pNested, r.GetSize(), true);
1106 if (eType == RENDER_AS_BITMAP)
1108 Bitmap aBitmap(pNested->GetBitmap(Point(0,0),aWhole.GetSize()));
1109 rDev.DrawBitmap(r.TopLeft(), aBitmap);
1111 else if (eType == RENDER_AS_BITMAPEX)
1113 BitmapEx aBitmapEx(pNested->GetBitmapEx(Point(0,0),aWhole.GetSize()));
1114 rDev.DrawBitmapEx(r.TopLeft(), aBitmapEx);
1116 else if (eType == RENDER_AS_OUTDEV ||
1117 eType == RENDER_AS_ALPHA_OUTDEV)
1119 rDev.DrawOutDev(r.TopLeft(), r.GetSize(),
1120 aWhole.TopLeft(), aWhole.GetSize(),
1121 *pNested);
1124 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1125 const RenderContext &rCtx) override
1127 // avoid infinite recursion
1128 if (rCtx.mbVDev)
1129 return;
1131 if (rCtx.meStyle == RENDER_EXPANDED)
1133 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx,2, 2));
1134 DemoRenderer::clearRects(rDev, aRegions);
1136 RenderType const eRenderTypes[] { RENDER_AS_BITMAP, RENDER_AS_OUTDEV,
1137 RENDER_AS_BITMAPEX, RENDER_AS_ALPHA_OUTDEV };
1138 for (size_t i = 0; i < aRegions.size(); i++)
1139 SizeAndRender(rDev, aRegions[i], eRenderTypes[i], rCtx);
1141 else
1142 SizeAndRender(rDev, r, RENDER_AS_BITMAP, rCtx);
1146 struct DrawXOR : public RegionRenderer
1148 RENDER_DETAILS(xor,KEY_X,1)
1150 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1151 const RenderContext &rCtx) override
1153 // avoid infinite recursion
1154 if (rCtx.mbVDev)
1155 return;
1157 rDev.Push();
1159 AntialiasingFlags nFlags = rDev.GetAntialiasing();
1160 rDev.SetAntialiasing(nFlags & ~AntialiasingFlags::Enable);
1161 rDev.SetRasterOp( RasterOp::Xor );
1163 rCtx.mpDemoRenderer->drawThumbs(rDev, r, true);
1165 rDev.Pop();
1169 struct DrawIcons : public RegionRenderer
1171 RENDER_DETAILS(icons,KEY_I,1)
1173 std::vector<OUString> maIconNames;
1174 std::vector<BitmapEx> maIcons;
1175 bool bHasLoadedAll;
1176 DrawIcons() : bHasLoadedAll(false)
1178 // a few icons to start with
1179 const char *pNames[] = {
1180 "cmd/lc_openurl.png",
1181 "cmd/lc_newdoc.png",
1182 "cmd/lc_choosemacro.png",
1183 "cmd/lc_save.png",
1184 "cmd/lc_saveas.png",
1185 "cmd/lc_importdialog.png",
1186 "cmd/lc_sendmail.png",
1187 "cmd/lc_editdoc.png",
1188 "cmd/lc_print.png",
1189 "cmd/lc_combobox.png",
1190 "cmd/lc_insertformcombo.png",
1191 "cmd/lc_printpreview.png",
1192 "cmd/lc_cut.png",
1193 "cmd/lc_copy.png",
1194 "cmd/lc_paste.png",
1195 "cmd/sc_autopilotmenu.png",
1196 "cmd/lc_formatpaintbrush.png",
1197 "cmd/lc_undo.png",
1198 "cmd/lc_redo.png",
1199 "cmd/lc_marks.png",
1200 "cmd/lc_fieldnames.png",
1201 "cmd/lc_hyperlinkdialog.png",
1202 "cmd/lc_basicshapes.rectangle.png",
1203 "cmd/lc_basicshapes.round-rectangle.png"
1205 for (size_t i = 0; i < SAL_N_ELEMENTS(pNames); i++)
1207 maIconNames.push_back(OUString::createFromAscii(pNames[i]));
1208 maIcons.emplace_back(maIconNames[i]);
1212 void LoadAllImages()
1214 if (bHasLoadedAll)
1215 return;
1216 bHasLoadedAll = true;
1218 css::uno::Reference<css::container::XNameAccess> xRef(ImageTree::get().getNameAccess());
1219 const css::uno::Sequence< OUString > aAllIcons = xRef->getElementNames();
1221 for (const auto& rIcon : aAllIcons)
1223 if (rIcon.endsWithIgnoreAsciiCase("svg"))
1224 continue; // too slow to load.
1225 maIconNames.push_back(rIcon);
1226 maIcons.emplace_back(rIcon);
1230 void doDrawIcons(OutputDevice &rDev, tools::Rectangle r, bool bExpanded)
1232 tools::Long nMaxH = 0;
1233 Point p(r.LeftCenter());
1234 size_t nToRender = maIcons.size();
1236 if (!bExpanded && maIcons.size() > 64)
1237 nToRender = 64;
1238 for (size_t i = 0; i < nToRender; i++)
1240 Size aSize(maIcons[i].GetSizePixel());
1241 // sAL_DEBUG("Draw icon '" << maIconNames[i] << "'");
1243 if (!(i % 4))
1244 rDev.DrawBitmapEx(p, maIcons[i]);
1245 else
1247 basegfx::B2DHomMatrix aTransform;
1248 aTransform.scale(aSize.Width(), aSize.Height());
1249 switch (i % 4)
1251 case 2:
1252 aTransform.shearX(static_cast<double>((i >> 2) % 8) / 8);
1253 aTransform.shearY(static_cast<double>((i >> 4) % 8) / 8);
1254 break;
1255 case 3:
1256 aTransform.translate(-aSize.Width()/2, -aSize.Height()/2);
1257 aTransform.rotate(i);
1258 if (i & 0x100)
1260 aTransform.shearX(static_cast<double>((i >> 2) % 8) / 8);
1261 aTransform.shearY(static_cast<double>((i >> 4) % 8) / 8);
1263 aTransform.translate(aSize.Width()/2, aSize.Height()/2);
1264 break;
1265 default:
1266 aTransform.translate(-aSize.Width()/2, -aSize.Height()/2);
1267 aTransform.rotate(2 * 2 * M_PI * i / nToRender);
1268 aTransform.translate(aSize.Width()/2, aSize.Height()/2);
1269 break;
1271 aTransform.translate(p.X(), p.Y());
1272 rDev.DrawTransformedBitmapEx(aTransform, maIcons[i]);
1275 // next position
1276 p.Move(aSize.Width(), 0);
1277 if (aSize.Height() > nMaxH)
1278 nMaxH = aSize.Height();
1279 if (p.X() >= r.Right()) // wrap to next line
1281 p = Point(r.Left(), p.Y() + nMaxH);
1282 nMaxH = 0;
1284 if (p.Y() >= r.Bottom()) // re-start at middle
1285 p = r.LeftCenter();
1289 static BitmapEx AlphaRecovery(OutputDevice &rDev, Point aPt, BitmapEx const &aSrc)
1291 // Compositing onto 2x colors beyond our control
1292 ScopedVclPtrInstance< VirtualDevice > aWhite;
1293 ScopedVclPtrInstance< VirtualDevice > aBlack;
1294 aWhite->SetOutputSizePixel(aSrc.GetSizePixel());
1295 aWhite->SetBackground(Wallpaper(COL_WHITE));
1296 aWhite->Erase();
1297 aBlack->SetOutputSizePixel(aSrc.GetSizePixel());
1298 aBlack->SetBackground(Wallpaper(COL_BLACK));
1299 aBlack->Erase();
1300 aWhite->DrawBitmapEx(Point(), aSrc);
1301 aBlack->DrawBitmapEx(Point(), aSrc);
1303 // Now recover that alpha...
1304 Bitmap aWhiteBmp = aWhite->GetBitmap(Point(),aSrc.GetSizePixel());
1305 Bitmap aBlackBmp = aBlack->GetBitmap(Point(),aSrc.GetSizePixel());
1306 AlphaMask aMask(aSrc.GetSizePixel());
1307 Bitmap aRecovered(aSrc.GetSizePixel(), vcl::PixelFormat::N24_BPP);
1309 BitmapScopedWriteAccess pMaskAcc(aMask);
1310 BitmapScopedWriteAccess pRecAcc(aRecovered);
1311 BitmapScopedReadAccess pAccW(aWhiteBmp); // a * pix + (1-a)
1312 BitmapScopedReadAccess pAccB(aBlackBmp); // a * pix + 0
1313 int nSizeX = aSrc.GetSizePixel().Width();
1314 int nSizeY = aSrc.GetSizePixel().Height();
1315 for (int y = 0; y < nSizeY; y++)
1317 Scanline pScanlineMask = pMaskAcc->GetScanline( y );
1318 Scanline pScanlineRec = pRecAcc->GetScanline( y );
1319 Scanline pScanlineW = pAccW->GetScanline( y );
1320 Scanline pScanlineB = pAccB->GetScanline( y );
1321 for (int x = 0; x < nSizeX; x++)
1323 BitmapColor aColW = pAccW->GetPixelFromData(pScanlineW,x);
1324 BitmapColor aColB = pAccB->GetPixelFromData(pScanlineB,x);
1325 tools::Long nAR = static_cast<tools::Long>(aColW.GetRed() - aColB.GetRed()); // (1-a)
1326 tools::Long nAG = static_cast<tools::Long>(aColW.GetGreen() - aColB.GetGreen()); // (1-a)
1327 tools::Long nAB = static_cast<tools::Long>(aColW.GetBlue() - aColB.GetBlue()); // (1-a)
1329 #define CLAMP(a,b,c) (((a)<=(b))?(b):(((a)>=(c))?(c):(a)))
1331 // we get the most precision from the largest delta
1332 tools::Long nInverseAlpha = std::max(nAR, std::max(nAG, nAB)); // (1-a)
1333 nInverseAlpha = CLAMP(nInverseAlpha, 0, 255);
1334 tools::Long nAlpha = 255 - nInverseAlpha;
1336 pMaskAcc->SetPixelOnData(pScanlineMask,x,BitmapColor(static_cast<sal_Int8>(CLAMP(nInverseAlpha,0,255))));
1337 // now recover the pixels
1338 tools::Long nR = (aColW.GetRed() + aColB.GetRed() - nInverseAlpha) * 128;
1339 tools::Long nG = (aColW.GetGreen() + aColB.GetGreen() - nInverseAlpha) * 128;
1340 tools::Long nB = (aColW.GetBlue() + aColB.GetBlue() - nInverseAlpha) * 128;
1341 if (nAlpha == 0)
1342 { // doesn't matter what's behind transparency
1343 nR = nG = nB = 0;
1345 else
1347 nR /= nAlpha; nG /= nAlpha; nB /= nAlpha;
1349 pRecAcc->SetPixelOnData(pScanlineRec,x,BitmapColor(
1350 static_cast<sal_uInt8>(CLAMP(nR,0,255)),
1351 static_cast<sal_uInt8>(CLAMP(nG,0,255)),
1352 static_cast<sal_uInt8>(CLAMP(nB,0,255))));
1353 #undef CLAMP
1357 rDev.DrawBitmap(aPt, aWhiteBmp);
1358 aPt.Move(aSrc.GetSizePixel().Width(), 0);
1359 rDev.DrawBitmap(aPt, aBlackBmp);
1360 aPt.Move(aSrc.GetSizePixel().Width(), 0);
1361 rDev.DrawBitmap(aPt, aRecovered);
1362 aPt.Move(aSrc.GetSizePixel().Width(), 0);
1363 rDev.DrawBitmap(aPt, aMask.GetBitmap());
1364 aPt.Move(aSrc.GetSizePixel().Width(), 0);
1366 return BitmapEx(aRecovered, aMask);
1369 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1370 const RenderContext &rCtx) override
1372 if (rCtx.meStyle == RENDER_EXPANDED)
1374 LoadAllImages();
1376 Point aLocation(0,maIcons[0].GetSizePixel().Height() + 8);
1377 for (size_t i = 0; i < maIcons.size(); i++)
1379 BitmapEx aSrc = maIcons[i];
1381 // original above
1382 Point aAbove(aLocation);
1383 aAbove.Move(0,-aSrc.GetSizePixel().Height() - 4);
1384 rDev.DrawBitmapEx(aAbove, aSrc);
1385 aAbove.Move(aSrc.GetSizePixel().Width(),0);
1386 aAbove.Move(aSrc.GetSizePixel().Width(),0);
1387 rDev.DrawBitmap(aAbove, aSrc.GetBitmap());
1388 aAbove.Move(aSrc.GetSizePixel().Width(),0);
1389 rDev.DrawBitmap(aAbove, aSrc.GetAlphaMask().GetBitmap());
1391 // intermediates middle
1392 BitmapEx aResult = AlphaRecovery(rDev, aLocation, aSrc);
1394 // result below
1395 Point aBelow(aLocation);
1396 aBelow.Move(0,aResult.GetSizePixel().Height());
1397 rDev.DrawBitmapEx(aBelow, aResult);
1399 // mini convert test.
1400 aBelow.Move(aResult.GetSizePixel().Width()+4,0);
1401 rDev.DrawBitmapEx(aBelow, aResult);
1403 Bitmap aGrey = aSrc.GetBitmap();
1404 aGrey.Convert(BmpConversion::N8BitGreys);
1405 rDev.DrawBitmap(aBelow, aGrey);
1407 aBelow.Move(aGrey.GetSizePixel().Width(),0);
1408 BitmapEx aGreyMask(aSrc);
1409 rDev.DrawBitmapEx(aBelow, aGreyMask);
1411 aLocation.Move(aSrc.GetSizePixel().Width()*6,0);
1412 if (aLocation.X() > r.Right())
1413 aLocation = Point(0,aLocation.Y()+aSrc.GetSizePixel().Height()*3+4);
1416 // now go crazy with random foo
1417 doDrawIcons(rDev, r, true);
1419 else
1421 doDrawIcons(rDev, r, false);
1426 struct FetchDrawBitmap : public RegionRenderer
1428 RENDER_DETAILS(fetchdraw,KEY_F,50)
1429 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1430 const RenderContext &) override
1432 Bitmap aBitmap(rDev.GetBitmap(Point(0,0),rDev.GetOutputSizePixel()));
1433 aBitmap.Scale(r.GetSize(), BmpScaleFlag::BestQuality);
1434 rDev.DrawBitmap(r.TopLeft(), aBitmap);
1438 void drawThumbs(vcl::RenderContext& rDev, tools::Rectangle aRect, bool bVDev)
1440 RenderContext aCtx;
1441 aCtx.meStyle = RENDER_THUMB;
1442 aCtx.mbVDev = bVDev;
1443 aCtx.mpDemoRenderer = this;
1444 aCtx.maSize = aRect.GetSize();
1445 std::vector<tools::Rectangle> aRegions(partition(aRect, mnSegmentsX, mnSegmentsY));
1446 DemoRenderer::clearRects(rDev, aRegions);
1447 for (size_t i = 0; i < maRenderers.size(); i++)
1449 RegionRenderer * r = maRenderers[i];
1451 rDev.SetClipRegion( vcl::Region( aRegions[i] ) );
1453 // profiling?
1454 if (getIterCount() > 0)
1456 if (!bVDev)
1458 double nStartTime = getTimeNow();
1459 for (int j = 0; j < r->getTestRepeatCount() * THUMB_REPEAT_FACTOR; j++)
1460 r->RenderRegion(rDev, aRegions[i], aCtx);
1461 addTime(i, (getTimeNow() - nStartTime) / THUMB_REPEAT_FACTOR);
1462 } else
1463 for (int j = 0; j < r->getTestRepeatCount(); j++)
1464 r->RenderRegion(rDev, aRegions[i], aCtx);
1466 else
1467 r->RenderRegion(rDev, aRegions[i], aCtx);
1469 rDev.SetClipRegion();
1473 void drawToDevice(vcl::RenderContext& rDev, Size aSize, bool bVDev)
1475 RenderContext aCtx;
1476 aCtx.mbVDev = bVDev;
1477 aCtx.mpDemoRenderer = this;
1478 aCtx.maSize = aSize;
1479 tools::Rectangle aWholeWin(Point(0,0), rDev.GetOutputSizePixel());
1481 drawBackground(rDev, aWholeWin);
1483 if (!bVDev /* want everything in the vdev */ &&
1484 mnSelectedRenderer >= 0 &&
1485 o3tl::make_unsigned(mnSelectedRenderer) < maRenderers.size())
1487 aCtx.meStyle = RENDER_EXPANDED;
1488 RegionRenderer * r = maRenderers[mnSelectedRenderer];
1489 // profiling?
1490 if (getIterCount() > 0)
1492 double nStartTime = getTimeNow();
1493 for (int i = 0; i < r->getTestRepeatCount(); i++)
1494 r->RenderRegion(rDev, aWholeWin, aCtx);
1495 addTime(mnSelectedRenderer, getTimeNow() - nStartTime);
1496 } else
1497 r->RenderRegion(rDev, aWholeWin, aCtx);
1499 else
1500 drawThumbs(rDev, aWholeWin, bVDev);
1502 std::vector<VclPtr<vcl::Window> > maInvalidates;
1503 void addInvalidate(vcl::Window *pWindow) { maInvalidates.emplace_back(pWindow); };
1504 void removeInvalidate(vcl::Window *pWindow)
1506 auto aIt = std::find(maInvalidates.begin(), maInvalidates.end(), pWindow);
1507 if (aIt != maInvalidates.end())
1508 maInvalidates.erase(aIt);
1510 void Invalidate()
1512 for (auto const& invalidate : maInvalidates)
1513 invalidate->Invalidate();
1519 #if FIXME_BOUNCE_BUTTON
1520 IMPL_LINK_NOARG(DemoRenderer,BounceTimerCb,Timer*,void)
1522 mpButton->Check(mnBounceX>0);
1523 mpButton->SetPressed(mnBounceY>0);
1525 Point aCur = mpButtonWin->GetPosPixel();
1526 static const int nMovePix = 10;
1527 aCur.Move(mnBounceX * nMovePix, mnBounceX * nMovePix);
1528 Size aWinSize = GetSizePixel();
1529 if (aCur.X() <= 0 || aCur.X() >= aWinSize.Width())
1530 mnBounceX *= -1;
1531 if (aCur.Y() <= 0 || aCur.Y() >= aWinSize.Height())
1532 mnBounceX *= -1;
1533 mpButtonWin->SetPosPixel(aCur);
1535 // All smoke and mirrors to test sub-region invalidation underneath
1536 Rectangle aRect(aCur, mpButtonWin->GetSizePixel());
1537 Invalidate(aRect);
1539 #endif
1541 void DemoRenderer::KeyInput(const KeyEvent &rKEvt)
1543 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
1545 // click to zoom out
1546 if (mnSelectedRenderer >= 0)
1548 if (nCode == KEY_ESCAPE || nCode == KEY_BACKSPACE)
1550 mnSelectedRenderer = -1;
1551 Invalidate();
1552 return;
1555 else
1557 for (size_t i = 0; i < maRenderers.size(); i++)
1559 if (nCode == maRenderers[i]->getAccelerator())
1561 mnSelectedRenderer = i;
1562 Invalidate();
1563 return;
1569 bool DemoRenderer::MouseButtonDown(const MouseEvent& rMEvt)
1571 // click to zoom out
1572 if (mnSelectedRenderer >= 0)
1574 mnSelectedRenderer = -1;
1575 Invalidate();
1576 return true;
1579 // click on a region to zoom into it
1580 std::vector<tools::Rectangle> aRegions(partition(GetSizePixel(), mnSegmentsX, mnSegmentsY));
1581 for (size_t i = 0; i < aRegions.size(); i++)
1583 if (aRegions[i].Contains(rMEvt.GetPosPixel()))
1585 mnSelectedRenderer = i;
1586 Invalidate();
1587 return true;
1591 #if FIXME_BOUNCE_BUTTON
1592 // otherwise bounce floating windows
1593 if (!mpButton)
1595 mpButtonWin = VclPtr<FloatingWindow>::Create(this);
1596 mpButton = VclPtr<PushButton>::Create(mpButtonWin);
1597 mpButton->SetSymbol(SymbolType::HELP);
1598 mpButton->SetText("PushButton demo");
1599 mpButton->SetPosSizePixel(Point(0,0), mpButton->GetOptimalSize());
1600 mpButton->Show();
1601 mpButtonWin->SetPosSizePixel(Point(0,0), mpButton->GetOptimalSize());
1602 mpButtonWin->Show();
1603 mnBounceX = 1; mnBounceX = 1;
1604 maBounce.SetInvokeHandler(LINK(this,DemoRenderer,BounceTimerCb));
1605 maBounce.SetTimeout(55);
1606 maBounce.Start();
1608 else
1610 maBounce.Stop();
1611 delete mpButtonWin;
1612 mpButtonWin = NULL;
1613 mpButton = NULL;
1615 #endif
1616 return false;
1619 void DemoRenderer::InitRenderers()
1621 maRenderers.push_back(new DrawLines);
1622 maRenderers.push_back(new DrawText);
1623 maRenderers.push_back(new DrawPoly);
1624 maRenderers.push_back(new DrawEllipse);
1625 maRenderers.push_back(new DrawCheckered);
1626 maRenderers.push_back(new DrawBitmapEx);
1627 maRenderers.push_back(new DrawBitmap);
1628 maRenderers.push_back(new DrawGradient);
1629 maRenderers.push_back(new DrawPolyPolygons);
1630 maRenderers.push_back(new DrawClipped);
1631 maRenderers.push_back(new DrawToVirtualDevice);
1632 maRenderers.push_back(new DrawXOR);
1633 maRenderers.push_back(new DrawIcons());
1634 maRenderers.push_back(new FetchDrawBitmap);
1637 OUString DemoRenderer::getRendererList()
1639 OUStringBuffer aBuf;
1640 for (size_t i = 0; i < maRenderers.size(); i++)
1642 aBuf.append(maRenderers[i]->getName());
1643 aBuf.append(' ');
1645 return aBuf.makeStringAndClear();
1648 double DemoRenderer::getAndResetBenchmark(const RenderStyle style)
1650 double geomean = 1.0;
1651 fprintf(stderr, "Rendering: %s, Times (ms):\n", style == RENDER_THUMB ? "THUMB": "EXPANDED");
1652 for (size_t i = 0; i < maRenderers.size(); i++)
1654 double avgtime = maRenderers[i]->sumTime / maRenderers[i]->countTime;
1655 geomean *= avgtime;
1656 fprintf(stderr, "%s: %f (iteration: %d*%d*%d)\n",
1657 OUStringToOString(maRenderers[i]->getName(),
1658 RTL_TEXTENCODING_UTF8).getStr(), avgtime,
1659 maRenderers[i]->countTime, maRenderers[i]->getTestRepeatCount(),
1660 (style == RENDER_THUMB) ? THUMB_REPEAT_FACTOR : 1);
1661 maRenderers[i]->sumTime = 0;
1662 maRenderers[i]->countTime = 0;
1664 geomean = pow(geomean, 1.0/maRenderers.size());
1665 fprintf(stderr, "GEOMEAN_%s: %f\n", style == RENDER_THUMB ? "THUMB": "EXPANDED", geomean);
1666 return geomean;
1669 void DemoRenderer::setIterCount(sal_Int32 i)
1671 iterCount = i;
1674 sal_Int32 DemoRenderer::getIterCount() const
1676 return iterCount;
1679 void DemoRenderer::addTime(int i, double t)
1681 maRenderers[i]->sumTime += t / maRenderers[i]->getTestRepeatCount();
1682 maRenderers[i]->countTime++;
1685 void DemoRenderer::selectRenderer(std::u16string_view rName )
1687 for (size_t i = 0; i < maRenderers.size(); i++)
1689 if (maRenderers[i]->getName() == rName)
1691 mnSelectedRenderer = i;
1692 Invalidate();
1693 return;
1698 int DemoRenderer::selectNextRenderer()
1700 mnSelectedRenderer++;
1701 if (mnSelectedRenderer == static_cast<signed>(maRenderers.size()))
1702 mnSelectedRenderer = -1;
1703 Invalidate();
1704 return mnSelectedRenderer;
1707 namespace {
1709 class DemoWin : public WorkWindow
1711 DemoRenderer &mrRenderer;
1712 bool underTesting;
1713 bool testThreads;
1715 class RenderThread final : public salhelper::Thread {
1716 DemoWin &mrWin;
1717 sal_uInt32 const mnDelaySecs = 0;
1718 public:
1719 RenderThread(DemoWin &rWin, sal_uInt32 nDelaySecs)
1720 : Thread("vcldemo render thread")
1721 , mrWin(rWin)
1722 , mnDelaySecs(nDelaySecs)
1724 launch();
1726 virtual ~RenderThread() override
1728 join();
1730 virtual void execute() override
1732 std::this_thread::sleep_for(std::chrono::seconds(mnDelaySecs));
1734 SolarMutexGuard aGuard;
1735 fprintf (stderr, "render from a different thread\n");
1736 mrWin.Invalidate();
1739 rtl::Reference<RenderThread> mxThread;
1741 public:
1742 DemoWin(DemoRenderer &rRenderer, bool bThreads) :
1743 WorkWindow(nullptr, WB_APP | WB_STDWORK),
1744 mrRenderer(rRenderer),
1745 testThreads(bThreads)
1747 mrRenderer.addInvalidate(this);
1748 underTesting = false;
1750 virtual ~DemoWin() override
1752 disposeOnce();
1754 virtual void dispose() override
1756 mxThread.clear();
1757 mrRenderer.removeInvalidate(this);
1758 WorkWindow::dispose();
1760 virtual void MouseButtonDown(const MouseEvent& rMEvt) override
1762 mrRenderer.SetSizePixel(GetSizePixel());
1763 if (mrRenderer.MouseButtonDown(rMEvt))
1764 return;
1766 if (testThreads)
1767 { // render this window asynchronously in a new thread
1768 sal_uInt32 nDelaySecs = 0;
1769 if (rMEvt.GetButtons() & MOUSE_RIGHT)
1770 nDelaySecs = 5;
1771 mxThread = new RenderThread(*this, nDelaySecs);
1773 else
1774 { // spawn another window
1775 VclPtrInstance<DemoWin> pNewWin(mrRenderer, testThreads);
1776 pNewWin->SetText("Another interactive VCL demo window");
1777 pNewWin->Show();
1780 virtual void KeyInput(const KeyEvent& rKEvt) override
1782 mrRenderer.SetSizePixel(GetSizePixel());
1783 mrRenderer.KeyInput(rKEvt);
1785 virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override
1787 mrRenderer.SetSizePixel(GetSizePixel());
1788 fprintf(stderr, "DemoWin::Paint(%" SAL_PRIdINT64 ",%" SAL_PRIdINT64 ",%" SAL_PRIdINT64 ",%" SAL_PRIdINT64 ")\n", sal_Int64(rRect.Left()), sal_Int64(rRect.Top()), sal_Int64(rRect.getOpenWidth()), sal_Int64(rRect.getOpenHeight()));
1789 if (mrRenderer.getIterCount() == 0)
1790 mrRenderer.drawToDevice(rRenderContext, GetSizePixel(), false);
1791 else
1792 TestAndQuit(rRenderContext);
1795 void TestAndQuit(vcl::RenderContext& rRenderContext)
1797 if (underTesting)
1798 return;
1799 underTesting = true;
1800 for (sal_Int32 i = 0; i < mrRenderer.getIterCount(); i++)
1802 while (mrRenderer.selectNextRenderer() > -1)
1804 mrRenderer.drawToDevice(rRenderContext, GetSizePixel(), false);
1808 double expandedGEOMEAN = mrRenderer.getAndResetBenchmark(RENDER_EXPANDED);
1810 for (sal_Int32 i = 0; i < mrRenderer.getIterCount(); i++)
1811 mrRenderer.drawToDevice(rRenderContext, GetSizePixel(), false);
1813 double thumbGEOMEAN = mrRenderer.getAndResetBenchmark(RENDER_THUMB);
1815 fprintf(stderr, "GEOMEAN_TOTAL: %f\n", pow(thumbGEOMEAN * expandedGEOMEAN, 0.5));
1816 Application::Quit();
1820 struct PointerData {
1821 PointerStyle eStyle;
1822 const char * name;
1827 const PointerData gvPointerData [] = {
1828 { PointerStyle::Null, "Null" },
1829 { PointerStyle::Magnify, "Magnify" },
1830 { PointerStyle::Fill, "Fill" },
1831 { PointerStyle::MoveData, "MoveData" },
1832 { PointerStyle::CopyData, "CopyData" },
1833 { PointerStyle::MoveFile, "MoveFile" },
1834 { PointerStyle::CopyFile, "CopyFile" },
1835 { PointerStyle::MoveFiles, "MoveFiles" },
1836 { PointerStyle::CopyFiles, "CopyFiles" },
1837 { PointerStyle::NotAllowed, "NotAllowed" },
1838 { PointerStyle::Rotate, "Rotate" },
1839 { PointerStyle::HShear, "HShear" },
1840 { PointerStyle::VShear, "VShear" },
1841 { PointerStyle::DrawLine, "DrawLine" },
1842 { PointerStyle::DrawRect, "DrawRect" },
1843 { PointerStyle::DrawPolygon, "DrawPolygon" },
1844 { PointerStyle::DrawBezier, "DrawBezier" },
1845 { PointerStyle::DrawArc, "DrawArc" },
1846 { PointerStyle::DrawPie, "DrawPie" },
1847 { PointerStyle::DrawCircleCut, "DrawCircleCut" },
1848 { PointerStyle::DrawEllipse, "DrawEllipse" },
1849 { PointerStyle::DrawConnect, "DrawConnect" },
1850 { PointerStyle::DrawText, "DrawText" },
1851 { PointerStyle::Mirror, "Mirror" },
1852 { PointerStyle::Crook, "Crook" },
1853 { PointerStyle::Crop, "Crop" },
1854 { PointerStyle::MovePoint, "MovePoint" },
1855 { PointerStyle::MoveBezierWeight, "MoveBezierWeight" },
1856 { PointerStyle::DrawFreehand, "DrawFreehand" },
1857 { PointerStyle::DrawCaption, "DrawCaption" },
1858 { PointerStyle::LinkData, "LinkData" },
1859 { PointerStyle::MoveDataLink, "MoveDataLink" },
1860 { PointerStyle::CopyDataLink, "CopyDataLink" },
1861 { PointerStyle::LinkFile, "LinkFile" },
1862 { PointerStyle::MoveFileLink, "MoveFileLink" },
1863 { PointerStyle::CopyFileLink, "CopyFileLink" },
1864 { PointerStyle::Chart, "Chart" },
1865 { PointerStyle::Detective, "Detective" },
1866 { PointerStyle::PivotCol, "PivotCol" },
1867 { PointerStyle::PivotRow, "PivotRow" },
1868 { PointerStyle::PivotField, "PivotField" },
1869 { PointerStyle::PivotDelete, "PivotDelete" },
1870 { PointerStyle::Chain, "Chain" },
1871 { PointerStyle::ChainNotAllowed, "ChainNotAllowed" },
1872 { PointerStyle::AutoScrollN, "AutoScrollN" },
1873 { PointerStyle::AutoScrollS, "AutoScrollS" },
1874 { PointerStyle::AutoScrollW, "AutoScrollW" },
1875 { PointerStyle::AutoScrollE, "AutoScrollE" },
1876 { PointerStyle::AutoScrollNW, "AutoScrollNW" },
1877 { PointerStyle::AutoScrollNE, "AutoScrollNE" },
1878 { PointerStyle::AutoScrollSW, "AutoScrollSW" },
1879 { PointerStyle::AutoScrollSE, "AutoScrollSE" },
1880 { PointerStyle::AutoScrollNS, "AutoScrollNS" },
1881 { PointerStyle::AutoScrollWE, "AutoScrollWE" },
1882 { PointerStyle::AutoScrollNSWE, "AutoScrollNSWE" },
1883 { PointerStyle::TextVertical, "TextVertical" },
1884 { PointerStyle::TabSelectS, "TabSelectS" },
1885 { PointerStyle::TabSelectE, "TabSelectE" },
1886 { PointerStyle::TabSelectSE, "TabSelectSE" },
1887 { PointerStyle::TabSelectW, "TabSelectW" },
1888 { PointerStyle::TabSelectSW, "TabSelectSW" },
1889 { PointerStyle::HideWhitespace, "HideWhitespace" },
1890 { PointerStyle::ShowWhitespace, "ShowWhitespace" },
1891 { PointerStyle::FatCross, "FatCross" },
1894 namespace {
1896 class DemoWidgets : public WorkWindow
1898 VclPtr<MenuBar> mpBar;
1899 VclPtr<VclBox> mpBox;
1900 VclPtr<ToolBox> mpToolbox;
1901 VclPtr<PushButton> mpButton;
1902 std::vector<VclPtr<VclHBox>> mvCursorBoxes;
1903 std::vector<VclPtr<PushButton>> mvCursorButtons;
1905 DECL_LINK(CursorButtonClick, Button*, void);
1907 public:
1908 DemoWidgets() :
1909 WorkWindow(nullptr, WB_APP | WB_STDWORK),
1910 mpBox(VclPtrInstance<VclVBox>(this, false, 3)),
1911 mpToolbox(VclPtrInstance<ToolBox>(mpBox.get())),
1912 mpButton(VclPtrInstance<PushButton>(mpBox.get()))
1914 SetText("VCL widget demo");
1916 Wallpaper aWallpaper(BitmapEx("sfx2/res/128x128_writer_doc-p.png"));
1917 aWallpaper.SetStyle(WallpaperStyle::BottomRight);
1918 aWallpaper.SetColor(COL_RED);
1920 mpBox->SetBackground(aWallpaper);
1921 mpBox->Show();
1923 Help::EnableBalloonHelp();
1924 mpToolbox->SetHelpText("Help text");
1925 mpToolbox->InsertItem(ToolBoxItemId(0), "Toolbar item", OUString());
1926 mpToolbox->SetQuickHelpText(ToolBoxItemId(0), "This is a tooltip popup");
1927 mpToolbox->InsertSeparator();
1928 mpToolbox->Show();
1930 mpButton->SetText("Click me; go on");
1931 mpButton->Show();
1933 int i = 0;
1934 VclHBox* pCurrentCursorHBox = nullptr;
1935 constexpr int numButtonsPerRow = 9;
1936 for (auto & rData : gvPointerData)
1938 if (i % numButtonsPerRow == 0)
1940 mvCursorBoxes.push_back(VclPtrInstance<VclHBox>(mpBox.get(), true, numButtonsPerRow));
1941 pCurrentCursorHBox = mvCursorBoxes.back().get();
1942 pCurrentCursorHBox->Show();
1944 mvCursorButtons.emplace_back(VclPtrInstance<PushButton>(pCurrentCursorHBox));
1945 PushButton& rButton = *mvCursorButtons.back();
1946 rButton.SetText(OUString::createFromAscii(rData.name));
1947 rButton.SetClickHdl(LINK(this,DemoWidgets,CursorButtonClick));
1948 rButton.Show();
1949 ++i;
1952 mpBar = VclPtr<MenuBar>::Create();
1953 mpBar->InsertItem(0,"File");
1954 VclPtrInstance<PopupMenu> pPopup;
1955 pPopup->InsertItem(0,"Item");
1956 mpBar->SetPopupMenu(0, pPopup);
1957 SetMenuBar(mpBar);
1959 Show();
1961 virtual ~DemoWidgets() override { disposeOnce(); }
1962 virtual void dispose() override
1964 for (auto & p : mvCursorButtons)
1965 p.disposeAndClear();
1966 mvCursorButtons.clear();
1967 for (auto & p : mvCursorBoxes)
1968 p.disposeAndClear();
1969 mvCursorBoxes.clear();
1970 mpToolbox.disposeAndClear();
1971 mpButton.disposeAndClear();
1972 mpBox.disposeAndClear();
1973 mpBar.disposeAndClear();
1974 WorkWindow::dispose();
1976 virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override
1978 tools::Rectangle aWholeSize(Point(0, 0),GetOutputSizePixel());
1979 vcl::Region aClip(aWholeSize);
1980 tools::Rectangle aExclude(tools::Rectangle(Point(50,50),Size(100,100)));
1981 aClip.Exclude(aExclude);
1983 Wallpaper aWallpaper(COL_GREEN);
1985 rRenderContext.Push(vcl::PushFlags::CLIPREGION);
1986 rRenderContext.IntersectClipRegion(aClip);
1987 rRenderContext.DrawWallpaper(aWholeSize, aWallpaper);
1988 rRenderContext.Pop();
1990 ScopedVclPtrInstance< VirtualDevice > pDev(*GetOutDev());
1991 pDev->EnableRTL(IsRTLEnabled());
1992 pDev->SetOutputSizePixel(aExclude.GetSize());
1994 tools::Rectangle aSubRect(aWholeSize);
1995 aSubRect.Move(-aExclude.Left(), -aExclude.Top());
1996 pDev->DrawWallpaper(aSubRect, aWallpaper );
1998 rRenderContext.DrawOutDev(aExclude.TopLeft(), aExclude.GetSize(),
1999 Point( 0, 0 ), aExclude.GetSize(), *pDev );
2005 IMPL_LINK(DemoWidgets, CursorButtonClick, Button*, pButton, void)
2007 for (size_t i=0; i<SAL_N_ELEMENTS(gvPointerData); ++i)
2009 if (mvCursorButtons[i].get() == pButton)
2011 mpBox->SetPointer( gvPointerData[i].eStyle );
2012 return;
2015 assert(false);
2018 namespace {
2020 class DemoPopup : public FloatingWindow
2022 public:
2023 DemoPopup() : FloatingWindow( nullptr, WB_SYSTEMWINDOW|WB_TOOLTIPWIN)
2025 SetType( WindowType::HELPTEXTWINDOW );
2027 SetOutputSizePixel( Size( 300, 30 ) );
2028 SetBackground(Wallpaper(COL_YELLOW));
2030 Show( true, ShowFlags::NoActivate );
2031 PaintImmediately();
2034 virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override
2036 Size aSize = GetOutputSizePixel();
2037 tools::Rectangle aTextRect(Point(6, 6), aSize);
2039 SetTextColor(COL_BLACK);
2040 SetTextAlign(ALIGN_TOP);
2041 rRenderContext.DrawText(aTextRect, "This is a standalone help text test",
2042 DrawTextFlags::MultiLine|DrawTextFlags::WordBreak|
2043 DrawTextFlags::Left|DrawTextFlags::Top);
2045 rRenderContext.SetLineColor(COL_BLACK);
2046 rRenderContext.SetFillColor();
2047 rRenderContext.DrawRect( tools::Rectangle( Point(), aSize ) );
2048 aSize.AdjustWidth( -2 );
2049 aSize.AdjustHeight( -2 );
2050 Color aColor( rRenderContext.GetLineColor() );
2051 rRenderContext.SetLineColor( COL_GRAY );
2052 rRenderContext.DrawRect( tools::Rectangle( Point( 1, 1 ), aSize ) );
2053 rRenderContext.SetLineColor( aColor );
2056 virtual void MouseButtonDown( const MouseEvent & ) override
2058 Application::Quit();
2064 namespace {
2065 void renderFonts()
2067 ScopedVclPtrInstance<VirtualDevice> xDevice;
2068 Size aSize(1024, 1024);
2069 xDevice->SetOutputSizePixel(aSize);
2071 #if 0
2072 for (auto & aFontName : aFontNames)
2074 vcl::Font aFont(aFontName, Size(0,96));
2076 aFont.Set(COL_BLACK);
2077 xDevice->SetFont(aFont);
2078 xDevice->Erase();
2080 FontMetric aMetric = xDevice->GetFontMetric(aFont);
2082 FontCharMapRef xMap;
2083 if (xDevice->GetFontCharMap(xMap))
2085 ... iterate through glyphs ...
2089 bool GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr, int nIndex,
2090 int nLen, int nBase, MetricVector& rVector );
2092 include/vcl/outdev.hxx:typedef std::vector< Rectangle > MetricVector;
2093 include/vcl/outdev.hxx: MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr );
2094 include/vcl/outdev.hxx: MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr,
2095 include/vcl/outdev.hxx: MetricVector* pVector, OUString* pDisplayText, vcl::ITextLayout& _rLayout );
2096 include/vcl/outdev.hxx: DrawTextFlags nStyle = DrawTextFlags::Mnemonic, MetricVector* pVector = nullp
2098 bool GetTextBoundRect( Rectangle& rRect,
2099 const OUString& rStr, sal_Int32 nBase = 0, sal_Int32 nIndex = 0, sal_Int32 nLen = -1,
2100 sal_uLong nLayoutWidth = 0, const long* pDXArray = nullptr ) const;
2103 void DrawText( const Point& rStartPt, const OUString& rStr,
2104 sal_Int32 nIndex = 0, sal_Int32 nLen = -1,
2105 MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr );
2107 void DrawText( const Rectangle& rRect,
2108 const OUString& rStr, DrawTextFlags nStyle = DrawTextFlags::NONE,
2109 MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr,
2110 vcl::ITextLayout* _pTextLayout = nullptr );
2112 Rectangle GetTextRect( const Rectangle& rRect,
2113 const OUString& rStr, DrawTextFlags nStyle = DrawTextFlags::WordBreak,
2114 TextRectInfo* pInfo = nullptr,
2115 const vcl::ITextLayout* _pTextLayout = nullptr ) const;
2118 #endif
2123 namespace {
2125 class DemoApp : public Application
2127 static int showHelp(DemoRenderer &rRenderer)
2129 fprintf(stderr,"vcldemo - a VCL test app\n");
2130 fprintf(stderr," --help - print this text\n");
2131 fprintf(stderr," --show <renderer> - start with a given renderer, options are:\n");
2132 OUString aRenderers(rRenderer.getRendererList());
2133 fprintf(stderr," %s\n",
2134 OUStringToOString(aRenderers, RTL_TEXTENCODING_UTF8).getStr());
2135 fprintf(stderr," --test <iterCount> - create benchmark data\n");
2136 fprintf(stderr," --widgets - launch the widget test.\n");
2137 fprintf(stderr," --popup - launch the popup test.\n");
2138 fprintf(stderr," --threads - render from multiple threads.\n");
2139 fprintf(stderr," --font <fontname> - run the font render test.\n");
2140 fprintf(stderr, "\n");
2141 return 0;
2144 public:
2145 DemoApp() {}
2147 virtual int Main() override
2151 bool bWidgets = false;
2152 bool bThreads = false;
2153 bool bPopup = false;
2154 DemoRenderer aRenderer;
2155 std::vector<OUString> aFontNames;
2157 for (sal_uInt16 i = 0; i < GetCommandLineParamCount(); ++i)
2159 bool bLast = i == GetCommandLineParamCount() - 1;
2160 OUString aArg = GetCommandLineParam(i);
2161 if (aArg == "--help" || aArg == "-h")
2162 return showHelp(aRenderer);
2163 if (aArg == "--show")
2165 if (bLast)
2166 return showHelp(aRenderer);
2167 else
2168 aRenderer.selectRenderer(GetCommandLineParam(++i));
2170 else if (aArg == "--test")
2172 if (bLast)
2173 return showHelp(aRenderer);
2174 else
2175 aRenderer.setIterCount(GetCommandLineParam(++i).toInt32());
2177 else if (aArg == "--widgets")
2178 bWidgets = true;
2179 else if (aArg == "--popup")
2180 bPopup = true;
2181 else if (aArg == "--threads")
2182 bThreads = true;
2183 else if (aArg == "--font" && !bLast)
2184 aFontNames.push_back(GetCommandLineParam(++i));
2185 else if (aArg.startsWith("--"))
2187 fprintf(stderr,"Unknown argument '%s'\n",
2188 OUStringToOString(aArg, RTL_TEXTENCODING_UTF8).getStr());
2189 return showHelp(aRenderer);
2193 ScopedVclPtrInstance<DemoWin> aMainWin(aRenderer, bThreads);
2194 VclPtr<DemoWidgets> xWidgets;
2195 VclPtr<DemoPopup> xPopup;
2197 aMainWin->SetText("Interactive VCL demo #1");
2198 if (bWidgets)
2199 xWidgets = VclPtr< DemoWidgets >::Create ();
2200 else if (bPopup)
2201 xPopup = VclPtrInstance< DemoPopup> ();
2202 else if (!aFontNames.empty())
2203 renderFonts();
2204 else
2205 aMainWin->Show();
2207 Application::Execute();
2209 xWidgets.disposeAndClear();
2210 xPopup.disposeAndClear();
2212 catch (const css::uno::Exception&)
2214 TOOLS_WARN_EXCEPTION("vcl.app", "Fatal");
2215 return 1;
2217 catch (const std::exception& e)
2219 SAL_WARN("vcl.app", "Fatal: " << e.what());
2220 return 1;
2222 return 0;
2225 protected:
2226 void Init() override
2228 LanguageTag::setConfiguredSystemLanguage(MsLangId::getSystemLanguage());
2232 uno::Reference<uno::XComponentContext> xComponentContext
2233 = ::cppu::defaultBootstrap_InitialComponentContext();
2234 uno::Reference<lang::XMultiServiceFactory> xMSF;
2235 xMSF.set(xComponentContext->getServiceManager(), uno::UNO_QUERY);
2236 if(!xMSF.is())
2237 Application::Abort("Bootstrap failure - no service manager");
2239 ::comphelper::setProcessServiceFactory(xMSF);
2241 catch (const uno::Exception &e)
2243 Application::Abort("Bootstrap exception " + e.Message);
2246 void DeInit() override
2248 framework::getDesktop(::comphelper::getProcessComponentContext())->terminate();
2249 framework::getDesktop(::comphelper::getProcessComponentContext())->disposing();
2251 uno::Reference< lang::XComponent >(
2252 comphelper::getProcessComponentContext(),
2253 uno::UNO_QUERY_THROW)-> dispose();
2254 ::comphelper::setProcessServiceFactory(nullptr);
2260 void vclmain::createApplication()
2262 #ifdef _WIN32
2263 _putenv_s("LIBO_VCL_DEMO", "1");
2264 #else
2265 setenv("LIBO_VCL_DEMO", "1", 0);
2266 #endif
2267 static DemoApp aApp;
2270 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */