xref: /trunk/main/drawinglayer/source/primitive2d/graphicprimitive2d.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_drawinglayer.hxx"
30 
31 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
32 #include <drawinglayer/animation/animationtiming.hxx>
33 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
34 #include <drawinglayer/primitive2d/rendergraphicprimitive2d.hxx>
35 #include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
36 #include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
38 #include <basegfx/polygon/b2dpolygon.hxx>
39 #include <basegfx/polygon/b2dpolygontools.hxx>
40 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
42 
43 //////////////////////////////////////////////////////////////////////////////
44 // helper class for animated graphics
45 
46 #include <vcl/animate.hxx>
47 #include <vcl/graph.hxx>
48 #include <vcl/virdev.hxx>
49 #include <vcl/svapp.hxx>
50 #include <vcl/metaact.hxx>
51 
52 //////////////////////////////////////////////////////////////////////////////
53 // includes for testing MetafilePrimitive2D::create2DDecomposition
54 
55 // this switch defines if the test code is included or not
56 #undef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE
57 
58 #ifdef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE
59 #include <vcl/gradient.hxx>
60 #include <vcl/pngread.hxx>
61 #include <vcl/lineinfo.hxx>
62 #endif // USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE
63 
64 //////////////////////////////////////////////////////////////////////////////
65 
66 namespace
67 {
68     struct animationStep
69     {
70         BitmapEx                                maBitmapEx;
71         sal_uInt32                              mnTime;
72     };
73 
74     class animatedBitmapExPreparator
75     {
76         ::Animation                             maAnimation;
77         ::std::vector< animationStep >          maSteps;
78 
79         sal_uInt32 generateStepTime(sal_uInt32 nIndex) const;
80 
81     public:
82         animatedBitmapExPreparator(const Graphic& rGraphic);
83 
84         sal_uInt32 count() const { return maSteps.size(); }
85         sal_uInt32 loopCount() const { return (sal_uInt32)maAnimation.GetLoopCount(); }
86         sal_uInt32 stepTime(sal_uInt32 a) const { return maSteps[a].mnTime; }
87         const BitmapEx& stepBitmapEx(sal_uInt32 a) const { return maSteps[a].maBitmapEx; }
88     };
89 
90     sal_uInt32 animatedBitmapExPreparator::generateStepTime(sal_uInt32 nIndex) const
91     {
92         const AnimationBitmap& rAnimBitmap = maAnimation.Get(sal_uInt16(nIndex));
93         sal_uInt32 nWaitTime(rAnimBitmap.nWait * 10);
94 
95         // #115934#
96         // Take care of special value for MultiPage TIFFs. ATM these shall just
97         // show their first page. Later we will offer some switching when object
98         // is selected.
99         if(ANIMATION_TIMEOUT_ON_CLICK == rAnimBitmap.nWait)
100         {
101             // ATM the huge value would block the timer, so
102             // use a long time to show first page (whole day)
103             nWaitTime = 100 * 60 * 60 * 24;
104         }
105 
106         // Bad trap: There are animated gifs with no set WaitTime (!).
107         // In that case use a default value.
108         if(0L == nWaitTime)
109         {
110             nWaitTime = 100L;
111         }
112 
113         return nWaitTime;
114     }
115 
116     animatedBitmapExPreparator::animatedBitmapExPreparator(const Graphic& rGraphic)
117     :   maAnimation(rGraphic.GetAnimation())
118     {
119         OSL_ENSURE(GRAPHIC_BITMAP == rGraphic.GetType() && rGraphic.IsAnimated(), "animatedBitmapExPreparator: graphic is not animated (!)");
120 
121         // #128539# secure access to Animation, looks like there exist animated GIFs out there
122         // with a step count of zero
123         if(maAnimation.Count())
124         {
125             VirtualDevice aVirtualDevice(*Application::GetDefaultDevice());
126             VirtualDevice aVirtualDeviceMask(*Application::GetDefaultDevice(), 1L);
127 
128             // Prepare VirtualDevices and their states
129             aVirtualDevice.EnableMapMode(sal_False);
130             aVirtualDeviceMask.EnableMapMode(sal_False);
131             aVirtualDevice.SetOutputSizePixel(maAnimation.GetDisplaySizePixel());
132             aVirtualDeviceMask.SetOutputSizePixel(maAnimation.GetDisplaySizePixel());
133             aVirtualDevice.Erase();
134             aVirtualDeviceMask.Erase();
135 
136             for(sal_uInt16 a(0L); a < maAnimation.Count(); a++)
137             {
138                 animationStep aNextStep;
139                 aNextStep.mnTime = generateStepTime(a);
140 
141                 // prepare step
142                 const AnimationBitmap& rAnimBitmap = maAnimation.Get(sal_uInt16(a));
143 
144                 switch(rAnimBitmap.eDisposal)
145                 {
146                     case DISPOSE_NOT:
147                     {
148                         aVirtualDevice.DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx);
149                         Bitmap aMask = rAnimBitmap.aBmpEx.GetMask();
150 
151                         if(aMask.IsEmpty())
152                         {
153                             const Point aEmpty;
154                             const Rectangle aRect(aEmpty, aVirtualDeviceMask.GetOutputSizePixel());
155                             const Wallpaper aWallpaper(COL_BLACK);
156                             aVirtualDeviceMask.DrawWallpaper(aRect, aWallpaper);
157                         }
158                         else
159                         {
160                             BitmapEx aExpandVisibilityMask = BitmapEx(aMask, aMask);
161                             aVirtualDeviceMask.DrawBitmapEx(rAnimBitmap.aPosPix, aExpandVisibilityMask);
162                         }
163 
164                         break;
165                     }
166                     case DISPOSE_BACK:
167                     {
168                         // #i70772# react on no mask, for primitives, too.
169                         const Bitmap aMask(rAnimBitmap.aBmpEx.GetMask());
170                         const Bitmap aContent(rAnimBitmap.aBmpEx.GetBitmap());
171 
172                         aVirtualDeviceMask.Erase();
173                         aVirtualDevice.DrawBitmap(rAnimBitmap.aPosPix, aContent);
174 
175                         if(aMask.IsEmpty())
176                         {
177                             const Rectangle aRect(rAnimBitmap.aPosPix, aContent.GetSizePixel());
178                             aVirtualDeviceMask.SetFillColor(COL_BLACK);
179                             aVirtualDeviceMask.SetLineColor();
180                             aVirtualDeviceMask.DrawRect(aRect);
181                         }
182                         else
183                         {
184                             aVirtualDeviceMask.DrawBitmap(rAnimBitmap.aPosPix, aMask);
185                         }
186 
187                         break;
188                     }
189                     case DISPOSE_FULL:
190                     {
191                         aVirtualDevice.DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx);
192                         break;
193                     }
194                     case DISPOSE_PREVIOUS :
195                     {
196                         aVirtualDevice.DrawBitmapEx(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx);
197                         aVirtualDeviceMask.DrawBitmap(rAnimBitmap.aPosPix, rAnimBitmap.aBmpEx.GetMask());
198                         break;
199                     }
200                 }
201 
202                 // create BitmapEx
203                 Bitmap aMainBitmap = aVirtualDevice.GetBitmap(Point(), aVirtualDevice.GetOutputSizePixel());
204                 Bitmap aMaskBitmap = aVirtualDeviceMask.GetBitmap(Point(), aVirtualDeviceMask.GetOutputSizePixel());
205                 aNextStep.maBitmapEx = BitmapEx(aMainBitmap, aMaskBitmap);
206 
207                 // add to vector
208                 maSteps.push_back(aNextStep);
209             }
210         }
211     }
212 } // end of anonymous namespace
213 
214 //////////////////////////////////////////////////////////////////////////////
215 
216 namespace drawinglayer
217 {
218     namespace primitive2d
219     {
220         Primitive2DSequence GraphicPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D&
221 #ifdef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE
222             rViewInformation
223 #else
224             /*rViewInformation*/
225 #endif // USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE
226             ) const
227         {
228             Primitive2DSequence aRetval;
229 
230             if(255L != getGraphicAttr().GetTransparency())
231             {
232                 Primitive2DReference xPrimitive;
233 
234                 // do not apply mirroring from GraphicAttr to the Metafile by calling
235                 // GetTransformedGraphic, this will try to mirror the Metafile using Scale()
236                 // at the Metafile. This again calls Scale at the single MetaFile actions,
237                 // but this implementation never worked. I reworked that implementations,
238                 // but for security reasons i will try not to use it.
239                 basegfx::B2DHomMatrix aTransform(getTransform());
240 
241                 if(getGraphicAttr().IsMirrored())
242                 {
243                     // content needs mirroring
244                     const bool bHMirr(getGraphicAttr().GetMirrorFlags() & BMP_MIRROR_HORZ);
245                     const bool bVMirr(getGraphicAttr().GetMirrorFlags() & BMP_MIRROR_VERT);
246 
247                     // mirror by applying negative scale to the unit primitive and
248                     // applying the object transformation on it.
249                     aTransform = basegfx::tools::createScaleB2DHomMatrix(
250                         bHMirr ? -1.0 : 1.0,
251                         bVMirr ? -1.0 : 1.0);
252                     aTransform.translate(
253                         bHMirr ? 1.0 : 0.0,
254                         bVMirr ? 1.0 : 0.0);
255                     aTransform = getTransform() * aTransform;
256                 }
257 
258                 // Get transformed graphic. Suppress rotation and cropping, only filtering is needed
259                 // here (and may be replaced later on). Cropping is handled below as mask primitive (if set).
260                 // Also need to suppress mirroring, it is part of the transformation now (see above).
261                 GraphicAttr aSuppressGraphicAttr(getGraphicAttr());
262                 aSuppressGraphicAttr.SetCrop(0, 0, 0, 0);
263                 aSuppressGraphicAttr.SetRotation(0);
264                 aSuppressGraphicAttr.SetMirrorFlags(0);
265 
266                 const GraphicObject&    rGraphicObject = getGraphicObject();
267                 const Graphic           aTransformedGraphic(rGraphicObject.GetTransformedGraphic(&aSuppressGraphicAttr));
268 
269                 switch(aTransformedGraphic.GetType())
270                 {
271                     case GRAPHIC_BITMAP :
272                     {
273                         if(aTransformedGraphic.IsAnimated())
274                         {
275                             // prepare animation data
276                             animatedBitmapExPreparator aData(aTransformedGraphic);
277 
278                             if(aData.count())
279                             {
280                                 // create sub-primitives for animated bitmap and the needed animation loop
281                                 animation::AnimationEntryLoop aAnimationLoop(aData.loopCount() ? aData.loopCount() : 0xffff);
282                                 Primitive2DSequence aBitmapPrimitives(aData.count());
283 
284                                 for(sal_uInt32 a(0L); a < aData.count(); a++)
285                                 {
286                                     animation::AnimationEntryFixed aTime((double)aData.stepTime(a), (double)a / (double)aData.count());
287                                     aAnimationLoop.append(aTime);
288                                     const Primitive2DReference xRef(new BitmapPrimitive2D(aData.stepBitmapEx(a), aTransform));
289                                     aBitmapPrimitives[a] = xRef;
290                                 }
291 
292                                 // prepare animation list
293                                 animation::AnimationEntryList aAnimationList;
294                                 aAnimationList.append(aAnimationLoop);
295 
296                                 // create and add animated switch primitive
297                                 xPrimitive = Primitive2DReference(new AnimatedSwitchPrimitive2D(aAnimationList, aBitmapPrimitives, false));
298                             }
299                         }
300                         else
301                         {
302                             xPrimitive = Primitive2DReference(new BitmapPrimitive2D(aTransformedGraphic.GetBitmapEx(), aTransform));
303                         }
304 
305                         break;
306                     }
307 
308                     case GRAPHIC_GDIMETAFILE :
309                     {
310 #ifdef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE
311                         static bool bDoTest(false);
312 
313                         if(bDoTest)
314                         {
315                             // All this is/was test code for testing MetafilePrimitive2D::create2DDecomposition
316                             // extensively. It may be needed again when diverse actions need debugging, so i leave
317                             // it in here, but take it out using USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE.
318                             // Use it by compiling with the code, insert any DrawObject, convert to Metafile. The
319                             // debugger will then stop here (when breakpoint set, of course). You may enter single
320                             // parts of actions and/or change to true what You want to check.
321                             GDIMetaFile aMtf;
322                             VirtualDevice aOut;
323                             const basegfx::B2DRange aRange(getB2DRange(rViewInformation));
324                             const Rectangle aRectangle(
325                                 basegfx::fround(aRange.getMinX()), basegfx::fround(aRange.getMinY()),
326                                 basegfx::fround(aRange.getMaxX()), basegfx::fround(aRange.getMaxY()));
327                             const Point aOrigin(aRectangle.TopLeft());
328                             const Fraction aScaleX(aRectangle.getWidth());
329                             const Fraction aScaleY(aRectangle.getHeight());
330                             MapMode aMapMode(MAP_100TH_MM, aOrigin, aScaleX, aScaleY);
331 
332                             Size aDummySize(2, 2);
333                             aOut.SetOutputSizePixel(aDummySize);
334                             aOut.EnableOutput(FALSE);
335                             aOut.SetMapMode(aMapMode);
336 
337                             aMtf.Clear();
338                             aMtf.Record(&aOut);
339 
340                             const Fraction aNeutralFraction(1, 1);
341                             const MapMode aRelativeMapMode(
342                                 MAP_RELATIVE,
343                                 Point(-aRectangle.Left(), -aRectangle.Top()),
344                                 aNeutralFraction, aNeutralFraction);
345                             aOut.SetMapMode(aRelativeMapMode);
346 
347                             if(false)
348                             {
349                                 const sal_Int32 nHor(aRectangle.getWidth() / 4);
350                                 const sal_Int32 nVer(aRectangle.getHeight() / 4);
351                                 const Rectangle aCenteredRectangle(
352                                     aRectangle.Left() + nHor, aRectangle.Top() + nVer,
353                                     aRectangle.Right() - nHor, aRectangle.Bottom() - nVer);
354                                 aOut.SetClipRegion(aCenteredRectangle);
355                             }
356 
357                             if(false)
358                             {
359                                 const Rectangle aRightRectangle(aRectangle.TopCenter(), aRectangle.BottomRight());
360                                 aOut.IntersectClipRegion(aRightRectangle);
361                             }
362 
363                             if(false)
364                             {
365                                 const Rectangle aRightRectangle(aRectangle.TopCenter(), aRectangle.BottomRight());
366                                 const Rectangle aBottomRectangle(aRectangle.LeftCenter(), aRectangle.BottomRight());
367                                 Region aRegion(aRightRectangle);
368                                 aRegion.Intersect(aBottomRectangle);
369                                 aOut.IntersectClipRegion(aRegion);
370                             }
371 
372                             if(false)
373                             {
374                                 const sal_Int32 nHor(aRectangle.getWidth() / 10);
375                                 const sal_Int32 nVer(aRectangle.getHeight() / 10);
376                                 aOut.MoveClipRegion(nHor, nVer);
377                             }
378 
379                             if(false)
380                             {
381                                 Wallpaper aWallpaper(Color(COL_BLACK));
382                                 aOut.DrawWallpaper(aRectangle, aWallpaper);
383                             }
384 
385                             if(false)
386                             {
387                                 Wallpaper aWallpaper(Gradient(GRADIENT_LINEAR, Color(COL_RED), Color(COL_GREEN)));
388                                 aOut.DrawWallpaper(aRectangle, aWallpaper);
389                             }
390 
391                             if(false)
392                             {
393                                 SvFileStream aRead((const String&)String(ByteString( "c:\\test.png" ), RTL_TEXTENCODING_UTF8), STREAM_READ);
394                                 vcl::PNGReader aPNGReader(aRead);
395                                 BitmapEx aBitmapEx(aPNGReader.Read());
396                                 Wallpaper aWallpaper(aBitmapEx);
397                                 aOut.DrawWallpaper(aRectangle, aWallpaper);
398                             }
399 
400                             if(false)
401                             {
402                                 const double fHor(aRectangle.getWidth());
403                                 const double fVer(aRectangle.getHeight());
404                                 Color aColor(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0));
405 
406                                 for(sal_uInt32 a(0); a < 5000; a++)
407                                 {
408                                     const Point aPoint(
409                                         aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
410                                         aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
411 
412                                     if(!(a % 3))
413                                     {
414                                         aColor = Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0));
415                                     }
416 
417                                     aOut.DrawPixel(aPoint, aColor);
418                                 }
419                             }
420 
421                             if(false)
422                             {
423                                 const double fHor(aRectangle.getWidth());
424                                 const double fVer(aRectangle.getHeight());
425 
426                                 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
427                                 aOut.SetFillColor();
428 
429                                 for(sal_uInt32 a(0); a < 5000; a++)
430                                 {
431                                     const Point aPoint(
432                                         aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
433                                         aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
434                                     aOut.DrawPixel(aPoint);
435                                 }
436                             }
437 
438                             if(false)
439                             {
440                                 const double fHor(aRectangle.getWidth());
441                                 const double fVer(aRectangle.getHeight());
442 
443                                 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
444                                 aOut.SetFillColor();
445 
446                                 Point aStart(
447                                     aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
448                                     aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
449                                 Point aStop(
450                                     aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
451                                     aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
452 
453                                 LineInfo aLineInfo(LINE_SOLID, basegfx::fround(fHor / 50.0));
454                                 bool bUseLineInfo(false);
455 
456                                 for(sal_uInt32 a(0); a < 20; a++)
457                                 {
458                                     if(!(a%6))
459                                     {
460                                         bUseLineInfo = !bUseLineInfo;
461                                     }
462 
463                                     if(!(a%4))
464                                     {
465                                         aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
466                                     }
467 
468                                     if(a%3)
469                                     {
470                                         aStart = aStop;
471                                         aStop = Point(
472                                             aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
473                                             aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
474                                     }
475                                     else
476                                     {
477                                         aStart = Point(
478                                             aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
479                                             aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
480                                         aStop = Point(
481                                             aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
482                                             aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
483                                     }
484 
485                                     if(bUseLineInfo)
486                                     {
487                                         aOut.DrawLine(aStart, aStop, aLineInfo);
488                                     }
489                                     else
490                                     {
491                                         aOut.DrawLine(aStart, aStop);
492                                     }
493                                 }
494                             }
495 
496                             if(false)
497                             {
498                                 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
499                                 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
500                                 aOut.DrawRect(aRectangle);
501                             }
502 
503                             if(false)
504                             {
505                                 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
506                                 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
507                                 const sal_uInt32 nHor(aRectangle.getWidth() / 10);
508                                 const sal_uInt32 nVer(aRectangle.getHeight() / 10);
509                                 aOut.DrawRect(aRectangle, nHor, nVer);
510                             }
511 
512                             if(false)
513                             {
514                                 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
515                                 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
516                                 aOut.DrawEllipse(aRectangle);
517                             }
518 
519                             if(false)
520                             {
521                                 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
522                                 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
523                                 aOut.DrawArc(aRectangle, aRectangle.TopLeft(), aRectangle.BottomCenter());
524                             }
525 
526                             if(false)
527                             {
528                                 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
529                                 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
530                                 aOut.DrawPie(aRectangle, aRectangle.TopLeft(), aRectangle.BottomCenter());
531                             }
532 
533                             if(false)
534                             {
535                                 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
536                                 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
537                                 aOut.DrawChord(aRectangle, aRectangle.TopLeft(), aRectangle.BottomCenter());
538                             }
539 
540                             if(false)
541                             {
542                                 const double fHor(aRectangle.getWidth());
543                                 const double fVer(aRectangle.getHeight());
544 
545                                 for(sal_uInt32 b(0); b < 5; b++)
546                                 {
547                                     const sal_uInt32 nCount(basegfx::fround(rand() * (20 / 32767.0)));
548                                     const bool bClose(basegfx::fround(rand() / 32767.0));
549                                     Polygon aPolygon(nCount + (bClose ? 1 : 0));
550 
551                                     for(sal_uInt32 a(0); a < nCount; a++)
552                                     {
553                                         const Point aPoint(
554                                             aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
555                                             aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
556                                         aPolygon[a] = aPoint;
557                                     }
558 
559                                     if(bClose)
560                                     {
561                                         aPolygon[aPolygon.GetSize() - 1] = aPolygon[0];
562                                     }
563 
564                                     aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
565                                     aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
566 
567                                     if(!(b%2))
568                                     {
569                                         const LineInfo aLineInfo(LINE_SOLID, basegfx::fround(fHor / 50.0));
570                                         aOut.DrawPolyLine(aPolygon, aLineInfo);
571                                     }
572                                     else
573                                     {
574                                         aOut.DrawPolyLine(aPolygon);
575                                     }
576                                 }
577                             }
578 
579                             if(false)
580                             {
581                                 const double fHor(aRectangle.getWidth());
582                                 const double fVer(aRectangle.getHeight());
583 
584                                 for(sal_uInt32 b(0); b < 5; b++)
585                                 {
586                                     const sal_uInt32 nCount(basegfx::fround(rand() * (20 / 32767.0)));
587                                     const bool bClose(basegfx::fround(rand() / 32767.0));
588                                     Polygon aPolygon(nCount + (bClose ? 1 : 0));
589 
590                                     for(sal_uInt32 a(0); a < nCount; a++)
591                                     {
592                                         const Point aPoint(
593                                             aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
594                                             aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
595                                         aPolygon[a] = aPoint;
596                                     }
597 
598                                     if(bClose)
599                                     {
600                                         aPolygon[aPolygon.GetSize() - 1] = aPolygon[0];
601                                     }
602 
603                                     aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
604                                     aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
605                                     aOut.DrawPolygon(aPolygon);
606                                 }
607                             }
608 
609                             if(false)
610                             {
611                                 const double fHor(aRectangle.getWidth());
612                                 const double fVer(aRectangle.getHeight());
613                                 PolyPolygon aPolyPolygon;
614 
615                                 for(sal_uInt32 b(0); b < 3; b++)
616                                 {
617                                     const sal_uInt32 nCount(basegfx::fround(rand() * (6 / 32767.0)));
618                                     const bool bClose(basegfx::fround(rand() / 32767.0));
619                                     Polygon aPolygon(nCount + (bClose ? 1 : 0));
620 
621                                     for(sal_uInt32 a(0); a < nCount; a++)
622                                     {
623                                         const Point aPoint(
624                                             aRectangle.Left() + basegfx::fround(rand() * (fHor / 32767.0)),
625                                             aRectangle.Top() + basegfx::fround(rand() * (fVer / 32767.0)));
626                                         aPolygon[a] = aPoint;
627                                     }
628 
629                                     if(bClose)
630                                     {
631                                         aPolygon[aPolygon.GetSize() - 1] = aPolygon[0];
632                                     }
633 
634                                     aPolyPolygon.Insert(aPolygon);
635                                 }
636 
637                                 aOut.SetLineColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
638                                 aOut.SetFillColor(Color(basegfx::BColor(rand() / 32767.0, rand() / 32767.0, rand() / 32767.0)));
639                                 aOut.DrawPolyPolygon(aPolyPolygon);
640                             }
641 
642                             if(false)
643                             {
644                                 SvFileStream aRead((const String&)String(ByteString( "c:\\test.png" ), RTL_TEXTENCODING_UTF8), STREAM_READ);
645                                 vcl::PNGReader aPNGReader(aRead);
646                                 BitmapEx aBitmapEx(aPNGReader.Read());
647                                 aOut.DrawBitmapEx(aRectangle.TopLeft(), aBitmapEx);
648                             }
649 
650                             if(false)
651                             {
652                                 SvFileStream aRead((const String&)String(ByteString( "c:\\test.png" ), RTL_TEXTENCODING_UTF8), STREAM_READ);
653                                 vcl::PNGReader aPNGReader(aRead);
654                                 BitmapEx aBitmapEx(aPNGReader.Read());
655                                 aOut.DrawBitmapEx(aRectangle.TopLeft(), aRectangle.GetSize(), aBitmapEx);
656                             }
657 
658                             if(false)
659                             {
660                                 SvFileStream aRead((const String&)String(ByteString( "c:\\test.png" ), RTL_TEXTENCODING_UTF8), STREAM_READ);
661                                 vcl::PNGReader aPNGReader(aRead);
662                                 BitmapEx aBitmapEx(aPNGReader.Read());
663                                 const Size aSizePixel(aBitmapEx.GetSizePixel());
664                                 aOut.DrawBitmapEx(
665                                     aRectangle.TopLeft(),
666                                     aRectangle.GetSize(),
667                                     Point(0, 0),
668                                     Size(aSizePixel.Width() /2, aSizePixel.Height() / 2),
669                                     aBitmapEx);
670                             }
671 
672                             if(false)
673                             {
674                                 const double fHor(aRectangle.getWidth());
675                                 const double fVer(aRectangle.getHeight());
676                                 const Point aPointA(
677                                     aRectangle.Left() + basegfx::fround(fHor * 0.2),
678                                     aRectangle.Top() + basegfx::fround(fVer * 0.3));
679                                 const Point aPointB(
680                                     aRectangle.Left() + basegfx::fround(fHor * 0.2),
681                                     aRectangle.Top() + basegfx::fround(fVer * 0.5));
682                                 const Point aPointC(
683                                     aRectangle.Left() + basegfx::fround(fHor * 0.2),
684                                     aRectangle.Top() + basegfx::fround(fVer * 0.7));
685                                 const String aText(ByteString("Hello, World!"), RTL_TEXTENCODING_UTF8);
686 
687                                 const String aFontName(ByteString("Comic Sans MS"), RTL_TEXTENCODING_UTF8);
688                                 Font aFont(aFontName, Size(0, 1000));
689                                 aFont.SetAlign(ALIGN_BASELINE);
690                                 aFont.SetColor(COL_RED);
691                                 //sal_Int32* pDXArray = new sal_Int32[aText.Len()];
692 
693                                 aFont.SetOutline(true);
694                                 aOut.SetFont(aFont);
695                                 aOut.DrawText(aPointA, aText, 0, aText.Len());
696 
697                                 aFont.SetShadow(true);
698                                 aOut.SetFont(aFont);
699                                 aOut.DrawText(aPointB, aText, 0, aText.Len());
700 
701                                 aFont.SetRelief(RELIEF_EMBOSSED);
702                                 aOut.SetFont(aFont);
703                                 aOut.DrawText(aPointC, aText, 0, aText.Len());
704 
705                                 //delete pDXArray;
706                             }
707 
708                             if(false)
709                             {
710                                 const double fHor(aRectangle.getWidth());
711                                 const double fVer(aRectangle.getHeight());
712                                 const Point aPointA(
713                                     aRectangle.Left() + basegfx::fround(fHor * 0.2),
714                                     aRectangle.Top() + basegfx::fround(fVer * 0.3));
715                                 const Point aPointB(
716                                     aRectangle.Left() + basegfx::fround(fHor * 0.2),
717                                     aRectangle.Top() + basegfx::fround(fVer * 0.5));
718                                 const Point aPointC(
719                                     aRectangle.Left() + basegfx::fround(fHor * 0.2),
720                                     aRectangle.Top() + basegfx::fround(fVer * 0.7));
721                                 const String aText(ByteString("Hello, World!"), RTL_TEXTENCODING_UTF8);
722 
723                                 const String aFontName(ByteString("Comic Sans MS"), RTL_TEXTENCODING_UTF8);
724                                 Font aFont(aFontName, Size(0, 1000));
725                                 aFont.SetAlign(ALIGN_BASELINE);
726                                 aFont.SetColor(COL_RED);
727 
728                                 aOut.SetFont(aFont);
729                                 const sal_Int32 nWidth(aOut.GetTextWidth(aText, 0, aText.Len()));
730                                 aOut.DrawText(aPointA, aText, 0, aText.Len());
731                                 aOut.DrawTextLine(aPointA, nWidth, STRIKEOUT_SINGLE, UNDERLINE_SINGLE, UNDERLINE_SMALLWAVE);
732                                 aOut.DrawTextLine(aPointB, nWidth, STRIKEOUT_SINGLE, UNDERLINE_SINGLE, UNDERLINE_SMALLWAVE);
733                                 aOut.DrawTextLine(aPointC, nWidth, STRIKEOUT_SINGLE, UNDERLINE_SINGLE, UNDERLINE_SMALLWAVE);
734                             }
735 
736                             aMtf.Stop();
737                             aMtf.WindStart();
738                             aMtf.SetPrefMapMode(MapMode(MAP_100TH_MM));
739                             aMtf.SetPrefSize(Size(aRectangle.getWidth(), aRectangle.getHeight()));
740 
741                             xPrimitive = Primitive2DReference(
742                                 new MetafilePrimitive2D(
743                                     aTransform,
744                                     aMtf));
745                         }
746                         else
747                         {
748 #endif // USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE
749                             // create MetafilePrimitive2D
750                             const GDIMetaFile& rMetafile = aTransformedGraphic.GetGDIMetaFile();
751 
752                             if( aTransformedGraphic.IsRenderGraphic() )
753                             {
754                                 xPrimitive = Primitive2DReference(
755                                     new RenderGraphicPrimitive2D(
756                                         static_cast< MetaRenderGraphicAction* >(rMetafile.GetAction(0))->GetRenderGraphic(),
757                                         aTransform));
758                             }
759                             else
760                             {
761                                 xPrimitive = Primitive2DReference(
762                                     new MetafilePrimitive2D(
763                                         aTransform,
764                                         rMetafile));
765 
766                                 // #i100357# find out if clipping is needed for this primitive. Unfortunately,
767                                 // there exist Metafiles who's content is bigger than the proposed PrefSize set
768                                 // at them. This is an error, but we need to work around this
769                                 const Size aMetaFilePrefSize(rMetafile.GetPrefSize());
770                                 const Size aMetaFileRealSize(
771                                     const_cast< GDIMetaFile& >(rMetafile).GetBoundRect(
772                                         *Application::GetDefaultDevice()).GetSize());
773 
774                                 if(aMetaFileRealSize.getWidth() > aMetaFilePrefSize.getWidth()
775                                     || aMetaFileRealSize.getHeight() > aMetaFilePrefSize.getHeight())
776                                 {
777                                     // clipping needed. Embed to MaskPrimitive2D. Create childs and mask polygon
778                                     const primitive2d::Primitive2DSequence aChildContent(&xPrimitive, 1);
779                                     basegfx::B2DPolygon aMaskPolygon(basegfx::tools::createUnitPolygon());
780                                     aMaskPolygon.transform(aTransform);
781 
782                                     xPrimitive = Primitive2DReference(
783                                         new MaskPrimitive2D(
784                                             basegfx::B2DPolyPolygon(aMaskPolygon),
785                                             aChildContent));
786                                 }
787                             }
788 #ifdef USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE
789                         }
790 #endif // USE_DEBUG_CODE_TO_TEST_METAFILE_DECOMPOSE
791 
792                         break;
793                     }
794 
795                     default:
796                     {
797                         // nothing to create
798                         break;
799                     }
800                 }
801 
802                 if(xPrimitive.is())
803                 {
804                     // check for cropping
805                     if(getGraphicAttr().IsCropped())
806                     {
807                         // decompose to get current pos and size
808                         basegfx::B2DVector aScale, aTranslate;
809                         double fRotate, fShearX;
810                         getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
811 
812                         // create ranges. The current object range is just scale and translate
813                         const basegfx::B2DRange aCurrent(
814                             aTranslate.getX(), aTranslate.getY(),
815                             aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY());
816 
817                         // calculate scalings between real image size and logic object size. This
818                         // is necessary since the crop values are relative to original bitmap size
819                         double fFactorX(1.0);
820                         double fFactorY(1.0);
821 
822                         {
823                             const MapMode aMapMode100thmm(MAP_100TH_MM);
824                             Size aBitmapSize(rGraphicObject.GetPrefSize());
825 
826                             // #i95968# better support PrefMapMode; special for MAP_PIXEL was missing
827                             if(MAP_PIXEL == rGraphicObject.GetPrefMapMode().GetMapUnit())
828                             {
829                                 aBitmapSize = Application::GetDefaultDevice()->PixelToLogic(aBitmapSize, aMapMode100thmm);
830                             }
831                             else
832                             {
833                                 aBitmapSize = Application::GetDefaultDevice()->LogicToLogic(aBitmapSize, rGraphicObject.GetPrefMapMode(), aMapMode100thmm);
834                             }
835 
836                             const double fDivX(aBitmapSize.Width() - getGraphicAttr().GetLeftCrop() - getGraphicAttr().GetRightCrop());
837                             const double fDivY(aBitmapSize.Height() - getGraphicAttr().GetTopCrop() - getGraphicAttr().GetBottomCrop());
838 
839                             if(!basegfx::fTools::equalZero(fDivX))
840                             {
841                                 fFactorX = aScale.getX() / fDivX;
842                             }
843 
844                             if(!basegfx::fTools::equalZero(fDivY))
845                             {
846                                 fFactorY = aScale.getY() / fDivY;
847                             }
848                         }
849 
850                         // Create cropped range, describes the bounds of the original graphic
851                         basegfx::B2DRange aCropped;
852                         aCropped.expand(aCurrent.getMinimum() - basegfx::B2DPoint(getGraphicAttr().GetLeftCrop() * fFactorX, getGraphicAttr().GetTopCrop() * fFactorY));
853                         aCropped.expand(aCurrent.getMaximum() + basegfx::B2DPoint(getGraphicAttr().GetRightCrop() * fFactorX, getGraphicAttr().GetBottomCrop() * fFactorY));
854 
855                         if(aCropped.isEmpty())
856                         {
857                             // nothing to add since cropped bitmap is completely empty
858                             // xPrimitive will not be used
859                         }
860                         else
861                         {
862                             // build new object transformation for transform primitive which contains xPrimitive
863                             basegfx::B2DHomMatrix aNewObjectTransform(getTransform());
864                             aNewObjectTransform.invert();
865                             aNewObjectTransform = basegfx::tools::createScaleTranslateB2DHomMatrix(
866                                 aCropped.getWidth(), aCropped.getHeight(),
867                                 aCropped.getMinX() - aCurrent.getMinX(), aCropped.getMinY() - aCurrent.getMinY())
868                                 * aNewObjectTransform;
869 
870                             // add shear, rotate and translate using combined matrix to speedup
871                             const basegfx::B2DHomMatrix aCombinedMatrix(basegfx::tools::createShearXRotateTranslateB2DHomMatrix(
872                                 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
873                             aNewObjectTransform = aCombinedMatrix * aNewObjectTransform;
874 
875                             // prepare TransformPrimitive2D with xPrimitive
876                             const Primitive2DReference xTransformPrimitive(new TransformPrimitive2D(aNewObjectTransform, Primitive2DSequence(&xPrimitive, 1L)));
877 
878                             if(aCurrent.isInside(aCropped))
879                             {
880                                 // cropped just got smaller, no need to really use a mask. Add to destination directly
881                                 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xTransformPrimitive);
882                             }
883                             else
884                             {
885                                 // cropped got bigger, mask it with original object's bounds
886                                 basegfx::B2DPolyPolygon aMaskPolyPolygon(basegfx::tools::createUnitPolygon());
887                                 aMaskPolyPolygon.transform(getTransform());
888 
889                                 // create maskPrimitive with aMaskPolyPolygon and aMaskContentVector
890                                 const Primitive2DReference xRefB(new MaskPrimitive2D(aMaskPolyPolygon, Primitive2DSequence(&xTransformPrimitive, 1L)));
891                                 appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xRefB);
892                             }
893                         }
894                     }
895                     else
896                     {
897                         // add to decomposition
898                         appendPrimitive2DReferenceToPrimitive2DSequence(aRetval, xPrimitive);
899                     }
900                 }
901             }
902 
903             return aRetval;
904         }
905 
906         GraphicPrimitive2D::GraphicPrimitive2D(
907             const basegfx::B2DHomMatrix& rTransform,
908             const GraphicObject& rGraphicObject,
909             const GraphicAttr& rGraphicAttr)
910         :   BufferedDecompositionPrimitive2D(),
911             maTransform(rTransform),
912             maGraphicObject(rGraphicObject),
913             maGraphicAttr(rGraphicAttr)
914         {
915         }
916 
917         GraphicPrimitive2D::GraphicPrimitive2D(
918             const basegfx::B2DHomMatrix& rTransform,
919             const GraphicObject& rGraphicObject)
920         :   BufferedDecompositionPrimitive2D(),
921             maTransform(rTransform),
922             maGraphicObject(rGraphicObject),
923             maGraphicAttr()
924         {
925         }
926 
927         bool GraphicPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
928         {
929             if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
930             {
931                 const GraphicPrimitive2D& rCompare = (GraphicPrimitive2D&)rPrimitive;
932 
933                 return (getTransform() == rCompare.getTransform()
934                     && getGraphicObject() == rCompare.getGraphicObject()
935                     && getGraphicAttr() == rCompare.getGraphicAttr());
936             }
937 
938             return false;
939         }
940 
941         basegfx::B2DRange GraphicPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
942         {
943             basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
944             aRetval.transform(getTransform());
945             return aRetval;
946         }
947 
948         // provide unique ID
949         ImplPrimitrive2DIDBlock(GraphicPrimitive2D, PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D)
950 
951     } // end of namespace primitive2d
952 } // end of namespace drawinglayer
953 
954 //////////////////////////////////////////////////////////////////////////////
955 // eof
956