Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / workben / vcldemo.cxx
blob74dc6fb5671e836c5d48bea03108d6763d61b9ba
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>
14 #include <config_features.h>
16 #include <math.h>
17 #include <rtl/math.hxx>
18 #include <sal/log.hxx>
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>
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>
38 #include <tools/diagnose_ex.h>
39 #include <tools/urlobj.hxx>
40 #include <tools/stream.hxx>
41 #include <vcl/svapp.hxx>
42 #include <vcl/pngread.hxx>
43 #include <vcl/wrkwin.hxx>
44 #include <vcl/virdev.hxx>
45 #include <vcl/graphicfilter.hxx>
46 #include <vcl/button.hxx>
47 #include <vcl/combobox.hxx>
48 #include <vcl/toolbox.hxx>
49 #include <vcl/pngwrite.hxx>
50 #include <vcl/floatwin.hxx>
51 #include <vcl/bitmapaccess.hxx>
52 #include <vcl/help.hxx>
53 #include <vcl/menu.hxx>
54 #include <vcl/ImageTree.hxx>
55 #include <vcl/BitmapEmbossGreyFilter.hxx>
56 #include <bitmapwriteaccess.hxx>
58 #include <basegfx/numeric/ftools.hxx>
59 #include <basegfx/matrix/b2dhommatrix.hxx>
60 #include <opengl/zone.hxx>
62 // internal headers for OpenGLTests class.
63 #if HAVE_FEATURE_OPENGL
64 #include <salgdi.hxx>
65 #include <salframe.hxx>
66 #include <openglgdiimpl.hxx>
67 #include <opengl/texture.hxx>
68 #include <opengl/framebuffer.hxx>
69 #include <vcl/opengl/OpenGLHelper.hxx>
70 #endif
72 #define FIXME_SELF_INTERSECTING_WORKING 0
73 #define FIXME_BOUNCE_BUTTON 0
74 #define THUMB_REPEAT_FACTOR 10
76 using namespace com::sun::star;
78 namespace {
79 double getTimeNow()
81 TimeValue aValue;
82 osl_getSystemTime(&aValue);
83 return static_cast<double>(aValue.Seconds) * 1000 +
84 static_cast<double>(aValue.Nanosec) / (1000*1000);
89 enum RenderStyle {
90 RENDER_THUMB, // small view <n> to a page
91 RENDER_EXPANDED, // expanded view of this renderer
94 class DemoRenderer
96 Bitmap maIntroBW;
97 BitmapEx maIntro;
99 int mnSegmentsX;
100 int mnSegmentsY;
102 struct RenderContext {
103 RenderStyle meStyle;
104 bool mbVDev;
105 DemoRenderer *mpDemoRenderer;
106 Size maSize;
108 struct RegionRenderer {
109 public:
110 RegionRenderer() :
111 sumTime(0),
112 countTime(0)
114 virtual ~RegionRenderer() {}
115 virtual OUString getName() = 0;
116 virtual sal_uInt16 getAccelerator() = 0;
117 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
118 const RenderContext &rCtx) = 0;
119 // repeating count for profiling (to exceed the poor time resolution on Windows)
120 virtual sal_uInt16 getTestRepeatCount() = 0;
121 #define RENDER_DETAILS(name,key,repeat) \
122 virtual OUString getName() override \
123 { return SAL_STRINGIFY(name); } \
124 virtual sal_uInt16 getAccelerator() override \
125 { return key; } \
126 virtual sal_uInt16 getTestRepeatCount() override \
127 { return repeat; }
129 double sumTime;
130 int countTime;
133 std::vector< RegionRenderer * > maRenderers;
134 sal_Int32 mnSelectedRenderer;
135 sal_Int32 iterCount;
137 void InitRenderers();
139 public:
140 DemoRenderer() : mnSegmentsX(0)
141 , mnSegmentsY(0)
142 , mnSelectedRenderer(-1)
143 , iterCount(0)
144 #if FIXME_BOUNCE_BUTTON
145 , mpButton(NULL)
146 , mpButtonWin(NULL)
147 , mnBounceX(1)
148 , mnBounceY(1)
149 #endif
151 if (!Application::LoadBrandBitmap("intro", maIntro))
152 Application::Abort("Failed to load intro image");
154 maIntroBW = maIntro.GetBitmap();
156 BitmapEx aTmpBmpEx(maIntroBW);
157 BitmapFilter::Filter(aTmpBmpEx, BitmapEmbossGreyFilter(0, 0));
158 maIntroBW = aTmpBmpEx.GetBitmap();
160 InitRenderers();
161 mnSegmentsY = rtl::math::round(std::sqrt(maRenderers.size()), 0,
162 rtl_math_RoundingMode_Down);
163 mnSegmentsX = (maRenderers.size() + mnSegmentsY - 1)/mnSegmentsY;
166 OUString getRendererList();
167 double getAndResetBenchmark(RenderStyle style);
168 void selectRenderer(const OUString &rName);
169 int selectNextRenderer();
170 void setIterCount(sal_Int32 iterCount);
171 sal_Int32 getIterCount() const;
172 void addTime(int i, double t);
174 Size maSize;
175 void SetSizePixel(const Size &rSize) { maSize = rSize; }
176 const Size& GetSizePixel() const { return maSize; }
179 // more of a 'Window' concept - push upwards ?
180 #if FIXME_BOUNCE_BUTTON
181 // Bouncing windows on click ...
182 PushButton *mpButton;
183 FloatingWindow *mpButtonWin;
184 AutoTimer maBounce;
185 int mnBounceX, mnBounceY;
186 DECL_LINK(BounceTimerCb, Timer*, void);
187 #endif
189 bool MouseButtonDown(const MouseEvent& rMEvt);
190 void KeyInput(const KeyEvent& rKEvt);
192 static std::vector<tools::Rectangle> partition(const tools::Rectangle &rRect, int nX, int nY)
194 std::vector<tools::Rectangle> aRegions = partition(rRect.GetSize(), nX, nY);
195 for (auto & region : aRegions)
196 region.Move(rRect.Left(), rRect.Top());
198 return aRegions;
201 static std::vector<tools::Rectangle> partition(const RenderContext &rCtx, int nX, int nY)
203 return partition(rCtx.maSize, nX, nY);
206 static std::vector<tools::Rectangle> partition(Size aSize, int nX, int nY)
208 tools::Rectangle r;
209 std::vector<tools::Rectangle> aRegions;
211 // Make small cleared area for these guys
212 long nBorderSize = std::min(aSize.Height() / 32, aSize.Width() / 32);
213 long nBoxWidth = (aSize.Width() - nBorderSize*(nX+1)) / nX;
214 long nBoxHeight = (aSize.Height() - nBorderSize*(nY+1)) / nY;
215 for (int y = 0; y < nY; y++)
217 for (int x = 0; x < nX; x++)
219 r.SetPos(Point(nBorderSize + (nBorderSize + nBoxWidth) * x,
220 nBorderSize + (nBorderSize + nBoxHeight) * y));
221 r.SetSize(Size(nBoxWidth, nBoxHeight));
222 aRegions.push_back(r);
226 return aRegions;
229 static void clearRects(OutputDevice &rDev, std::vector<tools::Rectangle> &rRects)
231 for (size_t i = 0; i < rRects.size(); i++)
233 // knock up a nice little border
234 rDev.SetLineColor(COL_GRAY);
235 rDev.SetFillColor(COL_LIGHTGRAY);
236 if (i % 2)
238 int nBorderSize = rRects[i].GetWidth() / 5;
239 rDev.DrawRect(rRects[i], nBorderSize, nBorderSize);
241 else
242 rDev.DrawRect(rRects[i]);
246 static void drawBackground(OutputDevice &rDev, const tools::Rectangle& r)
248 rDev.Erase();
249 Gradient aGradient;
250 aGradient.SetStartColor(COL_BLUE);
251 aGradient.SetEndColor(COL_GREEN);
252 aGradient.SetStyle(GradientStyle::Linear);
253 rDev.DrawGradient(r, aGradient);
256 struct DrawLines : public RegionRenderer
258 RENDER_DETAILS(lines,KEY_L,100)
259 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
260 const RenderContext &rCtx) override
262 if (rCtx.meStyle == RENDER_EXPANDED)
264 AntialiasingFlags nOldAA = rDev.GetAntialiasing();
265 rDev.SetAntialiasing(AntialiasingFlags::EnableB2dDraw);
267 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx, 4, 4));
268 DemoRenderer::clearRects(rDev, aRegions);
270 #if 0 // FIXME: get this through to the backend ...
271 double nTransparency[] = {
272 1.0, 1.0, 1.0, 1.0,
273 0.8, 0.8, 0.8, 0.8,
274 0.5, 0.5, 0.5, 0.5,
275 0.1, 0.1, 0.1, 0.1
277 #endif
278 drawing::LineCap const eLineCaps[] = {
279 drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT,
280 drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT,
281 drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT,
282 drawing::LineCap_BUTT, drawing::LineCap_ROUND, drawing::LineCap_SQUARE, drawing::LineCap_BUTT
284 basegfx::B2DLineJoin const eJoins[] = {
285 basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round,
286 basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round,
287 basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round,
288 basegfx::B2DLineJoin::NONE, basegfx::B2DLineJoin::Bevel, basegfx::B2DLineJoin::Miter, basegfx::B2DLineJoin::Round
290 double const aLineWidths[] = {
291 10.0, 15.0, 20.0, 10.0,
292 10.0, 15.0, 20.0, 10.0,
293 10.0, 15.0, 20.0, 10.0,
294 0.1, 1.0, 10.0, 50.0
296 for (size_t i = 0; i < aRegions.size(); i++)
298 // Half of them not-anti-aliased ..
299 if (i >= aRegions.size()/2)
300 rDev.SetAntialiasing(nOldAA);
302 static const struct {
303 double nX, nY;
304 } aPoints[] = {
305 { 0.2, 0.2 }, { 0.8, 0.3 }, { 0.7, 0.8 }
307 rDev.SetLineColor(COL_BLACK);
308 basegfx::B2DPolygon aPoly;
309 tools::Rectangle aSub(aRegions[i]);
310 for (size_t j = 0; j < SAL_N_ELEMENTS(aPoints); j++)
312 aPoly.append(basegfx::B2DPoint(aSub.Left() + aSub.GetWidth() * aPoints[j].nX,
313 aSub.Top() + aSub.GetHeight() * aPoints[j].nY));
315 rDev.DrawPolyLine(aPoly, aLineWidths[i], eJoins[i], eLineCaps[i]);
318 else
320 rDev.SetFillColor(COL_LIGHTRED);
321 rDev.SetLineColor(COL_BLACK);
322 rDev.DrawRect(r);
324 for(long i=0; i<r.GetHeight(); i+=15)
325 rDev.DrawLine(Point(r.Left(), r.Top()+i), Point(r.Right(), r.Bottom()-i));
326 for(long i=0; i<r.GetWidth(); i+=15)
327 rDev.DrawLine(Point(r.Left()+i, r.Bottom()), Point(r.Right()-i, r.Top()));
329 // Should draw a white-line across the middle
330 Color aLastPixel(COL_WHITE);
331 Point aCenter((r.Left() + r.Right())/2 - 4,
332 (r.Top() + r.Bottom())/2 - 4);
333 for(int i=0; i<8; i++)
335 rDev.DrawPixel(aCenter, aLastPixel);
336 aLastPixel = rDev.GetPixel(aCenter);
337 aCenter.Move(1,1);
343 struct DrawText : public RegionRenderer
345 RENDER_DETAILS(text,KEY_T,1)
347 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
348 const RenderContext &rCtx) override
350 if (rCtx.meStyle == RENDER_EXPANDED)
352 std::vector<tools::Rectangle> aToplevelRegions(
353 DemoRenderer::partition(rCtx, 1, 3));
354 std::vector<tools::Rectangle> aSubRegions(
355 DemoRenderer::partition(aToplevelRegions[0], 4, 2));
356 tools::Rectangle aBottom(aToplevelRegions[1].TopLeft(),
357 aToplevelRegions[2].BottomRight());
358 DemoRenderer::clearRects(rDev,aSubRegions);
359 static struct {
360 bool const mbClip;
361 bool const mbArabicText;
362 bool const mbRotate;
363 } const aRenderData[] = {
364 { false, false, false },
365 { false, true, false },
366 { false, true, true },
367 { false, false, true },
368 { true, false, true },
369 { true, true, true },
370 { true, true, false },
371 { true, false, false },
374 size_t i = 0;
375 for (int y = 0; y < 2; y++)
377 for (int x = 0; x < 4; x++)
379 assert(i < SAL_N_ELEMENTS(aRenderData));
380 drawText(rDev, aSubRegions[i], aRenderData[i].mbClip,
381 aRenderData[i].mbArabicText, aRenderData[i].mbRotate);
382 i++;
386 drawComplex(rDev, aBottom);
388 else
390 drawText(rDev, r, false, false, false);
394 static void drawText (OutputDevice &rDev, tools::Rectangle r, bool bClip, bool bArabicText, bool bRotate)
396 rDev.SetClipRegion( vcl::Region(r) );
398 OUString const aLatinText("Click any rect to zoom!!!!");
400 const unsigned char pTextUTF8[] = {
401 0xd9, 0x88, 0xd8, 0xa7, 0xd8, 0xad, 0xd9, 0x90,
402 0xd8, 0xaf, 0xd9, 0x92, 0x20, 0xd8, 0xa5, 0xd8,
403 0xab, 0xd9, 0x8d, 0xd9, 0x86, 0xd9, 0x8a, 0xd9,
404 0x86, 0x20, 0xd8, 0xab, 0xd9, 0x84, 0xd8, 0xa7,
405 0xd8, 0xab, 0xd8, 0xa9, 0xd9, 0x8c, 0x00
407 OUString aArabicText( reinterpret_cast<char const *>(pTextUTF8),
408 SAL_N_ELEMENTS( pTextUTF8 ) - 1,
409 RTL_TEXTENCODING_UTF8 );
411 OUString aText;
413 // To have more text displayed one after the other (overlapping, and in different colours), then
414 // change this value
415 const int nPrintNumCopies=1;
417 if (bArabicText)
418 aText = aArabicText;
419 else
420 aText = aLatinText;
422 std::vector<OUString> aFontNames;
424 static Color const nCols[] = {
425 COL_BLACK, COL_BLUE, COL_GREEN, COL_CYAN, COL_RED, COL_MAGENTA,
426 COL_BROWN, COL_GRAY, COL_LIGHTGRAY, COL_LIGHTBLUE, COL_LIGHTGREEN,
427 COL_LIGHTCYAN, COL_LIGHTRED, COL_LIGHTMAGENTA, COL_YELLOW, COL_WHITE
430 // a few fonts to start with
431 const char *pNames[] = {
432 "Times", "Liberation Sans", "Arial", "Linux Biolinum G", "Linux Libertine Display G"
435 for (size_t i = 0; i < SAL_N_ELEMENTS(pNames); i++)
436 aFontNames.push_back(OUString::createFromAscii(pNames[i]));
438 if (bClip && !bRotate)
440 // only show the first quarter of the text
441 tools::Rectangle aRect( r.TopLeft(), Size( r.GetWidth()/2, r.GetHeight()/2 ) );
442 rDev.SetClipRegion( vcl::Region( aRect ) );
445 for (int i = 1; i < nPrintNumCopies+1; i++)
447 int nFontHeight=0, nFontIndex=0, nFontColorIndex=0;
449 if (nPrintNumCopies == 1)
451 float const nFontMagnitude = 0.25f;
452 // random font size to avoid buffering
453 nFontHeight = 1 + nFontMagnitude * (0.9 + comphelper::rng::uniform_real_distribution(0.0, std::nextafter(0.1, DBL_MAX))) * (r.Bottom() - r.Top());
454 nFontIndex=0;
455 nFontColorIndex=0;
457 else
459 // random font size to avoid buffering
460 nFontHeight = 1 + i * (0.9 + comphelper::rng::uniform_real_distribution(0.0, std::nextafter(0.1, DBL_MAX))) * (r.Top() - r.Bottom()) / nPrintNumCopies;
461 nFontIndex = (i % aFontNames.size());
462 nFontColorIndex=(i % aFontNames.size());
465 rDev.SetTextColor(nCols[nFontColorIndex]);
466 vcl::Font aFont( aFontNames[nFontIndex], Size(0, nFontHeight ));
468 if (bRotate)
470 tools::Rectangle aFontRect = r;
472 int nHeight = r.GetHeight();
474 // move the text to the bottom of the bounding rect before rotating
475 aFontRect.AdjustTop(nHeight/2 );
476 aFontRect.AdjustBottom(nHeight );
478 aFont.SetOrientation(45 * 10); // 45 degrees
480 rDev.SetFont(aFont);
481 rDev.DrawText(aFontRect, aText);
483 if (bClip)
485 tools::Rectangle aClipRect( Point( r.Left(), r.Top() + ( r.GetHeight()/2 ) ) , Size( r.GetWidth()/2, r.GetHeight()/2 ) );
486 rDev.SetClipRegion( vcl::Region( aClipRect ) );
488 else
489 rDev.SetClipRegion( vcl::Region(r) );
491 else
493 rDev.SetFont(aFont);
494 rDev.DrawText(r, aText);
498 rDev.SetClipRegion();
501 static void drawComplex (OutputDevice &rDev, tools::Rectangle r)
503 const unsigned char pInvalid[] = { 0xfe, 0x1f, 0 };
504 const unsigned char pDiacritic1[] = { 0x61, 0xcc, 0x8a, 0xcc, 0x8c, 0 };
505 const unsigned char pDiacritic2[] = { 0x61, 0xcc, 0x88, 0xcc, 0x86, 0 };
506 const unsigned char pDiacritic3[] = { 0x61, 0xcc, 0x8b, 0xcc, 0x87, 0 };
507 const unsigned char pJustification[] = {
508 0x64, 0x20, 0xc3, 0xa1, 0xc3, 0xa9, 0x77, 0xc4, 0x8d,
509 0xc5, 0xa1, 0xc3, 0xbd, 0xc5, 0x99, 0x20, 0xc4, 0x9b, 0
511 const unsigned char pEmojis[] = {
512 0xf0, 0x9f, 0x8d, 0x80, 0xf0, 0x9f, 0x91, 0x98,
513 0xf0, 0x9f, 0x92, 0x8a, 0xf0, 0x9f, 0x92, 0x99,
514 0xf0, 0x9f, 0x92, 0xa4, 0xf0, 0x9f, 0x94, 0x90, 0
516 const unsigned char pThreeBowlG[] = {
517 0xe2, 0x9a, 0x82, 0xe2, 0x99, 0xa8, 0xc4, 0x9e, 0
519 const unsigned char pWavesAndDomino[] = {
520 0xe2, 0x99, 0x92, 0xf0, 0x9f, 0x81, 0xa0,
521 0xf0, 0x9f, 0x82, 0x93, 0
523 const unsigned char pSpadesAndBits[] = {
524 0xf0, 0x9f, 0x82, 0xa1, 0xc2, 0xa2, 0xc2, 0xa2, 0
527 static struct {
528 const char *mpFont;
529 const char *mpString;
530 } const aRuns[] = {
531 #define SET(font,string) { font, reinterpret_cast<const char *>(string) }
532 {"sans", "a"}, // logical font - no 'sans' font.
533 {"opensymbol", "#$%"}, // font fallback - $ is missing.
534 SET("sans", pInvalid), // unicode invalid character
535 // tdf#96266 - stacking diacritics
536 SET("carlito", pDiacritic1),
537 SET("carlito", pDiacritic2),
538 SET("carlito", pDiacritic3),
539 SET("liberation sans", pDiacritic1),
540 SET("liberation sans", pDiacritic2),
541 SET("liberation sans", pDiacritic3),
542 SET("liberation sans", pDiacritic3),
544 // tdf#95222 - justification issue
545 // - FIXME: replicate justification
546 SET("gentium basic", pJustification),
548 // tdf#97319 - Unicode beyond BMP; SMP & Plane 2
549 SET("symbola", pEmojis),
550 SET("symbola", pThreeBowlG),
551 SET("symbola", pWavesAndDomino),
552 SET("symbola", pSpadesAndBits),
555 // Nice clean white background
556 rDev.DrawWallpaper(r, Wallpaper(COL_WHITE));
557 rDev.SetClipRegion(vcl::Region(r));
559 Point aPos(r.Left(), r.Top()+20);
561 long nMaxTextHeight = 0;
562 for (size_t i = 0; i < SAL_N_ELEMENTS(aRuns); ++i)
564 // Legend
565 vcl::Font aIndexFont("sans", Size(0,20));
566 aIndexFont.SetColor( COL_BLACK);
567 tools::Rectangle aTextRect;
568 rDev.SetFont(aIndexFont);
569 OUString aText = OUString::number(i) + ".";
570 rDev.DrawText(aPos, aText);
571 if (rDev.GetTextBoundRect(aTextRect, aText))
572 aPos.Move(aTextRect.GetWidth() + 8, 0);
574 // Text
575 FontWeight aWeights[] = { WEIGHT_NORMAL,
576 WEIGHT_BOLD,
577 WEIGHT_NORMAL };
578 FontItalic const aItalics[] = { ITALIC_NONE,
579 ITALIC_NONE,
580 ITALIC_NORMAL };
581 vcl::Font aFont(OUString::createFromAscii(
582 aRuns[i].mpFont),
583 Size(0,42));
584 aFont.SetColor(COL_BLACK);
585 for (size_t j = 0; j < SAL_N_ELEMENTS(aWeights); ++j)
587 aFont.SetItalic(aItalics[j]);
588 aFont.SetWeight(aWeights[j]);
589 rDev.SetFont(aFont);
591 OUString aString(aRuns[i].mpString,
592 strlen(aRuns[i].mpString),
593 RTL_TEXTENCODING_UTF8);
594 long nNewX = drawStringBox(rDev, aPos, aString,
595 nMaxTextHeight);
597 aPos.setX( nNewX );
599 if (aPos.X() >= r.Right())
601 aPos = Point(r.Left(), aPos.Y() + nMaxTextHeight + 15);
602 nMaxTextHeight = 0;
603 if(j>0)
604 j--; // re-render the last point.
606 if (aPos.Y() > r.Bottom())
607 break;
609 if (aPos.Y() > r.Bottom())
610 break;
613 rDev.SetClipRegion();
615 // render text, bbox, DX arrays etc.
616 static long drawStringBox(OutputDevice &rDev, Point aPos,
617 const OUString &aText,
618 long &nMaxTextHeight)
620 rDev.Push();
622 tools::Rectangle aTextRect;
624 rDev.DrawText(aPos,aText);
626 if (rDev.GetTextBoundRect(aTextRect, aText))
628 aTextRect.Move(aPos.X(), aPos.Y());
629 rDev.SetFillColor();
630 rDev.SetLineColor(COL_BLACK);
631 rDev.DrawRect(aTextRect);
632 if (aTextRect.GetHeight() > nMaxTextHeight)
633 nMaxTextHeight = aTextRect.GetHeight();
634 // This should intersect with the text
635 tools::Rectangle aInnerRect(
636 aTextRect.Left()+1, aTextRect.Top()+1,
637 aTextRect.Right()-1, aTextRect.Bottom()-1);
638 rDev.SetLineColor(COL_WHITE);
639 rDev.SetRasterOp(RasterOp::Xor);
640 rDev.DrawRect(aInnerRect);
641 rDev.SetRasterOp(RasterOp::OverPaint);
644 // DX array rendering
645 std::unique_ptr<long[]> pItems(new long[aText.getLength()+10]);
646 rDev.GetTextArray(aText, pItems.get());
647 for (long j = 0; j < aText.getLength(); ++j)
649 Point aTop = aTextRect.TopLeft();
650 Point aBottom = aTop;
651 aTop.Move(pItems[j], 0);
652 aBottom.Move(pItems[j], aTextRect.GetHeight());
653 rDev.SetLineColor(COL_RED);
654 rDev.SetRasterOp(RasterOp::Xor);
655 rDev.DrawLine(aTop,aBottom);
656 rDev.SetRasterOp(RasterOp::OverPaint);
659 aPos.Move(aTextRect.GetWidth() + 16, 0);
661 rDev.Pop();
662 return aPos.X();
666 struct DrawCheckered : public RegionRenderer
668 RENDER_DETAILS(checks,KEY_C,20)
669 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
670 const RenderContext &rCtx) override
672 if (rCtx.meStyle == RENDER_EXPANDED)
674 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx, 2, 2));
675 for (size_t i = 0; i < aRegions.size(); i++)
677 vcl::Region aRegion;
678 tools::Rectangle aSub(aRegions[i]);
679 tools::Rectangle aSmaller(aSub);
680 aSmaller.Move(10,10);
681 aSmaller.setWidth(aSmaller.getWidth()-20);
682 aSmaller.setHeight(aSmaller.getHeight()-24);
683 switch (i) {
684 case 0:
685 aRegion = vcl::Region(aSub);
686 break;
687 case 1:
688 aRegion = vcl::Region(aSmaller);
689 aRegion.XOr(aSub);
690 break;
691 case 2:
693 tools::Polygon aPoly(aSub);
694 aPoly.Rotate(aSub.Center(), 450);
695 aPoly.Clip(aSmaller);
696 aRegion = vcl::Region(aPoly);
697 break;
699 case 3:
701 tools::PolyPolygon aPolyPoly;
702 sal_Int32 nTW = aSub.GetWidth()/6;
703 sal_Int32 nTH = aSub.GetHeight()/6;
704 tools::Rectangle aTiny(Point(4, 4), Size(nTW*2, nTH*2));
705 aPolyPoly.Insert( tools::Polygon(aTiny));
706 aTiny.Move(nTW*3, nTH*3);
707 aPolyPoly.Insert( tools::Polygon(aTiny));
708 aTiny.Move(nTW, nTH);
709 aPolyPoly.Insert( tools::Polygon(aTiny));
711 aRegion = vcl::Region(aPolyPoly);
712 break;
714 } // switch
715 rDev.SetClipRegion(aRegion);
716 rDev.DrawCheckered(aSub.TopLeft(), aSub.GetSize());
717 rDev.SetClipRegion();
720 else
722 rDev.DrawCheckered(r.TopLeft(), r.GetSize());
727 struct DrawPoly : public RegionRenderer
729 RENDER_DETAILS(poly,KEY_P,20)
730 DrawCheckered maCheckered;
731 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
732 const RenderContext &rCtx) override
734 maCheckered.RenderRegion(rDev, r, rCtx);
736 long nDx = r.GetWidth()/20;
737 long nDy = r.GetHeight()/20;
738 tools::Rectangle aShrunk(r);
739 aShrunk.Move(nDx, nDy);
740 aShrunk.SetSize(Size(r.GetWidth()-nDx*2,
741 r.GetHeight()-nDy*2));
742 tools::Polygon aPoly(aShrunk);
743 tools::PolyPolygon aPPoly(aPoly);
744 rDev.SetLineColor(COL_RED);
745 rDev.SetFillColor(COL_RED);
746 // This hits the optional 'drawPolyPolygon' code-path
747 rDev.DrawTransparent(aPPoly, 64);
751 struct DrawEllipse : public RegionRenderer
753 RENDER_DETAILS(ellipse,KEY_E,500)
754 static void doInvert(OutputDevice &rDev, const tools::Rectangle &r,
755 InvertFlags nFlags)
757 rDev.Invert(r, nFlags);
758 if (r.GetWidth() > 10 && r.GetHeight() > 10)
760 tools::Rectangle aSmall(r.Center()-Point(4,4), Size(8,8));
761 rDev.Invert(aSmall,nFlags);
764 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
765 const RenderContext &rCtx) override
767 rDev.SetLineColor(COL_RED);
768 rDev.SetFillColor(COL_GREEN);
769 rDev.DrawEllipse(r);
771 if (rCtx.meStyle == RENDER_EXPANDED)
773 auto aRegions = partition(rCtx, 2, 2);
774 doInvert(rDev, aRegions[0], InvertFlags::NONE);
775 rDev.DrawText(aRegions[0], "InvertFlags::NONE");
776 doInvert(rDev, aRegions[1], InvertFlags::N50);
777 rDev.DrawText(aRegions[1], "InvertFlags::N50");
778 doInvert(rDev, aRegions[3], InvertFlags::TrackFrame);
779 rDev.DrawText(aRegions[3], "InvertFlags::TrackFrame");
784 struct DrawGradient : public RegionRenderer
786 RENDER_DETAILS(gradient,KEY_G,50)
787 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
788 const RenderContext &rCtx) override
790 if (rCtx.meStyle == RENDER_EXPANDED)
792 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx,5, 4));
793 static Color const nStartCols[] = {
794 COL_RED, COL_RED, COL_RED, COL_GREEN, COL_GREEN,
795 COL_BLUE, COL_BLUE, COL_BLUE, COL_CYAN, COL_CYAN,
796 COL_BLACK, COL_LIGHTGRAY, COL_WHITE, COL_BLUE, COL_CYAN,
797 COL_WHITE, COL_WHITE, COL_WHITE, COL_BLACK, COL_BLACK
799 static Color const nEndCols[] = {
800 COL_WHITE, COL_WHITE, COL_WHITE, COL_BLACK, COL_BLACK,
801 COL_RED, COL_RED, COL_RED, COL_GREEN, COL_GREEN,
802 COL_GRAY, COL_GRAY, COL_LIGHTGRAY, COL_LIGHTBLUE, COL_LIGHTCYAN,
803 COL_BLUE, COL_BLUE, COL_BLUE, COL_CYAN, COL_CYAN
805 GradientStyle eStyles[] = {
806 GradientStyle::Linear, GradientStyle::Axial, GradientStyle::Radial, GradientStyle::Elliptical, GradientStyle::Square,
807 GradientStyle::Rect, GradientStyle::FORCE_EQUAL_SIZE, GradientStyle::Linear, GradientStyle::Radial, GradientStyle::Linear,
808 GradientStyle::Linear, GradientStyle::Axial, GradientStyle::Radial, GradientStyle::Elliptical, GradientStyle::Square,
809 GradientStyle::Rect, GradientStyle::FORCE_EQUAL_SIZE, GradientStyle::Linear, GradientStyle::Radial, GradientStyle::Linear
811 sal_uInt16 nAngles[] = {
812 0, 0, 0, 0, 0,
813 15, 30, 45, 60, 75,
814 90, 120, 135, 160, 180,
815 0, 0, 0, 0, 0
817 sal_uInt16 nBorders[] = {
818 0, 0, 0, 0, 0,
819 1, 10, 100, 10, 1,
820 0, 0, 0, 0, 0,
821 1, 10, 20, 10, 1,
822 0, 0, 0, 0, 0
824 DemoRenderer::clearRects(rDev, aRegions);
825 assert(aRegions.size() <= SAL_N_ELEMENTS(nStartCols));
826 assert(aRegions.size() <= SAL_N_ELEMENTS(nEndCols));
827 assert(aRegions.size() <= SAL_N_ELEMENTS(eStyles));
828 assert(aRegions.size() <= SAL_N_ELEMENTS(nAngles));
829 assert(aRegions.size() <= SAL_N_ELEMENTS(nBorders));
830 for (size_t i = 0; i < aRegions.size(); i++)
832 tools::Rectangle aSub = aRegions[i];
833 Gradient aGradient;
834 aGradient.SetStartColor(nStartCols[i]);
835 aGradient.SetEndColor(nEndCols[i]);
836 aGradient.SetStyle(eStyles[i]);
837 aGradient.SetAngle(nAngles[i]);
838 aGradient.SetBorder(nBorders[i]);
839 rDev.DrawGradient(aSub, aGradient);
842 else
844 Gradient aGradient;
845 aGradient.SetStartColor(COL_YELLOW);
846 aGradient.SetEndColor(COL_RED);
847 aGradient.SetStyle(GradientStyle::Rect);
848 aGradient.SetBorder(r.GetSize().Width()/20);
849 rDev.DrawGradient(r, aGradient);
854 struct DrawBitmap : public RegionRenderer
856 RENDER_DETAILS(bitmap,KEY_B,10)
858 // Simulate Page Borders rendering - which ultimately should
859 // be done with a shader / gradient
860 static void SimulateBorderStretch(OutputDevice &rDev, const tools::Rectangle& r)
862 BitmapEx aPageShadowMask("sw/res/page-shadow-mask.png");
864 BitmapEx aRight(aPageShadowMask);
865 sal_Int32 nSlice = (aPageShadowMask.GetSizePixel().Width() - 3) / 4;
866 // a width x 1 slice
867 aRight.Crop(tools::Rectangle(Point((nSlice * 3) + 3, (nSlice * 2) + 1),
868 Size(nSlice, 1)));
869 AlphaMask aAlphaMask(aRight.GetBitmap());
870 Bitmap aBlockColor(aAlphaMask.GetSizePixel(), 24);
871 aBlockColor.Erase(COL_RED);
872 BitmapEx aShadowStretch(aBlockColor, aAlphaMask);
874 Point aRenderPt(r.TopLeft());
876 long aSizes[] = { 200, 100, 200, 100, 50, 5, 2 };
878 // and yes - we really do this in the page border rendering code ...
879 for (size_t i = 0; i < SAL_N_ELEMENTS(aSizes); i++)
881 aShadowStretch.Scale(Size(aShadowStretch.GetSizePixel().Width(), aSizes[i]),
882 BmpScaleFlag::Fast);
884 rDev.DrawBitmapEx(aRenderPt, aShadowStretch);
885 aRenderPt.Move(aShadowStretch.GetSizePixel().Width() + 4, 0);
888 AlphaMask aWholeMask(aPageShadowMask.GetBitmap());
889 aBlockColor = Bitmap(aPageShadowMask.GetSizePixel(), 24);
890 aBlockColor.Erase(COL_GREEN);
891 BitmapEx aWhole(aBlockColor, aWholeMask);
893 aRenderPt = r.Center();
894 aRenderPt.Move(nSlice+1, 0);
896 // An offset background for alpha rendering
897 rDev.SetFillColor(COL_BLUE);
898 tools::Rectangle aSurround(r.Center(), aPageShadowMask.GetSizePixel());
899 rDev.DrawRect(aSurround);
900 rDev.DrawBitmapEx(aRenderPt, aWhole);
903 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
904 const RenderContext &rCtx) override
906 Bitmap aBitmap(rCtx.mpDemoRenderer->maIntroBW);
907 aBitmap.Scale(r.GetSize(), BmpScaleFlag::BestQuality);
908 rDev.DrawBitmap(r.TopLeft(), aBitmap);
910 SimulateBorderStretch(rDev, r);
914 struct DrawBitmapEx : public RegionRenderer
916 RENDER_DETAILS(bitmapex,KEY_X,2)
917 DrawCheckered maCheckered;
918 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
919 const RenderContext &rCtx) override
921 maCheckered.RenderRegion(rDev, r, rCtx);
923 BitmapEx aBitmap(rCtx.mpDemoRenderer->maIntro);
924 aBitmap.Scale(r.GetSize(), BmpScaleFlag::BestQuality);
925 AlphaMask aSemiTransp(aBitmap.GetSizePixel());
926 aSemiTransp.Erase(64);
927 rDev.DrawBitmapEx(r.TopLeft(), BitmapEx(aBitmap.GetBitmap(),
928 aSemiTransp));
932 struct DrawPolyPolygons : public RegionRenderer
934 RENDER_DETAILS(polypoly,KEY_N,100)
935 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
936 const RenderContext &) override
938 static struct {
939 double nX, nY;
940 } const aPoints[] = { { 0.1, 0.1 }, { 0.9, 0.9 },
941 #if FIXME_SELF_INTERSECTING_WORKING
942 { 0.9, 0.1 }, { 0.1, 0.9 },
943 { 0.1, 0.1 }
944 #else
945 { 0.1, 0.9 }, { 0.5, 0.5 },
946 { 0.9, 0.1 }, { 0.1, 0.1 }
947 #endif
950 tools::PolyPolygon aPolyPoly;
951 // Render 4x polygons & aggregate into another PolyPolygon
952 for (int x = 0; x < 2; x++)
954 for (int y = 0; y < 2; y++)
956 tools::Rectangle aSubRect(r);
957 aSubRect.Move(x * r.GetWidth()/3, y * r.GetHeight()/3);
958 aSubRect.SetSize(Size(r.GetWidth()/2, r.GetHeight()/4));
959 tools::Polygon aPoly(SAL_N_ELEMENTS(aPoints));
960 for (size_t v = 0; v < SAL_N_ELEMENTS(aPoints); v++)
962 aPoly.SetPoint(Point(aSubRect.Left() +
963 aSubRect.GetWidth() * aPoints[v].nX,
964 aSubRect.Top() +
965 aSubRect.GetHeight() * aPoints[v].nY),
968 rDev.SetLineColor(COL_YELLOW);
969 rDev.SetFillColor(COL_BLACK);
970 rDev.DrawPolygon(aPoly);
972 // now move and add to the polypolygon
973 aPoly.Move(0, r.GetHeight()/2);
974 aPolyPoly.Insert(aPoly);
977 rDev.SetLineColor(COL_LIGHTRED);
978 rDev.SetFillColor(COL_GREEN);
979 rDev.DrawTransparent(aPolyPoly, 50);
983 struct DrawClipped : public RegionRenderer
985 RENDER_DETAILS(clip,KEY_D,10)
986 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
987 const RenderContext &) override
989 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(r, 2, 2));
990 const int nLimits[] = { 4, -100 };
991 for (int i = 0; i < 2; ++i)
993 sal_uInt16 nHue = 0;
994 rDev.Push(PushFlags::CLIPREGION);
995 tools::Rectangle aOuter = aRegions[i];
996 tools::Rectangle aInner = aOuter;
997 while (aInner.GetWidth() > nLimits[i] && aInner.GetHeight() > nLimits[i])
999 aInner.expand(-1);
1000 rDev.SetClipRegion(vcl::Region(aInner));
1001 rDev.SetFillColor(Color::HSBtoRGB(nHue, 75, 100));
1002 nHue = (nHue + 97) % 360;
1003 rDev.DrawRect(aOuter);
1005 rDev.Pop();
1009 sal_uInt16 nHue = 0;
1010 tools::Rectangle aOuter = aRegions[2];
1011 std::vector<tools::Rectangle> aPieces(DemoRenderer::partition(aOuter, 2, 2));
1012 for (int j = 0; j < std::min(aOuter.GetWidth(), aOuter.GetHeight())/5; ++j)
1014 rDev.Push(PushFlags::CLIPREGION);
1016 vcl::Region aClipRegion;
1017 for (int i = 0; i < 4; ++i)
1019 aPieces[i].expand(-1);
1020 aPieces[i].Move(2 - i/2, 2 - i/2);
1021 aClipRegion.Union(aPieces[i]);
1023 assert (aClipRegion.getRegionBand());
1024 rDev.SetClipRegion(aClipRegion);
1025 rDev.SetFillColor(Color::HSBtoRGB(nHue, 75, 75));
1026 nHue = (nHue + 97) % 360;
1027 rDev.DrawRect(aOuter);
1029 rDev.Pop();
1034 sal_uInt16 nHue = 0;
1035 tools::Rectangle aOuter = aRegions[3];
1036 std::vector<tools::Rectangle> aPieces(DemoRenderer::partition(aOuter, 2, 2));
1037 bool bDone = false;
1038 for (int j = 0; !bDone; ++j)
1040 rDev.Push(PushFlags::CLIPREGION);
1042 for (int i = 0; i < 4; ++i)
1044 vcl::Region aClipRegion;
1045 tools::Polygon aPoly;
1046 switch (i) {
1047 case 3:
1048 case 0: // 45degree rectangle.
1049 aPoly = tools::Polygon(aPieces[i]);
1050 aPoly.Rotate(aPieces[i].Center(), 450);
1051 break;
1052 case 1: // arc
1053 aPoly = tools::Polygon(aPieces[i],
1054 aPieces[i].TopLeft(),
1055 aPieces[i].BottomRight());
1056 break;
1057 case 2:
1058 aPoly = tools::Polygon(aPieces[i],
1059 aPieces[i].GetWidth()/5,
1060 aPieces[i].GetHeight()/5);
1061 aPoly.Rotate(aPieces[i].Center(), 450);
1062 break;
1064 aClipRegion = vcl::Region(aPoly);
1065 aPieces[i].expand(-1);
1066 aPieces[i].Move(2 - i/2, 2 - i/2);
1068 bDone = aPieces[i].GetWidth() < 4 ||
1069 aPieces[i].GetHeight() < 4;
1071 if (!bDone)
1073 assert (!aClipRegion.getRegionBand());
1075 rDev.SetClipRegion(aClipRegion);
1076 rDev.SetFillColor(Color::HSBtoRGB(nHue, 50, 75));
1077 nHue = (nHue + 97) % 360;
1078 rDev.DrawRect(aOuter);
1082 rDev.Pop();
1088 struct DrawToVirtualDevice : public RegionRenderer
1090 RENDER_DETAILS(vdev,KEY_V,1)
1091 enum RenderType {
1092 RENDER_AS_BITMAP,
1093 RENDER_AS_OUTDEV,
1094 RENDER_AS_BITMAPEX,
1095 RENDER_AS_ALPHA_OUTDEV
1098 static void SizeAndRender(OutputDevice &rDev, const tools::Rectangle& r, RenderType eType,
1099 const RenderContext &rCtx)
1101 ScopedVclPtr<VirtualDevice> pNested;
1103 if (static_cast<int>(eType) < RENDER_AS_BITMAPEX)
1104 pNested = VclPtr<VirtualDevice>::Create(rDev).get();
1105 else
1106 pNested = VclPtr<VirtualDevice>::Create(rDev,DeviceFormat::DEFAULT,DeviceFormat::DEFAULT).get();
1108 pNested->SetOutputSizePixel(r.GetSize());
1109 tools::Rectangle aWhole(Point(0,0), r.GetSize());
1111 // mini me
1112 rCtx.mpDemoRenderer->drawToDevice(*pNested, r.GetSize(), true);
1114 if (eType == RENDER_AS_BITMAP)
1116 Bitmap aBitmap(pNested->GetBitmap(Point(0,0),aWhole.GetSize()));
1117 rDev.DrawBitmap(r.TopLeft(), aBitmap);
1119 else if (eType == RENDER_AS_BITMAPEX)
1121 BitmapEx aBitmapEx(pNested->GetBitmapEx(Point(0,0),aWhole.GetSize()));
1122 rDev.DrawBitmapEx(r.TopLeft(), aBitmapEx);
1124 else if (eType == RENDER_AS_OUTDEV ||
1125 eType == RENDER_AS_ALPHA_OUTDEV)
1127 rDev.DrawOutDev(r.TopLeft(), r.GetSize(),
1128 aWhole.TopLeft(), aWhole.GetSize(),
1129 *pNested);
1132 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1133 const RenderContext &rCtx) override
1135 // avoid infinite recursion
1136 if (rCtx.mbVDev)
1137 return;
1139 if (rCtx.meStyle == RENDER_EXPANDED)
1141 std::vector<tools::Rectangle> aRegions(DemoRenderer::partition(rCtx,2, 2));
1142 DemoRenderer::clearRects(rDev, aRegions);
1144 RenderType const eRenderTypes[] { RENDER_AS_BITMAP, RENDER_AS_OUTDEV,
1145 RENDER_AS_BITMAPEX, RENDER_AS_ALPHA_OUTDEV };
1146 for (size_t i = 0; i < aRegions.size(); i++)
1147 SizeAndRender(rDev, aRegions[i], eRenderTypes[i], rCtx);
1149 else
1150 SizeAndRender(rDev, r, RENDER_AS_BITMAP, rCtx);
1154 struct DrawXOR : public RegionRenderer
1156 RENDER_DETAILS(xor,KEY_X,1)
1158 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1159 const RenderContext &rCtx) override
1161 // avoid infinite recursion
1162 if (rCtx.mbVDev)
1163 return;
1165 rDev.Push();
1167 AntialiasingFlags nFlags = rDev.GetAntialiasing();
1168 rDev.SetAntialiasing(nFlags & ~AntialiasingFlags::EnableB2dDraw);
1169 rDev.SetRasterOp( RasterOp::Xor );
1171 rCtx.mpDemoRenderer->drawThumbs(rDev, r, true);
1173 rDev.Pop();
1177 struct DrawIcons : public RegionRenderer
1179 RENDER_DETAILS(icons,KEY_I,1)
1181 std::vector<OUString> maIconNames;
1182 std::vector<BitmapEx> maIcons;
1183 bool bHasLoadedAll;
1184 DrawIcons() : bHasLoadedAll(false)
1186 // a few icons to start with
1187 const char *pNames[] = {
1188 "cmd/lc_openurl.png",
1189 "cmd/lc_newdoc.png",
1190 "cmd/lc_choosemacro.png",
1191 "cmd/lc_save.png",
1192 "cmd/lc_saveas.png",
1193 "cmd/lc_importdialog.png",
1194 "cmd/lc_sendmail.png",
1195 "cmd/lc_editdoc.png",
1196 "cmd/lc_print.png",
1197 "cmd/lc_combobox.png",
1198 "cmd/lc_insertformcombo.png",
1199 "cmd/lc_printpreview.png",
1200 "cmd/lc_cut.png",
1201 "cmd/lc_copy.png",
1202 "cmd/lc_paste.png",
1203 "cmd/sc_autopilotmenu.png",
1204 "cmd/lc_formatpaintbrush.png",
1205 "cmd/lc_undo.png",
1206 "cmd/lc_redo.png",
1207 "cmd/lc_marks.png",
1208 "cmd/lc_fieldnames.png",
1209 "cmd/lc_hyperlinkdialog.png",
1210 "cmd/lc_basicshapes.rectangle.png",
1211 "cmd/lc_basicshapes.round-rectangle.png"
1213 for (size_t i = 0; i < SAL_N_ELEMENTS(pNames); i++)
1215 maIconNames.push_back(OUString::createFromAscii(pNames[i]));
1216 maIcons.emplace_back(maIconNames[i]);
1220 void LoadAllImages()
1222 if (bHasLoadedAll)
1223 return;
1224 bHasLoadedAll = true;
1226 css::uno::Reference<css::container::XNameAccess> xRef(ImageTree::get().getNameAccess());
1227 const css::uno::Sequence< OUString > aAllIcons = xRef->getElementNames();
1229 for (const auto& rIcon : aAllIcons)
1231 if (rIcon.endsWithIgnoreAsciiCase("svg"))
1232 continue; // too slow to load.
1233 maIconNames.push_back(rIcon);
1234 maIcons.emplace_back(rIcon);
1238 void doDrawIcons(OutputDevice &rDev, tools::Rectangle r, bool bExpanded)
1240 long nMaxH = 0;
1241 Point p(r.LeftCenter());
1242 size_t nToRender = maIcons.size();
1244 if (!bExpanded && maIcons.size() > 64)
1245 nToRender = 64;
1246 for (size_t i = 0; i < nToRender; i++)
1248 Size aSize(maIcons[i].GetSizePixel());
1249 // sAL_DEBUG("Draw icon '" << maIconNames[i] << "'");
1251 if (!(i % 4))
1252 rDev.DrawBitmapEx(p, maIcons[i]);
1253 else
1255 basegfx::B2DHomMatrix aTransform;
1256 aTransform.scale(aSize.Width(), aSize.Height());
1257 switch (i % 4)
1259 case 2:
1260 aTransform.shearX(static_cast<double>((i >> 2) % 8) / 8);
1261 aTransform.shearY(static_cast<double>((i >> 4) % 8) / 8);
1262 break;
1263 case 3:
1264 aTransform.translate(-aSize.Width()/2, -aSize.Height()/2);
1265 aTransform.rotate(i);
1266 if (i & 0x100)
1268 aTransform.shearX(static_cast<double>((i >> 2) % 8) / 8);
1269 aTransform.shearY(static_cast<double>((i >> 4) % 8) / 8);
1271 aTransform.translate(aSize.Width()/2, aSize.Height()/2);
1272 break;
1273 default:
1274 aTransform.translate(-aSize.Width()/2, -aSize.Height()/2);
1275 aTransform.rotate(2 * F_2PI * i / nToRender);
1276 aTransform.translate(aSize.Width()/2, aSize.Height()/2);
1277 break;
1279 aTransform.translate(p.X(), p.Y());
1280 rDev.DrawTransformedBitmapEx(aTransform, maIcons[i]);
1283 // next position
1284 p.Move(aSize.Width(), 0);
1285 if (aSize.Height() > nMaxH)
1286 nMaxH = aSize.Height();
1287 if (p.X() >= r.Right()) // wrap to next line
1289 p = Point(r.Left(), p.Y() + nMaxH);
1290 nMaxH = 0;
1292 if (p.Y() >= r.Bottom()) // re-start at middle
1293 p = r.LeftCenter();
1297 static BitmapEx AlphaRecovery(OutputDevice &rDev, Point aPt, BitmapEx const &aSrc)
1299 // Compositing onto 2x colors beyond our control
1300 ScopedVclPtrInstance< VirtualDevice > aWhite;
1301 ScopedVclPtrInstance< VirtualDevice > aBlack;
1302 aWhite->SetOutputSizePixel(aSrc.GetSizePixel());
1303 aWhite->SetBackground(Wallpaper(COL_WHITE));
1304 aWhite->Erase();
1305 aBlack->SetOutputSizePixel(aSrc.GetSizePixel());
1306 aBlack->SetBackground(Wallpaper(COL_BLACK));
1307 aBlack->Erase();
1308 aWhite->DrawBitmapEx(Point(), aSrc);
1309 aBlack->DrawBitmapEx(Point(), aSrc);
1311 // Now recover that alpha...
1312 Bitmap aWhiteBmp = aWhite->GetBitmap(Point(),aSrc.GetSizePixel());
1313 Bitmap aBlackBmp = aBlack->GetBitmap(Point(),aSrc.GetSizePixel());
1314 AlphaMask aMask(aSrc.GetSizePixel());
1315 Bitmap aRecovered(aSrc.GetSizePixel(), 24);
1317 AlphaScopedWriteAccess pMaskAcc(aMask);
1318 BitmapScopedWriteAccess pRecAcc(aRecovered);
1319 Bitmap::ScopedReadAccess pAccW(aWhiteBmp); // a * pix + (1-a)
1320 Bitmap::ScopedReadAccess pAccB(aBlackBmp); // a * pix + 0
1321 int nSizeX = aSrc.GetSizePixel().Width();
1322 int nSizeY = aSrc.GetSizePixel().Height();
1323 for (int y = 0; y < nSizeY; y++)
1325 Scanline pScanlineMask = pMaskAcc->GetScanline( y );
1326 Scanline pScanlineRec = pRecAcc->GetScanline( y );
1327 Scanline pScanlineW = pAccW->GetScanline( y );
1328 Scanline pScanlineB = pAccB->GetScanline( y );
1329 for (int x = 0; x < nSizeX; x++)
1331 BitmapColor aColW = pAccW->GetPixelFromData(pScanlineW,x);
1332 BitmapColor aColB = pAccB->GetPixelFromData(pScanlineB,x);
1333 long nAR = static_cast<long>(aColW.GetRed() - aColB.GetRed()); // (1-a)
1334 long nAG = static_cast<long>(aColW.GetGreen() - aColB.GetGreen()); // (1-a)
1335 long nAB = static_cast<long>(aColW.GetBlue() - aColB.GetBlue()); // (1-a)
1337 #define CLAMP(a,b,c) (((a)<=(b))?(b):(((a)>=(c))?(c):(a)))
1339 // we get the most precision from the largest delta
1340 long nInverseAlpha = std::max(nAR, std::max(nAG, nAB)); // (1-a)
1341 nInverseAlpha = CLAMP(nInverseAlpha, 0, 255);
1342 long nAlpha = 255 - nInverseAlpha;
1344 pMaskAcc->SetPixelOnData(pScanlineMask,x,BitmapColor(static_cast<sal_Int8>(CLAMP(nInverseAlpha,0,255))));
1345 // now recover the pixels
1346 long nR = (aColW.GetRed() + aColB.GetRed() - nInverseAlpha) * 128;
1347 long nG = (aColW.GetGreen() + aColB.GetGreen() - nInverseAlpha) * 128;
1348 long nB = (aColW.GetBlue() + aColB.GetBlue() - nInverseAlpha) * 128;
1349 if (nAlpha == 0)
1350 { // doesn't matter what's behind transparency
1351 nR = nG = nB = 0;
1353 else
1355 nR /= nAlpha; nG /= nAlpha; nB /= nAlpha;
1357 pRecAcc->SetPixelOnData(pScanlineRec,x,BitmapColor(
1358 static_cast<sal_uInt8>(CLAMP(nR,0,255)),
1359 static_cast<sal_uInt8>(CLAMP(nG,0,255)),
1360 static_cast<sal_uInt8>(CLAMP(nB,0,255))));
1361 #undef CLAMP
1365 rDev.DrawBitmap(aPt, aWhiteBmp);
1366 aPt.Move(aSrc.GetSizePixel().Width(), 0);
1367 rDev.DrawBitmap(aPt, aBlackBmp);
1368 aPt.Move(aSrc.GetSizePixel().Width(), 0);
1369 rDev.DrawBitmap(aPt, aRecovered);
1370 aPt.Move(aSrc.GetSizePixel().Width(), 0);
1371 rDev.DrawBitmap(aPt, aMask.GetBitmap());
1372 aPt.Move(aSrc.GetSizePixel().Width(), 0);
1374 return BitmapEx(aRecovered, aMask);
1377 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1378 const RenderContext &rCtx) override
1380 if (rCtx.meStyle == RENDER_EXPANDED)
1382 LoadAllImages();
1384 Point aLocation(0,maIcons[0].GetSizePixel().Height() + 8);
1385 for (size_t i = 0; i < maIcons.size(); i++)
1387 BitmapEx aSrc = maIcons[i];
1389 // original above
1390 Point aAbove(aLocation);
1391 aAbove.Move(0,-aSrc.GetSizePixel().Height() - 4);
1392 rDev.DrawBitmapEx(aAbove, aSrc);
1393 aAbove.Move(aSrc.GetSizePixel().Width(),0);
1394 aAbove.Move(aSrc.GetSizePixel().Width(),0);
1395 rDev.DrawBitmap(aAbove, aSrc.GetBitmap());
1396 aAbove.Move(aSrc.GetSizePixel().Width(),0);
1397 rDev.DrawBitmap(aAbove, aSrc.GetMask());
1399 // intermediates middle
1400 BitmapEx aResult = AlphaRecovery(rDev, aLocation, aSrc);
1402 // result below
1403 Point aBelow(aLocation);
1404 aBelow.Move(0,aResult.GetSizePixel().Height());
1405 rDev.DrawBitmapEx(aBelow, aResult);
1407 // mini convert test.
1408 aBelow.Move(aResult.GetSizePixel().Width()+4,0);
1409 rDev.DrawBitmapEx(aBelow, aResult);
1411 Bitmap aGrey = aSrc.GetBitmap();
1412 aGrey.Convert(BmpConversion::N8BitGreys);
1413 rDev.DrawBitmap(aBelow, aGrey);
1415 aBelow.Move(aGrey.GetSizePixel().Width(),0);
1416 BitmapEx aGreyMask(aSrc.GetBitmap(),
1417 AlphaMask(aSrc.GetMask()));
1418 rDev.DrawBitmapEx(aBelow, aGreyMask);
1420 aLocation.Move(aSrc.GetSizePixel().Width()*6,0);
1421 if (aLocation.X() > r.Right())
1422 aLocation = Point(0,aLocation.Y()+aSrc.GetSizePixel().Height()*3+4);
1425 // now go crazy with random foo
1426 doDrawIcons(rDev, r, true);
1428 else
1430 doDrawIcons(rDev, r, false);
1435 struct FetchDrawBitmap : public RegionRenderer
1437 RENDER_DETAILS(fetchdraw,KEY_F,50)
1438 virtual void RenderRegion(OutputDevice &rDev, tools::Rectangle r,
1439 const RenderContext &) override
1441 Bitmap aBitmap(rDev.GetBitmap(Point(0,0),rDev.GetOutputSizePixel()));
1442 aBitmap.Scale(r.GetSize(), BmpScaleFlag::BestQuality);
1443 rDev.DrawBitmap(r.TopLeft(), aBitmap);
1447 void drawThumbs(vcl::RenderContext& rDev, tools::Rectangle aRect, bool bVDev)
1449 RenderContext aCtx;
1450 aCtx.meStyle = RENDER_THUMB;
1451 aCtx.mbVDev = bVDev;
1452 aCtx.mpDemoRenderer = this;
1453 aCtx.maSize = aRect.GetSize();
1454 std::vector<tools::Rectangle> aRegions(partition(aRect, mnSegmentsX, mnSegmentsY));
1455 DemoRenderer::clearRects(rDev, aRegions);
1456 for (size_t i = 0; i < maRenderers.size(); i++)
1458 RegionRenderer * r = maRenderers[i];
1460 rDev.SetClipRegion( vcl::Region( aRegions[i] ) );
1462 // profiling?
1463 if (getIterCount() > 0)
1465 if (!bVDev)
1467 double nStartTime = getTimeNow();
1468 for (int j = 0; j < r->getTestRepeatCount() * THUMB_REPEAT_FACTOR; j++)
1469 r->RenderRegion(rDev, aRegions[i], aCtx);
1470 addTime(i, (getTimeNow() - nStartTime) / THUMB_REPEAT_FACTOR);
1471 } else
1472 for (int j = 0; j < r->getTestRepeatCount(); j++)
1473 r->RenderRegion(rDev, aRegions[i], aCtx);
1475 else
1476 r->RenderRegion(rDev, aRegions[i], aCtx);
1478 rDev.SetClipRegion();
1482 void drawToDevice(vcl::RenderContext& rDev, Size aSize, bool bVDev)
1484 RenderContext aCtx;
1485 aCtx.mbVDev = bVDev;
1486 aCtx.mpDemoRenderer = this;
1487 aCtx.maSize = aSize;
1488 tools::Rectangle aWholeWin(Point(0,0), rDev.GetOutputSizePixel());
1490 drawBackground(rDev, aWholeWin);
1492 if (!bVDev /* want everything in the vdev */ &&
1493 mnSelectedRenderer >= 0 &&
1494 static_cast<sal_uInt32>(mnSelectedRenderer) < maRenderers.size())
1496 aCtx.meStyle = RENDER_EXPANDED;
1497 RegionRenderer * r = maRenderers[mnSelectedRenderer];
1498 // profiling?
1499 if (getIterCount() > 0)
1501 double nStartTime = getTimeNow();
1502 for (int i = 0; i < r->getTestRepeatCount(); i++)
1503 r->RenderRegion(rDev, aWholeWin, aCtx);
1504 addTime(mnSelectedRenderer, getTimeNow() - nStartTime);
1505 } else
1506 r->RenderRegion(rDev, aWholeWin, aCtx);
1508 else
1509 drawThumbs(rDev, aWholeWin, bVDev);
1511 std::vector<VclPtr<vcl::Window> > maInvalidates;
1512 void addInvalidate(vcl::Window *pWindow) { maInvalidates.emplace_back(pWindow); };
1513 void removeInvalidate(vcl::Window *pWindow)
1515 auto aIt = std::find(maInvalidates.begin(), maInvalidates.end(), pWindow);
1516 if (aIt != maInvalidates.end())
1517 maInvalidates.erase(aIt);
1519 void Invalidate()
1521 for (auto const& invalidate : maInvalidates)
1522 invalidate->Invalidate();
1526 #if FIXME_BOUNCE_BUTTON
1527 IMPL_LINK_NOARG(DemoRenderer,BounceTimerCb,Timer*,void)
1529 mpButton->Check(mnBounceX>0);
1530 mpButton->SetPressed(mnBounceY>0);
1532 Point aCur = mpButtonWin->GetPosPixel();
1533 static const int nMovePix = 10;
1534 aCur.Move(mnBounceX * nMovePix, mnBounceX * nMovePix);
1535 Size aWinSize = GetSizePixel();
1536 if (aCur.X() <= 0 || aCur.X() >= aWinSize.Width())
1537 mnBounceX *= -1;
1538 if (aCur.Y() <= 0 || aCur.Y() >= aWinSize.Height())
1539 mnBounceX *= -1;
1540 mpButtonWin->SetPosPixel(aCur);
1542 // All smoke and mirrors to test sub-region invalidation underneath
1543 Rectangle aRect(aCur, mpButtonWin->GetSizePixel());
1544 Invalidate(aRect);
1546 #endif
1548 void DemoRenderer::KeyInput(const KeyEvent &rKEvt)
1550 sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
1552 // click to zoom out
1553 if (mnSelectedRenderer >= 0)
1555 if (nCode == KEY_ESCAPE || nCode == KEY_BACKSPACE)
1557 mnSelectedRenderer = -1;
1558 Invalidate();
1559 return;
1562 else
1564 for (size_t i = 0; i < maRenderers.size(); i++)
1566 if (nCode == maRenderers[i]->getAccelerator())
1568 mnSelectedRenderer = i;
1569 Invalidate();
1570 return;
1576 bool DemoRenderer::MouseButtonDown(const MouseEvent& rMEvt)
1578 // click to zoom out
1579 if (mnSelectedRenderer >= 0)
1581 mnSelectedRenderer = -1;
1582 Invalidate();
1583 return true;
1586 // click on a region to zoom into it
1587 std::vector<tools::Rectangle> aRegions(partition(GetSizePixel(), mnSegmentsX, mnSegmentsY));
1588 for (size_t i = 0; i < aRegions.size(); i++)
1590 if (aRegions[i].IsInside(rMEvt.GetPosPixel()))
1592 mnSelectedRenderer = i;
1593 Invalidate();
1594 return true;
1598 #if FIXME_BOUNCE_BUTTON
1599 // otherwise bounce floating windows
1600 if (!mpButton)
1602 mpButtonWin = VclPtr<FloatingWindow>::Create(this);
1603 mpButton = VclPtr<PushButton>::Create(mpButtonWin);
1604 mpButton->SetSymbol(SymbolType::HELP);
1605 mpButton->SetText("PushButton demo");
1606 mpButton->SetPosSizePixel(Point(0,0), mpButton->GetOptimalSize());
1607 mpButton->Show();
1608 mpButtonWin->SetPosSizePixel(Point(0,0), mpButton->GetOptimalSize());
1609 mpButtonWin->Show();
1610 mnBounceX = 1; mnBounceX = 1;
1611 maBounce.SetInvokeHandler(LINK(this,DemoRenderer,BounceTimerCb));
1612 maBounce.SetTimeout(55);
1613 maBounce.Start();
1615 else
1617 maBounce.Stop();
1618 delete mpButtonWin;
1619 mpButtonWin = NULL;
1620 mpButton = NULL;
1622 #endif
1623 return false;
1626 void DemoRenderer::InitRenderers()
1628 maRenderers.push_back(new DrawLines);
1629 maRenderers.push_back(new DrawText);
1630 maRenderers.push_back(new DrawPoly);
1631 maRenderers.push_back(new DrawEllipse);
1632 maRenderers.push_back(new DrawCheckered);
1633 maRenderers.push_back(new DrawBitmapEx);
1634 maRenderers.push_back(new DrawBitmap);
1635 maRenderers.push_back(new DrawGradient);
1636 maRenderers.push_back(new DrawPolyPolygons);
1637 maRenderers.push_back(new DrawClipped);
1638 maRenderers.push_back(new DrawToVirtualDevice);
1639 maRenderers.push_back(new DrawXOR);
1640 maRenderers.push_back(new DrawIcons());
1641 maRenderers.push_back(new FetchDrawBitmap);
1644 OUString DemoRenderer::getRendererList()
1646 OUStringBuffer aBuf;
1647 for (size_t i = 0; i < maRenderers.size(); i++)
1649 aBuf.append(maRenderers[i]->getName());
1650 aBuf.append(' ');
1652 return aBuf.makeStringAndClear();
1655 double DemoRenderer::getAndResetBenchmark(const RenderStyle style)
1657 double geomean = 1.0;
1658 fprintf(stderr, "Rendering: %s, Times (ms):\n", style == RENDER_THUMB ? "THUMB": "EXPANDED");
1659 for (size_t i = 0; i < maRenderers.size(); i++)
1661 double avgtime = maRenderers[i]->sumTime / maRenderers[i]->countTime;
1662 geomean *= avgtime;
1663 fprintf(stderr, "%s: %f (iteration: %d*%d*%d)\n",
1664 OUStringToOString(maRenderers[i]->getName(),
1665 RTL_TEXTENCODING_UTF8).getStr(), avgtime,
1666 maRenderers[i]->countTime, maRenderers[i]->getTestRepeatCount(),
1667 (style == RENDER_THUMB) ? THUMB_REPEAT_FACTOR : 1);
1668 maRenderers[i]->sumTime = 0;
1669 maRenderers[i]->countTime = 0;
1671 geomean = pow(geomean, 1.0/maRenderers.size());
1672 fprintf(stderr, "GEOMEAN_%s: %f\n", style == RENDER_THUMB ? "THUMB": "EXPANDED", geomean);
1673 return geomean;
1676 void DemoRenderer::setIterCount(sal_Int32 i)
1678 iterCount = i;
1681 sal_Int32 DemoRenderer::getIterCount() const
1683 return iterCount;
1686 void DemoRenderer::addTime(int i, double t)
1688 maRenderers[i]->sumTime += t / maRenderers[i]->getTestRepeatCount();
1689 maRenderers[i]->countTime++;
1692 void DemoRenderer::selectRenderer(const OUString &rName )
1694 for (size_t i = 0; i < maRenderers.size(); i++)
1696 if (maRenderers[i]->getName() == rName)
1698 mnSelectedRenderer = i;
1699 Invalidate();
1700 return;
1705 int DemoRenderer::selectNextRenderer()
1707 mnSelectedRenderer++;
1708 if (mnSelectedRenderer == static_cast<signed>(maRenderers.size()))
1709 mnSelectedRenderer = -1;
1710 Invalidate();
1711 return mnSelectedRenderer;
1714 class DemoWin : public WorkWindow
1716 DemoRenderer &mrRenderer;
1717 bool underTesting;
1718 bool testThreads;
1720 class RenderThread final : public salhelper::Thread {
1721 DemoWin &mrWin;
1722 sal_uInt32 const mnDelaySecs = 0;
1723 public:
1724 RenderThread(DemoWin &rWin, sal_uInt32 nDelaySecs)
1725 : Thread("vcldemo render thread")
1726 , mrWin(rWin)
1727 , mnDelaySecs(nDelaySecs)
1729 launch();
1731 virtual ~RenderThread() override
1733 join();
1735 virtual void execute() override
1737 wait(std::chrono::seconds(mnDelaySecs));
1739 SolarMutexGuard aGuard;
1740 fprintf (stderr, "render from a different thread\n");
1741 mrWin.Invalidate();
1744 rtl::Reference<RenderThread> mxThread;
1746 public:
1747 DemoWin(DemoRenderer &rRenderer, bool bThreads) :
1748 WorkWindow(nullptr, WB_APP | WB_STDWORK),
1749 mrRenderer(rRenderer),
1750 testThreads(bThreads)
1752 mrRenderer.addInvalidate(this);
1753 underTesting = false;
1755 virtual ~DemoWin() override
1757 disposeOnce();
1759 virtual void dispose() override
1761 mxThread.clear();
1762 mrRenderer.removeInvalidate(this);
1763 WorkWindow::dispose();
1765 virtual void MouseButtonDown(const MouseEvent& rMEvt) override
1767 mrRenderer.SetSizePixel(GetSizePixel());
1768 if (!mrRenderer.MouseButtonDown(rMEvt))
1770 if (testThreads)
1771 { // render this window asynchronously in a new thread
1772 sal_uInt32 nDelaySecs = 0;
1773 if (rMEvt.GetButtons() & MOUSE_RIGHT)
1774 nDelaySecs = 5;
1775 mxThread = new RenderThread(*this, nDelaySecs);
1777 else
1778 { // spawn another window
1779 VclPtrInstance<DemoWin> pNewWin(mrRenderer, testThreads);
1780 pNewWin->SetText("Another interactive VCL demo window");
1781 pNewWin->Show();
1785 virtual void KeyInput(const KeyEvent& rKEvt) override
1787 mrRenderer.SetSizePixel(GetSizePixel());
1788 mrRenderer.KeyInput(rKEvt);
1790 virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override
1792 mrRenderer.SetSizePixel(GetSizePixel());
1793 fprintf(stderr, "DemoWin::Paint(%ld,%ld,%ld,%ld)\n", rRect.getX(), rRect.getY(), rRect.getWidth(), rRect.getHeight());
1794 if (mrRenderer.getIterCount() == 0)
1795 mrRenderer.drawToDevice(rRenderContext, GetSizePixel(), false);
1796 else
1797 TestAndQuit(rRenderContext);
1800 void TestAndQuit(vcl::RenderContext& rRenderContext)
1802 if (underTesting)
1803 return;
1804 underTesting = true;
1805 for (sal_Int32 i = 0; i < mrRenderer.getIterCount(); i++)
1807 while (mrRenderer.selectNextRenderer() > -1)
1809 mrRenderer.drawToDevice(rRenderContext, GetSizePixel(), false);
1813 double expandedGEOMEAN = mrRenderer.getAndResetBenchmark(RENDER_EXPANDED);
1815 for (sal_Int32 i = 0; i < mrRenderer.getIterCount(); i++)
1816 mrRenderer.drawToDevice(rRenderContext, GetSizePixel(), false);
1818 double thumbGEOMEAN = mrRenderer.getAndResetBenchmark(RENDER_THUMB);
1820 fprintf(stderr, "GEOMEAN_TOTAL: %f\n", pow(thumbGEOMEAN * expandedGEOMEAN, 0.5));
1821 Application::Quit();
1825 struct PointerData {
1826 PointerStyle eStyle;
1827 const char * name;
1829 static const PointerData gvPointerData [] = {
1830 { PointerStyle::Null, "Null" },
1831 { PointerStyle::Magnify, "Magnify" },
1832 { PointerStyle::Fill, "Fill" },
1833 { PointerStyle::MoveData, "MoveData" },
1834 { PointerStyle::CopyData, "CopyData" },
1835 { PointerStyle::MoveFile, "MoveFile" },
1836 { PointerStyle::CopyFile, "CopyFile" },
1837 { PointerStyle::MoveFiles, "MoveFiles" },
1838 { PointerStyle::CopyFiles, "CopyFiles" },
1839 { PointerStyle::NotAllowed, "NotAllowed" },
1840 { PointerStyle::Rotate, "Rotate" },
1841 { PointerStyle::HShear, "HShear" },
1842 { PointerStyle::VShear, "VShear" },
1843 { PointerStyle::DrawLine, "DrawLine" },
1844 { PointerStyle::DrawRect, "DrawRect" },
1845 { PointerStyle::DrawPolygon, "DrawPolygon" },
1846 { PointerStyle::DrawBezier, "DrawBezier" },
1847 { PointerStyle::DrawArc, "DrawArc" },
1848 { PointerStyle::DrawPie, "DrawPie" },
1849 { PointerStyle::DrawCircleCut, "DrawCircleCut" },
1850 { PointerStyle::DrawEllipse, "DrawEllipse" },
1851 { PointerStyle::DrawConnect, "DrawConnect" },
1852 { PointerStyle::DrawText, "DrawText" },
1853 { PointerStyle::Mirror, "Mirror" },
1854 { PointerStyle::Crook, "Crook" },
1855 { PointerStyle::Crop, "Crop" },
1856 { PointerStyle::MovePoint, "MovePoint" },
1857 { PointerStyle::MoveBezierWeight, "MoveBezierWeight" },
1858 { PointerStyle::DrawFreehand, "DrawFreehand" },
1859 { PointerStyle::DrawCaption, "DrawCaption" },
1860 { PointerStyle::LinkData, "LinkData" },
1861 { PointerStyle::MoveDataLink, "MoveDataLink" },
1862 { PointerStyle::CopyDataLink, "CopyDataLink" },
1863 { PointerStyle::LinkFile, "LinkFile" },
1864 { PointerStyle::MoveFileLink, "MoveFileLink" },
1865 { PointerStyle::CopyFileLink, "CopyFileLink" },
1866 { PointerStyle::Chart, "Chart" },
1867 { PointerStyle::Detective, "Detective" },
1868 { PointerStyle::PivotCol, "PivotCol" },
1869 { PointerStyle::PivotRow, "PivotRow" },
1870 { PointerStyle::PivotField, "PivotField" },
1871 { PointerStyle::PivotDelete, "PivotDelete" },
1872 { PointerStyle::Chain, "Chain" },
1873 { PointerStyle::ChainNotAllowed, "ChainNotAllowed" },
1874 { PointerStyle::AutoScrollN, "AutoScrollN" },
1875 { PointerStyle::AutoScrollS, "AutoScrollS" },
1876 { PointerStyle::AutoScrollW, "AutoScrollW" },
1877 { PointerStyle::AutoScrollE, "AutoScrollE" },
1878 { PointerStyle::AutoScrollNW, "AutoScrollNW" },
1879 { PointerStyle::AutoScrollNE, "AutoScrollNE" },
1880 { PointerStyle::AutoScrollSW, "AutoScrollSW" },
1881 { PointerStyle::AutoScrollSE, "AutoScrollSE" },
1882 { PointerStyle::AutoScrollNS, "AutoScrollNS" },
1883 { PointerStyle::AutoScrollWE, "AutoScrollWE" },
1884 { PointerStyle::AutoScrollNSWE, "AutoScrollNSWE" },
1885 { PointerStyle::TextVertical, "TextVertical" },
1886 { PointerStyle::TabSelectS, "TabSelectS" },
1887 { PointerStyle::TabSelectE, "TabSelectE" },
1888 { PointerStyle::TabSelectSE, "TabSelectSE" },
1889 { PointerStyle::TabSelectW, "TabSelectW" },
1890 { PointerStyle::TabSelectSW, "TabSelectSW" },
1891 { PointerStyle::HideWhitespace, "HideWhitespace" },
1892 { PointerStyle::ShowWhitespace, "ShowWhitespace" },
1894 class DemoWidgets : public WorkWindow
1896 VclPtr<MenuBar> mpBar;
1897 VclPtr<VclBox> mpBox;
1898 VclPtr<ToolBox> mpToolbox;
1899 VclPtr<PushButton> mpButton;
1900 VclPtr<VclHBox> mpHBox;
1901 VclPtr<CheckBox> mpGLCheck;
1902 VclPtr<ComboBox> mpGLCombo;
1903 VclPtr<PushButton> mpGLButton;
1904 std::vector<VclPtr<VclHBox>> mvCursorBoxes;
1905 std::vector<VclPtr<PushButton>> mvCursorButtons;
1907 DECL_LINK(GLTestClick, Button*, void);
1908 DECL_LINK(CursorButtonClick, Button*, void);
1910 public:
1911 DemoWidgets() :
1912 WorkWindow(nullptr, WB_APP | WB_STDWORK),
1913 mpBox(VclPtrInstance<VclVBox>(this, false, 3)),
1914 mpToolbox(VclPtrInstance<ToolBox>(mpBox.get())),
1915 mpButton(VclPtrInstance<PushButton>(mpBox.get())),
1916 mpHBox(VclPtrInstance<VclHBox>(mpBox.get(), true, 3)),
1917 mpGLCheck(VclPtrInstance<CheckBox>(mpHBox.get())),
1918 mpGLCombo(VclPtrInstance<ComboBox>(mpHBox.get())),
1919 mpGLButton(VclPtrInstance<PushButton>(mpHBox.get()))
1921 SetText("VCL widget demo");
1923 Wallpaper aWallpaper(BitmapEx("sfx2/res/startcenter-logo.png"));
1924 aWallpaper.SetStyle(WallpaperStyle::BottomRight);
1925 aWallpaper.SetColor(COL_RED);
1927 mpBox->SetBackground(aWallpaper);
1928 mpBox->Show();
1930 Help::EnableBalloonHelp();
1931 mpToolbox->SetHelpText("Help text");
1932 mpToolbox->InsertItem(0, "Toolbar item");
1933 mpToolbox->SetQuickHelpText(0, "This is a tooltip popup");
1934 mpToolbox->InsertSeparator();
1935 mpToolbox->Show();
1937 mpButton->SetText("Click me; go on");
1938 mpButton->Show();
1940 mpGLCheck->SetText("Test in OGL zone");
1941 mpGLCheck->Show();
1942 mpGLCombo->InsertEntry("sleep 1 second");
1943 mpGLCombo->InsertEntry("sleep 3 seconds");
1944 mpGLCombo->InsertEntry("sleep 7 seconds");
1945 mpGLCombo->SelectEntryPos(2);
1946 mpGLCombo->Show();
1947 mpGLButton->SetText("Execute test");
1948 mpGLButton->SetClickHdl(LINK(this,DemoWidgets,GLTestClick));
1949 mpGLButton->Show();
1950 mpHBox->Show();
1952 int i = 0;
1953 VclHBox* pCurrentCursorHBox = nullptr;
1954 constexpr int numButtonsPerRow = 9;
1955 for (auto & rData : gvPointerData)
1957 if (i % numButtonsPerRow == 0)
1959 mvCursorBoxes.push_back(VclPtrInstance<VclHBox>(mpBox.get(), true, numButtonsPerRow));
1960 pCurrentCursorHBox = mvCursorBoxes.back().get();
1961 pCurrentCursorHBox->Show();
1963 mvCursorButtons.emplace_back(VclPtrInstance<PushButton>(pCurrentCursorHBox));
1964 PushButton& rButton = *mvCursorButtons.back();
1965 rButton.SetText(OUString::createFromAscii(rData.name));
1966 rButton.SetClickHdl(LINK(this,DemoWidgets,CursorButtonClick));
1967 rButton.Show();
1968 ++i;
1971 mpBar = VclPtr<MenuBar>::Create();
1972 mpBar->InsertItem(0,"File");
1973 VclPtrInstance<PopupMenu> pPopup;
1974 pPopup->InsertItem(0,"Item");
1975 mpBar->SetPopupMenu(0, pPopup);
1976 SetMenuBar(mpBar);
1978 Show();
1980 virtual ~DemoWidgets() override { disposeOnce(); }
1981 virtual void dispose() override
1983 mpGLButton.disposeAndClear();
1984 mpGLCombo.disposeAndClear();
1985 mpGLCheck.disposeAndClear();
1986 mpHBox.disposeAndClear();
1987 for (auto & p : mvCursorButtons)
1988 p.disposeAndClear();
1989 mvCursorButtons.clear();
1990 for (auto & p : mvCursorBoxes)
1991 p.disposeAndClear();
1992 mvCursorBoxes.clear();
1993 mpToolbox.disposeAndClear();
1994 mpButton.disposeAndClear();
1995 mpBox.disposeAndClear();
1996 mpBar.disposeAndClear();
1997 WorkWindow::dispose();
1999 virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override
2001 tools::Rectangle aWholeSize(Point(0, 0),GetOutputSizePixel());
2002 vcl::Region aClip(aWholeSize);
2003 tools::Rectangle aExclude(tools::Rectangle(Point(50,50),Size(100,100)));
2004 aClip.Exclude(aExclude);
2006 Wallpaper aWallpaper(COL_GREEN);
2008 rRenderContext.Push(PushFlags::CLIPREGION);
2009 rRenderContext.IntersectClipRegion(aClip);
2010 rRenderContext.DrawWallpaper(aWholeSize, aWallpaper);
2011 rRenderContext.Pop();
2013 ScopedVclPtrInstance< VirtualDevice > pDev(*this);
2014 pDev->EnableRTL(IsRTLEnabled());
2015 pDev->SetOutputSizePixel(aExclude.GetSize());
2017 tools::Rectangle aSubRect(aWholeSize);
2018 aSubRect.Move(-aExclude.Left(), -aExclude.Top());
2019 pDev->DrawWallpaper(aSubRect, aWallpaper );
2021 rRenderContext.DrawOutDev(aExclude.TopLeft(), aExclude.GetSize(),
2022 Point( 0, 0 ), aExclude.GetSize(), *pDev );
2026 IMPL_LINK_NOARG(DemoWidgets, GLTestClick, Button*, void)
2028 sal_Int32 nSelected = mpGLCombo->GetSelectedEntryPos();
2029 sal_uInt32 nDelaySeconds = 0;
2031 switch (nSelected)
2033 case 0:
2034 nDelaySeconds = 1;
2035 break;
2036 case 1:
2037 nDelaySeconds = 3;
2038 break;
2039 case 2:
2040 nDelaySeconds = 7;
2041 break;
2042 default:
2043 break;
2046 // Only create OpenGLZone RAII object if asked for:
2047 std::unique_ptr<OpenGLZone> zone;
2048 if (mpGLCheck->IsChecked()) {
2049 zone.reset(new OpenGLZone);
2052 osl::Thread::wait(std::chrono::seconds(nDelaySeconds));
2055 IMPL_LINK(DemoWidgets, CursorButtonClick, Button*, pButton, void)
2057 for (size_t i=0; i<SAL_N_ELEMENTS(gvPointerData); ++i)
2059 if (mvCursorButtons[i].get() == pButton)
2061 mpBox->SetPointer( gvPointerData[i].eStyle );
2062 return;
2065 assert(false);
2068 class DemoPopup : public FloatingWindow
2070 public:
2071 DemoPopup() : FloatingWindow( nullptr, WB_SYSTEMWINDOW|WB_TOOLTIPWIN)
2073 SetType( WindowType::HELPTEXTWINDOW );
2075 SetOutputSizePixel( Size( 300, 30 ) );
2076 SetBackground(Wallpaper(COL_YELLOW));
2078 Show( true, ShowFlags::NoActivate );
2079 Update();
2082 virtual void Paint(vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle&) override
2084 // Interestingly in GL mode on Windows, this doesn't render.
2086 Size aSize = GetOutputSizePixel();
2087 tools::Rectangle aTextRect(Point(6, 6), aSize);
2089 SetTextColor(COL_BLACK);
2090 SetTextAlign(ALIGN_TOP);
2091 DrawText(aTextRect, "This is a standalone help text test",
2092 DrawTextFlags::MultiLine|DrawTextFlags::WordBreak|
2093 DrawTextFlags::Left|DrawTextFlags::Top);
2095 SetLineColor(COL_BLACK);
2096 SetFillColor();
2097 DrawRect( tools::Rectangle( Point(), aSize ) );
2098 aSize.AdjustWidth( -2 );
2099 aSize.AdjustHeight( -2 );
2100 Color aColor( GetLineColor() );
2101 SetLineColor( COL_GRAY );
2102 DrawRect( tools::Rectangle( Point( 1, 1 ), aSize ) );
2103 SetLineColor( aColor );
2106 virtual void MouseButtonDown( const MouseEvent & ) override
2108 Application::Quit();
2112 class OpenGLTests
2114 VclPtr<WorkWindow> mxWinA;
2115 VclPtr<WorkWindow> mxWinB;
2116 rtl::Reference<OpenGLContext> mpA;
2117 rtl::Reference<OpenGLContext> mpB;
2119 static OpenGLSalGraphicsImpl *getImpl(const VclPtr<OutputDevice> &xOut)
2121 SalGraphics *pGraphics = xOut->GetGraphics();
2122 return dynamic_cast<OpenGLSalGraphicsImpl *>(pGraphics->GetImpl());
2124 public:
2125 OpenGLTests() :
2126 mxWinA(VclPtr<WorkWindow>::Create(nullptr, WB_APP | WB_STDWORK)),
2127 mxWinB(VclPtr<WorkWindow>::Create(nullptr, WB_APP | WB_STDWORK))
2129 OpenGLSalGraphicsImpl *pImplA;
2130 OpenGLSalGraphicsImpl *pImplB;
2131 if (!OpenGLHelper::isVCLOpenGLEnabled())
2133 pImplA = pImplB = nullptr;
2134 fprintf (stderr, "OpenGL is not enabled: try SAL_FORCEGL=1\n");
2135 return;
2138 pImplA = getImpl(mxWinA);
2139 pImplB = getImpl(mxWinB);
2140 assert (pImplA && pImplB);
2141 mpA = pImplA->GetOpenGLContext();
2142 mpB = pImplB->GetOpenGLContext();
2144 assert (mpA.is() && mpB.is());
2145 assert (mpA != mpB);
2147 ~OpenGLTests()
2149 mxWinB.disposeAndClear();
2150 mxWinA.disposeAndClear();
2153 void testCurrentFramebuffer()
2155 fprintf(stderr,"test OpenGLContext's framebuffer association.\n");
2156 mpA->makeCurrent();
2157 OpenGLFramebuffer *pBuffer;
2159 OpenGLTexture aTexture(256,128);
2160 pBuffer = mpA->AcquireFramebuffer(aTexture);
2162 assert (pBuffer->IsFree()); (void)pBuffer;
2163 mpB->makeCurrent();
2164 assert (mpA->mpCurrentFramebuffer == nullptr);
2167 void testVirtualDevice()
2169 fprintf(stderr, "test sharing OpenGLContexts with virtual-devices reference counting\n");
2170 VclPtrInstance<WorkWindow> xTempWin(nullptr, WB_STDWORK);
2171 xTempWin->Show();
2172 // forcibly make this context current by rendering
2173 xTempWin->DrawPixel(Point(0, 0), COL_RED);
2175 // get some other guys to leach off this context
2176 VclPtrInstance<VirtualDevice> xVDev;
2177 OpenGLSalGraphicsImpl* pImpl = getImpl(xVDev);
2178 assert(pImpl);
2179 rtl::Reference<OpenGLContext> pContext = pImpl->GetOpenGLContext();
2180 VclPtrInstance<VirtualDevice> xVDev2;
2181 OpenGLSalGraphicsImpl* pImpl2 = getImpl(xVDev2);
2182 assert(pImpl2);
2183 rtl::Reference<OpenGLContext> pContext2 = pImpl2->GetOpenGLContext();
2185 // sharing the same off-screen context.
2186 assert(pContext == pContext2);
2187 assert(pContext == getImpl(xTempWin)->GetOpenGLContext());
2188 assert(pContext != mpA && pContext != mpB);
2189 (void)pContext; (void)pContext2;
2191 // Kill the parent we free-ride on ...
2192 xTempWin.disposeAndClear();
2194 // This appears to continue working; fun.
2195 Point aPt(0, 0);
2196 xVDev->DrawPixel(aPt, COL_GREEN);
2197 assert(xVDev->GetPixel(aPt) == COL_GREEN);
2198 xVDev.disposeAndClear();
2200 // Switch context to see if we can switch back.
2201 mxWinA->DrawPixel(aPt, COL_WHITE);
2203 // Now try switching back to this guy ...
2204 xVDev2->DrawPixel(aPt, COL_BLUE);
2205 assert(xVDev2->GetPixel(aPt) == COL_BLUE);
2206 xVDev2.disposeAndClear();
2209 int execute()
2211 if (!OpenGLHelper::isVCLOpenGLEnabled())
2212 return 1;
2214 testCurrentFramebuffer();
2215 testVirtualDevice();
2217 return 0;
2221 namespace {
2222 void renderFonts(const std::vector<OUString> &aFontNames)
2224 ScopedVclPtrInstance<VirtualDevice> xDevice;
2225 Size aSize(1024, 1024);
2226 xDevice->SetOutputSizePixel(aSize);
2228 for (auto & aFontName : aFontNames)
2230 vcl::Font aFont(aFontName, Size(0,96));
2231 #if 0
2232 aFont.SetCOL_BLACK);
2233 xDevice->SetFont(aFont);
2234 xDevice->Erase();
2236 FontMetric aMetric = xDevice->GetFontMetric(aFont);
2238 FontCharMapRef xMap;
2239 if (xDevice->GetFontCharMap(xMap))
2241 ... iterate through glyphs ...
2245 bool GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr, int nIndex,
2246 int nLen, int nBase, MetricVector& rVector );
2248 include/vcl/outdev.hxx:typedef std::vector< Rectangle > MetricVector;
2249 include/vcl/outdev.hxx: MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr );
2250 include/vcl/outdev.hxx: MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr,
2251 include/vcl/outdev.hxx: MetricVector* pVector, OUString* pDisplayText, vcl::ITextLayout& _rLayout );
2252 include/vcl/outdev.hxx: DrawTextFlags nStyle = DrawTextFlags::Mnemonic, MetricVector* pVector = nullp
2254 bool GetTextBoundRect( Rectangle& rRect,
2255 const OUString& rStr, sal_Int32 nBase = 0, sal_Int32 nIndex = 0, sal_Int32 nLen = -1,
2256 sal_uLong nLayoutWidth = 0, const long* pDXArray = nullptr ) const;
2259 void DrawText( const Point& rStartPt, const OUString& rStr,
2260 sal_Int32 nIndex = 0, sal_Int32 nLen = -1,
2261 MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr );
2263 void DrawText( const Rectangle& rRect,
2264 const OUString& rStr, DrawTextFlags nStyle = DrawTextFlags::NONE,
2265 MetricVector* pVector = nullptr, OUString* pDisplayText = nullptr,
2266 vcl::ITextLayout* _pTextLayout = nullptr );
2268 Rectangle GetTextRect( const Rectangle& rRect,
2269 const OUString& rStr, DrawTextFlags nStyle = DrawTextFlags::WordBreak,
2270 TextRectInfo* pInfo = nullptr,
2271 const vcl::ITextLayout* _pTextLayout = nullptr ) const;
2273 #endif
2279 class DemoApp : public Application
2281 static int showHelp(DemoRenderer &rRenderer)
2283 fprintf(stderr,"vcldemo - a VCL test app\n");
2284 fprintf(stderr," --help - print this text\n");
2285 fprintf(stderr," --show <renderer> - start with a given renderer, options are:\n");
2286 OUString aRenderers(rRenderer.getRendererList());
2287 fprintf(stderr," %s\n",
2288 OUStringToOString(aRenderers, RTL_TEXTENCODING_UTF8).getStr());
2289 fprintf(stderr," --test <iterCount> - create benchmark data\n");
2290 fprintf(stderr," --widgets - launch the widget test.\n");
2291 fprintf(stderr," --popup - launch the popup test.\n");
2292 fprintf(stderr," --threads - render from multiple threads.\n");
2293 fprintf(stderr," --gltest - run openGL regression tests.\n");
2294 fprintf(stderr," --font <fontname> - run the font render test.\n");
2295 fprintf(stderr, "\n");
2296 return 0;
2299 public:
2300 DemoApp() {}
2302 virtual int Main() override
2306 bool bWidgets = false, bThreads = false;
2307 bool bPopup = false, bGLTest = false;
2308 DemoRenderer aRenderer;
2309 std::vector<OUString> aFontNames;
2311 for (sal_uInt16 i = 0; i < GetCommandLineParamCount(); ++i)
2313 bool bLast = i == GetCommandLineParamCount() - 1;
2314 OUString aArg = GetCommandLineParam(i);
2315 if (aArg == "--help" || aArg == "-h")
2316 return showHelp(aRenderer);
2317 if (aArg == "--show")
2319 if (bLast)
2320 return showHelp(aRenderer);
2321 else
2322 aRenderer.selectRenderer(GetCommandLineParam(++i));
2324 else if (aArg == "--test")
2326 if (bLast)
2327 return showHelp(aRenderer);
2328 else
2329 aRenderer.setIterCount(GetCommandLineParam(++i).toInt32());
2331 else if (aArg == "--widgets")
2332 bWidgets = true;
2333 else if (aArg == "--popup")
2334 bPopup = true;
2335 else if (aArg == "--gltest")
2336 bGLTest = true;
2337 else if (aArg == "--threads")
2338 bThreads = true;
2339 else if (aArg == "--font" && !bLast)
2340 aFontNames.push_back(GetCommandLineParam(++i));
2341 else if (aArg.startsWith("--"))
2343 fprintf(stderr,"Unknown argument '%s'\n",
2344 OUStringToOString(aArg, RTL_TEXTENCODING_UTF8).getStr());
2345 return showHelp(aRenderer);
2349 ScopedVclPtrInstance<DemoWin> aMainWin(aRenderer, bThreads);
2350 VclPtr<DemoWidgets> xWidgets;
2351 VclPtr<DemoPopup> xPopup;
2353 aMainWin->SetText("Interactive VCL demo #1");
2354 #if HAVE_FEATURE_OPENGL
2355 if (bGLTest)
2357 OpenGLTests aTests;
2358 return aTests.execute();
2360 else
2361 #endif
2362 if (bWidgets)
2363 xWidgets = VclPtr< DemoWidgets >::Create ();
2364 else if (bPopup)
2365 xPopup = VclPtrInstance< DemoPopup> ();
2366 else if (!aFontNames.empty())
2367 renderFonts(aFontNames);
2368 else
2369 aMainWin->Show();
2371 Application::Execute();
2373 xWidgets.disposeAndClear();
2374 xPopup.disposeAndClear();
2376 catch (const css::uno::Exception&)
2378 TOOLS_WARN_EXCEPTION("vcl.app", "Fatal");
2379 return 1;
2381 catch (const std::exception& e)
2383 SAL_WARN("vcl.app", "Fatal: " << e.what());
2384 return 1;
2386 return 0;
2389 protected:
2390 void Init() override
2394 uno::Reference<uno::XComponentContext> xComponentContext
2395 = ::cppu::defaultBootstrap_InitialComponentContext();
2396 uno::Reference<lang::XMultiServiceFactory> xMSF;
2397 xMSF.set(xComponentContext->getServiceManager(), uno::UNO_QUERY);
2398 if(!xMSF.is())
2399 Application::Abort("Bootstrap failure - no service manager");
2401 ::comphelper::setProcessServiceFactory(xMSF);
2403 catch (const uno::Exception &e)
2405 Application::Abort("Bootstrap exception " + e.Message);
2408 void DeInit() override
2410 uno::Reference< lang::XComponent >(
2411 comphelper::getProcessComponentContext(),
2412 uno::UNO_QUERY_THROW)-> dispose();
2413 ::comphelper::setProcessServiceFactory(nullptr);
2417 void vclmain::createApplication()
2419 #ifdef _WIN32
2420 _putenv_s("LIBO_VCL_DEMO", "1");
2421 #else
2422 setenv("LIBO_VCL_DEMO", "1", 0);
2423 #endif
2424 static DemoApp aApp;
2427 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */