1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_vcl.hxx"
24
25 #include <vcl/gdimetafiletools.hxx>
26 #include <vcl/metaact.hxx>
27 #include <basegfx/polygon/b2dpolygonclipper.hxx>
28 #include <basegfx/matrix/b2dhommatrixtools.hxx>
29 #include <basegfx/polygon/b2dpolypolygontools.hxx>
30 #include <basegfx/polygon/b2dpolygontools.hxx>
31 #include <vcl/virdev.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vcl/graphictools.hxx>
34
35 //////////////////////////////////////////////////////////////////////////////
36 // helpers
37
38 namespace
39 {
handleGeometricContent(const basegfx::B2DPolyPolygon & rClip,const basegfx::B2DPolyPolygon & rSource,GDIMetaFile & rTarget,bool bStroke)40 bool handleGeometricContent(
41 const basegfx::B2DPolyPolygon& rClip,
42 const basegfx::B2DPolyPolygon& rSource,
43 GDIMetaFile& rTarget,
44 bool bStroke)
45 {
46 if(rSource.count() && rClip.count())
47 {
48 const basegfx::B2DPolyPolygon aResult(
49 basegfx::tools::clipPolyPolygonOnPolyPolygon(
50 rSource,
51 rClip,
52 true, // inside
53 bStroke));
54
55 if(aResult.count())
56 {
57 if(aResult == rSource)
58 {
59 // not clipped, but inside. Add original
60 return false;
61 }
62 else
63 {
64 // add clipped geometry
65 if(bStroke)
66 {
67 for(sal_uInt32 a(0); a < aResult.count(); a++)
68 {
69 rTarget.AddAction(
70 new MetaPolyLineAction(
71 Polygon(aResult.getB2DPolygon(a))));
72 }
73 }
74 else
75 {
76 rTarget.AddAction(
77 new MetaPolyPolygonAction(
78 PolyPolygon(aResult)));
79 }
80 }
81 }
82 }
83
84 return true;
85 }
86
handleGradientContent(const basegfx::B2DPolyPolygon & rClip,const basegfx::B2DPolyPolygon & rSource,const Gradient & rGradient,GDIMetaFile & rTarget)87 bool handleGradientContent(
88 const basegfx::B2DPolyPolygon& rClip,
89 const basegfx::B2DPolyPolygon& rSource,
90 const Gradient& rGradient,
91 GDIMetaFile& rTarget)
92 {
93 if(rSource.count() && rClip.count())
94 {
95 const basegfx::B2DPolyPolygon aResult(
96 basegfx::tools::clipPolyPolygonOnPolyPolygon(
97 rSource,
98 rClip,
99 true, // inside
100 false)); // stroke
101
102 if(aResult.count())
103 {
104 if(aResult == rSource)
105 {
106 // not clipped, but inside. Add original
107 return false;
108 }
109 else
110 {
111 // add clipped geometry
112 rTarget.AddAction(
113 new MetaGradientExAction(
114 PolyPolygon(aResult),
115 rGradient));
116 }
117 }
118 }
119
120 return true;
121 }
122
handleBitmapContent(const basegfx::B2DPolyPolygon & rClip,const Point & rPoint,const Size & rSize,const BitmapEx & rBitmapEx,GDIMetaFile & rTarget)123 bool handleBitmapContent(
124 const basegfx::B2DPolyPolygon& rClip,
125 const Point& rPoint,
126 const Size& rSize,
127 const BitmapEx& rBitmapEx,
128 GDIMetaFile& rTarget)
129 {
130 if(!rSize.Width() || !rSize.Height() || rBitmapEx.IsEmpty())
131 {
132 // bitmap or size is empty
133 return true;
134 }
135
136 const basegfx::B2DRange aLogicBitmapRange(
137 rPoint.X(), rPoint.Y(),
138 rPoint.X() + rSize.Width(), rPoint.Y() + rSize.Height());
139 const basegfx::B2DPolyPolygon aClipOfBitmap(
140 basegfx::tools::clipPolyPolygonOnRange(
141 rClip,
142 aLogicBitmapRange,
143 true,
144 false)); // stroke
145
146 if(!aClipOfBitmap.count())
147 {
148 // outside clip region
149 return true;
150 }
151
152 // inside or overlapping. Use area to find out if it is completely
153 // covering (inside) or overlapping
154 const double fClipArea(basegfx::tools::getArea(aClipOfBitmap));
155 const double fBitmapArea(
156 aLogicBitmapRange.getWidth() * aLogicBitmapRange.getWidth() +
157 aLogicBitmapRange.getHeight() * aLogicBitmapRange.getHeight());
158 const double fFactor(fClipArea / fBitmapArea);
159
160 if(basegfx::fTools::more(fFactor, 1.0 - 0.001))
161 {
162 // completely covering (with 0.1% tolerance)
163 return false;
164 }
165
166 // needs clipping (with 0.1% tolerance). Prepare VirtualDevice
167 // in pixel mode for alpha channel painting (black is transparent,
168 // white to paint 100% opacity)
169 const Size aSizePixel(rBitmapEx.GetSizePixel());
170 VirtualDevice aVDev;
171
172 aVDev.SetOutputSizePixel(aSizePixel);
173 aVDev.EnableMapMode(false);
174 aVDev.SetFillColor(COL_WHITE);
175 aVDev.SetLineColor();
176
177 if(rBitmapEx.IsTransparent())
178 {
179 // use given alpha channel
180 aVDev.DrawBitmap(Point(0, 0), rBitmapEx.GetAlpha().GetBitmap());
181 }
182 else
183 {
184 // reset alpha channel
185 aVDev.SetBackground(Wallpaper(Color(COL_BLACK)));
186 aVDev.Erase();
187 }
188
189 // transform polygon from clipping to pixel coordinates
190 basegfx::B2DPolyPolygon aPixelPoly(aClipOfBitmap);
191 basegfx::B2DHomMatrix aTransform;
192
193 aTransform.translate(-aLogicBitmapRange.getMinX(), -aLogicBitmapRange.getMinY());
194 aTransform.scale(
195 static_cast< double >(aSizePixel.Width()) / aLogicBitmapRange.getWidth(),
196 static_cast< double >(aSizePixel.Height()) / aLogicBitmapRange.getHeight());
197 aPixelPoly.transform(aTransform);
198
199 // to fill the non-covered parts, use the Xor fill rule of
200 // PolyPolygon painting. Start with a all-covering polygon and
201 // add the clip polygon one
202 basegfx::B2DPolyPolygon aInvertPixelPoly;
203
204 aInvertPixelPoly.append(
205 basegfx::tools::createPolygonFromRect(
206 basegfx::B2DRange(
207 0.0, 0.0,
208 aSizePixel.Width(), aSizePixel.Height())));
209 aInvertPixelPoly.append(aPixelPoly);
210
211 // paint as alpha
212 aVDev.DrawPolyPolygon(aInvertPixelPoly);
213
214 // get created alpha mask and set defaults
215 AlphaMask aAlpha(
216 aVDev.GetBitmap(
217 Point(0, 0),
218 aSizePixel));
219
220 aAlpha.SetPrefSize(rBitmapEx.GetPrefSize());
221 aAlpha.SetPrefMapMode(rBitmapEx.GetPrefMapMode());
222
223 // add new action replacing the old one
224 rTarget.AddAction(
225 new MetaBmpExScaleAction(
226 Point(
227 basegfx::fround(aLogicBitmapRange.getMinX()),
228 basegfx::fround(aLogicBitmapRange.getMinY())),
229 Size(
230 basegfx::fround(aLogicBitmapRange.getWidth()),
231 basegfx::fround(aLogicBitmapRange.getHeight())),
232 BitmapEx(rBitmapEx.GetBitmap(), aAlpha)));
233
234 return true;
235 }
236
addSvtGraphicStroke(const SvtGraphicStroke & rStroke,GDIMetaFile & rTarget)237 void addSvtGraphicStroke(const SvtGraphicStroke& rStroke, GDIMetaFile& rTarget)
238 {
239 // write SvtGraphicFill
240 SvMemoryStream aMemStm;
241 aMemStm << rStroke;
242 rTarget.AddAction(
243 new MetaCommentAction(
244 "XPATHSTROKE_SEQ_BEGIN",
245 0,
246 static_cast< const sal_uInt8* >(aMemStm.GetData()),
247 aMemStm.Seek(STREAM_SEEK_TO_END)));
248 }
249
addSvtGraphicFill(const SvtGraphicFill & rFilling,GDIMetaFile & rTarget)250 void addSvtGraphicFill(const SvtGraphicFill &rFilling, GDIMetaFile& rTarget)
251 {
252 // write SvtGraphicFill
253 SvMemoryStream aMemStm;
254 aMemStm << rFilling;
255 rTarget.AddAction(
256 new MetaCommentAction(
257 "XPATHFILL_SEQ_BEGIN",
258 0,
259 static_cast< const sal_uInt8* >(aMemStm.GetData()),
260 aMemStm.Seek(STREAM_SEEK_TO_END)));
261 }
262 } // end of anonymous namespace
263
264 //////////////////////////////////////////////////////////////////////////////
265 // #121267# Tooling to internally clip geometry against internal clip regions
266
clipMetafileContentAgainstOwnRegions(GDIMetaFile & rSource)267 void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource)
268 {
269 const sal_uLong nObjCount(rSource.GetActionCount());
270
271 if(!nObjCount)
272 {
273 return;
274 }
275
276 // prepare target data container and push/pop stack data
277 GDIMetaFile aTarget;
278 bool bChanged(false);
279 std::vector< basegfx::B2DPolyPolygon > aClips;
280 std::vector< sal_uInt16 > aPushFlags;
281 std::vector< MapMode > aMapModes;
282
283 // start with empty region
284 aClips.push_back(basegfx::B2DPolyPolygon());
285
286 // start with default MapMode (MAP_PIXEL)
287 aMapModes.push_back(MapMode());
288
289 for(sal_uLong i(0); i < nObjCount; ++i)
290 {
291 const MetaAction* pAction(rSource.GetAction(i));
292 const sal_uInt16 nType(pAction->GetType());
293 bool bDone(false);
294
295 // basic operation takes care of clipregion actions (four) and push/pop of these
296 // to steer the currently set clip region. There *is* an active
297 // clip region when (aClips.size() && aClips.back().count()), see
298 // below
299 switch(nType)
300 {
301 case META_CLIPREGION_ACTION :
302 {
303 const MetaClipRegionAction* pA = static_cast< const MetaClipRegionAction* >(pAction);
304
305 if(pA->IsClipping())
306 {
307 const Region& rRegion = pA->GetRegion();
308 const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
309
310 aClips.back() = aNewClip;
311 }
312 else
313 {
314 aClips.back() = basegfx::B2DPolyPolygon();
315 }
316
317 break;
318 }
319
320 case META_ISECTRECTCLIPREGION_ACTION :
321 {
322 const MetaISectRectClipRegionAction* pA = static_cast< const MetaISectRectClipRegionAction* >(pAction);
323 const Rectangle& rRect = pA->GetRect();
324
325 if(!rRect.IsEmpty() && aClips.size() && aClips.back().count())
326 {
327 const basegfx::B2DRange aClipRange(
328 rRect.Left(), rRect.Top(),
329 rRect.Right(), rRect.Bottom());
330
331 aClips.back() = basegfx::tools::clipPolyPolygonOnRange(
332 aClips.back(),
333 aClipRange,
334 true, // inside
335 false); // stroke
336 }
337 break;
338 }
339
340 case META_ISECTREGIONCLIPREGION_ACTION :
341 {
342 const MetaISectRegionClipRegionAction* pA = static_cast< const MetaISectRegionClipRegionAction* >(pAction);
343 const Region& rRegion = pA->GetRegion();
344
345 if(!rRegion.IsEmpty() && aClips.size() && aClips.back().count())
346 {
347 const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
348
349 aClips.back() = basegfx::tools::clipPolyPolygonOnPolyPolygon(
350 aClips.back(),
351 aNewClip,
352 true, // inside
353 false); // stroke
354 }
355 break;
356 }
357
358 case META_MOVECLIPREGION_ACTION :
359 {
360 const MetaMoveClipRegionAction* pA = static_cast< const MetaMoveClipRegionAction* >(pAction);
361 const long aHorMove(pA->GetHorzMove());
362 const long aVerMove(pA->GetVertMove());
363
364 if((aHorMove || aVerMove) && aClips.size() && aClips.back().count())
365 {
366 aClips.back().transform(
367 basegfx::tools::createTranslateB2DHomMatrix(
368 aHorMove,
369 aVerMove));
370 }
371 break;
372 }
373
374 case META_PUSH_ACTION :
375 {
376 const MetaPushAction* pA = static_cast< const MetaPushAction* >(pAction);
377 const sal_uInt16 nFlags(pA->GetFlags());
378
379 aPushFlags.push_back(nFlags);
380
381 if(nFlags & PUSH_CLIPREGION)
382 {
383 aClips.push_back(aClips.back());
384 }
385
386 if(nFlags & PUSH_MAPMODE)
387 {
388 aMapModes.push_back(aMapModes.back());
389 }
390 break;
391 }
392
393 case META_POP_ACTION :
394 {
395
396 if(aPushFlags.size())
397 {
398 const sal_uInt16 nFlags(aPushFlags.back());
399 aPushFlags.pop_back();
400
401 if(nFlags & PUSH_CLIPREGION)
402 {
403 if(aClips.size() > 1)
404 {
405 aClips.pop_back();
406 }
407 else
408 {
409 OSL_ENSURE(false, "Wrong POP() in ClipRegions (!)");
410 }
411 }
412
413 if(nFlags & PUSH_MAPMODE)
414 {
415 if(aMapModes.size() > 1)
416 {
417 aMapModes.pop_back();
418 }
419 else
420 {
421 OSL_ENSURE(false, "Wrong POP() in MapModes (!)");
422 }
423 }
424 }
425 else
426 {
427 OSL_ENSURE(false, "Invalid pop() without push() (!)");
428 }
429
430 break;
431 }
432
433 case META_MAPMODE_ACTION :
434 {
435 const MetaMapModeAction* pA = static_cast< const MetaMapModeAction* >(pAction);
436
437 aMapModes.back() = pA->GetMapMode();
438 break;
439 }
440
441 default:
442 {
443 break;
444 }
445 }
446
447 // this area contains all actions which could potentially be clipped. Since
448 // this tooling is only a fallback (see comments in header), only the needed
449 // actions will be implemented. Extend using the pattern for the already
450 // implemented actions.
451 if(aClips.size() && aClips.back().count())
452 {
453 switch(nType)
454 {
455 //
456 // pixel actions, just check on inside
457 //
458 case META_PIXEL_ACTION :
459 {
460 const MetaPixelAction* pA = static_cast< const MetaPixelAction* >(pAction);
461 const Point& rPoint = pA->GetPoint();
462
463 if(!basegfx::tools::isInside(
464 aClips.back(),
465 basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
466 {
467 // when not inside, do not add original
468 bDone = true;
469 }
470 break;
471 }
472
473 case META_POINT_ACTION :
474 {
475 const MetaPointAction* pA = static_cast< const MetaPointAction* >(pAction);
476 const Point& rPoint = pA->GetPoint();
477
478 if(!basegfx::tools::isInside(
479 aClips.back(),
480 basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
481 {
482 // when not inside, do not add original
483 bDone = true;
484 }
485 break;
486 }
487
488 //
489 // geometry actions
490 //
491 case META_LINE_ACTION :
492 {
493 const MetaLineAction* pA = static_cast< const MetaLineAction* >(pAction);
494 const Point& rStart(pA->GetStartPoint());
495 const Point& rEnd(pA->GetEndPoint());
496 basegfx::B2DPolygon aLine;
497
498 aLine.append(basegfx::B2DPoint(rStart.X(), rStart.Y()));
499 aLine.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y()));
500
501 bDone = handleGeometricContent(
502 aClips.back(),
503 basegfx::B2DPolyPolygon(aLine),
504 aTarget,
505 true); // stroke
506 break;
507 }
508
509 case META_RECT_ACTION :
510 {
511 const MetaRectAction* pA = static_cast< const MetaRectAction* >(pAction);
512 const Rectangle& rRect = pA->GetRect();
513
514 if(rRect.IsEmpty())
515 {
516 bDone = true;
517 }
518 else
519 {
520
521 bDone = handleGeometricContent(
522 aClips.back(),
523 basegfx::B2DPolyPolygon(
524 basegfx::tools::createPolygonFromRect(
525 basegfx::B2DRange(
526 rRect.Left(), rRect.Top(),
527 rRect.Right(), rRect.Bottom()))),
528 aTarget,
529 false); // stroke
530 }
531 break;
532 }
533
534 case META_ROUNDRECT_ACTION :
535 {
536 const MetaRoundRectAction* pA = static_cast< const MetaRoundRectAction* >(pAction);
537 const Rectangle& rRect = pA->GetRect();
538
539 if(rRect.IsEmpty())
540 {
541 bDone = true;
542 }
543 else
544 {
545 const sal_uInt32 nHor(pA->GetHorzRound());
546 const sal_uInt32 nVer(pA->GetVertRound());
547 const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom());
548 basegfx::B2DPolygon aOutline;
549
550 if(nHor || nVer)
551 {
552 double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0));
553 double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0));
554 fRadiusX = std::max(0.0, std::min(1.0, fRadiusX));
555 fRadiusY = std::max(0.0, std::min(1.0, fRadiusY));
556
557 aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
558 }
559 else
560 {
561 aOutline = basegfx::tools::createPolygonFromRect(aRange);
562 }
563
564 bDone = handleGeometricContent(
565 aClips.back(),
566 basegfx::B2DPolyPolygon(aOutline),
567 aTarget,
568 false); // stroke
569 }
570 break;
571 }
572
573 case META_ELLIPSE_ACTION :
574 {
575 const MetaEllipseAction* pA = static_cast< const MetaEllipseAction* >(pAction);
576 const Rectangle& rRect = pA->GetRect();
577
578 if(rRect.IsEmpty())
579 {
580 bDone = true;
581 }
582 else
583 {
584 const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom());
585
586 bDone = handleGeometricContent(
587 aClips.back(),
588 basegfx::B2DPolyPolygon(
589 basegfx::tools::createPolygonFromEllipse(
590 aRange.getCenter(),
591 aRange.getWidth() * 0.5,
592 aRange.getHeight() * 0.5)),
593 aTarget,
594 false); // stroke
595 }
596 break;
597 }
598
599 case META_ARC_ACTION :
600 {
601 const MetaArcAction* pA = static_cast< const MetaArcAction* >(pAction);
602 const Rectangle& rRect = pA->GetRect();
603
604 if(rRect.IsEmpty())
605 {
606 bDone = true;
607 }
608 else
609 {
610 const Polygon aToolsPoly(
611 rRect,
612 pA->GetStartPoint(),
613 pA->GetEndPoint(),
614 POLY_ARC);
615
616 bDone = handleGeometricContent(
617 aClips.back(),
618 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
619 aTarget,
620 true); // stroke
621 }
622 break;
623 }
624
625 case META_PIE_ACTION :
626 {
627 const MetaPieAction* pA = static_cast< const MetaPieAction* >(pAction);
628 const Rectangle& rRect = pA->GetRect();
629
630 if(rRect.IsEmpty())
631 {
632 bDone = true;
633 }
634 else
635 {
636 const Polygon aToolsPoly(
637 rRect,
638 pA->GetStartPoint(),
639 pA->GetEndPoint(),
640 POLY_PIE);
641
642 bDone = handleGeometricContent(
643 aClips.back(),
644 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
645 aTarget,
646 false); // stroke
647 }
648 break;
649 }
650
651 case META_CHORD_ACTION :
652 {
653 const MetaChordAction* pA = static_cast< const MetaChordAction* >(pAction);
654 const Rectangle& rRect = pA->GetRect();
655
656 if(rRect.IsEmpty())
657 {
658 bDone = true;
659 }
660 else
661 {
662 const Polygon aToolsPoly(
663 rRect,
664 pA->GetStartPoint(),
665 pA->GetEndPoint(),
666 POLY_CHORD);
667
668 bDone = handleGeometricContent(
669 aClips.back(),
670 basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
671 aTarget,
672 false); // stroke
673 }
674 break;
675 }
676
677 case META_POLYLINE_ACTION :
678 {
679 const MetaPolyLineAction* pA = static_cast< const MetaPolyLineAction* >(pAction);
680
681 bDone = handleGeometricContent(
682 aClips.back(),
683 basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()),
684 aTarget,
685 true); // stroke
686 break;
687 }
688
689 case META_POLYGON_ACTION :
690 {
691 const MetaPolygonAction* pA = static_cast< const MetaPolygonAction* >(pAction);
692
693 bDone = handleGeometricContent(
694 aClips.back(),
695 basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()),
696 aTarget,
697 false); // stroke
698 break;
699 }
700
701 case META_POLYPOLYGON_ACTION :
702 {
703 const MetaPolyPolygonAction* pA = static_cast< const MetaPolyPolygonAction* >(pAction);
704 const PolyPolygon& rPoly = pA->GetPolyPolygon();
705
706 bDone = handleGeometricContent(
707 aClips.back(),
708 rPoly.getB2DPolyPolygon(),
709 aTarget,
710 false); // stroke
711 break;
712 }
713
714 //
715 // bitmap actions, create BitmapEx with alpha channel derived
716 // from clipping
717 //
718 case META_BMPEX_ACTION :
719 {
720 const MetaBmpExAction* pA = static_cast< const MetaBmpExAction* >(pAction);
721 const BitmapEx& rBitmapEx = pA->GetBitmapEx();
722
723 // the logical size depends on the PrefSize of the given bitmap in
724 // combination with the current MapMode
725 Size aLogicalSize(rBitmapEx.GetPrefSize());
726
727 if(MAP_PIXEL == rBitmapEx.GetPrefMapMode().GetMapUnit())
728 {
729 aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit());
730 }
731 else
732 {
733 aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmapEx.GetPrefMapMode(), aMapModes.back().GetMapUnit());
734 }
735
736 bDone = handleBitmapContent(
737 aClips.back(),
738 pA->GetPoint(),
739 aLogicalSize,
740 rBitmapEx,
741 aTarget);
742 break;
743 }
744
745 case META_BMP_ACTION :
746 {
747 const MetaBmpAction* pA = static_cast< const MetaBmpAction* >(pAction);
748 const Bitmap& rBitmap = pA->GetBitmap();
749
750 // the logical size depends on the PrefSize of the given bitmap in
751 // combination with the current MapMode
752 Size aLogicalSize(rBitmap.GetPrefSize());
753
754 if(MAP_PIXEL == rBitmap.GetPrefMapMode().GetMapUnit())
755 {
756 aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit());
757 }
758 else
759 {
760 aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmap.GetPrefMapMode(), aMapModes.back().GetMapUnit());
761 }
762
763 bDone = handleBitmapContent(
764 aClips.back(),
765 pA->GetPoint(),
766 aLogicalSize,
767 BitmapEx(rBitmap),
768 aTarget);
769 break;
770 }
771
772 case META_BMPEXSCALE_ACTION :
773 {
774 const MetaBmpExScaleAction* pA = static_cast< const MetaBmpExScaleAction* >(pAction);
775
776 bDone = handleBitmapContent(
777 aClips.back(),
778 pA->GetPoint(),
779 pA->GetSize(),
780 pA->GetBitmapEx(),
781 aTarget);
782 break;
783 }
784
785 case META_BMPSCALE_ACTION :
786 {
787 const MetaBmpScaleAction* pA = static_cast< const MetaBmpScaleAction* >(pAction);
788
789 bDone = handleBitmapContent(
790 aClips.back(),
791 pA->GetPoint(),
792 pA->GetSize(),
793 BitmapEx(pA->GetBitmap()),
794 aTarget);
795 break;
796 }
797
798 case META_BMPEXSCALEPART_ACTION :
799 {
800 const MetaBmpExScalePartAction* pA = static_cast< const MetaBmpExScalePartAction* >(pAction);
801 const BitmapEx& rBitmapEx = pA->GetBitmapEx();
802
803 if(rBitmapEx.IsEmpty())
804 {
805 // empty content
806 bDone = true;
807 }
808 else
809 {
810 BitmapEx aCroppedBitmapEx(rBitmapEx);
811 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
812
813 if(aCropRectangle.IsEmpty())
814 {
815 // empty content
816 bDone = true;
817 }
818 else
819 {
820 aCroppedBitmapEx.Crop(aCropRectangle);
821 bDone = handleBitmapContent(
822 aClips.back(),
823 pA->GetDestPoint(),
824 pA->GetDestSize(),
825 aCroppedBitmapEx,
826 aTarget);
827 }
828 }
829 break;
830 }
831
832 case META_BMPSCALEPART_ACTION :
833 {
834 const MetaBmpScalePartAction* pA = static_cast< const MetaBmpScalePartAction* >(pAction);
835 const Bitmap& rBitmap = pA->GetBitmap();
836
837 if(rBitmap.IsEmpty())
838 {
839 // empty content
840 bDone = true;
841 }
842 else
843 {
844 Bitmap aCroppedBitmap(rBitmap);
845 const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
846
847 if(aCropRectangle.IsEmpty())
848 {
849 // empty content
850 bDone = true;
851 }
852 else
853 {
854 aCroppedBitmap.Crop(aCropRectangle);
855 bDone = handleBitmapContent(
856 aClips.back(),
857 pA->GetDestPoint(),
858 pA->GetDestSize(),
859 BitmapEx(aCroppedBitmap),
860 aTarget);
861 }
862 }
863 break;
864 }
865
866 //
867 // need to handle all those 'hacks' which hide data in comments
868 //
869 case META_COMMENT_ACTION :
870 {
871 const MetaCommentAction* pA = static_cast< const MetaCommentAction* >(pAction);
872 const ByteString& rComment = pA->GetComment();
873
874 if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN"))
875 {
876 // nothing to do; this just means that between here and XGRAD_SEQ_END
877 // exists a META_GRADIENTEX_ACTION mixed with Xor-tricked painiting
878 // commands. This comment is used to scan over these and filter for
879 // the gradient action. It is needed to support META_GRADIENTEX_ACTION
880 // in this processor to solve usages.
881 }
882 else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHFILL_SEQ_BEGIN"))
883 {
884 SvtGraphicFill aFilling;
885 PolyPolygon aPath;
886
887 { // read SvtGraphicFill
888 SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ);
889 aMemStm >> aFilling;
890 }
891
892 aFilling.getPath(aPath);
893
894 if(aPath.Count())
895 {
896 const basegfx::B2DPolyPolygon aSource(aPath.getB2DPolyPolygon());
897 const basegfx::B2DPolyPolygon aResult(
898 basegfx::tools::clipPolyPolygonOnPolyPolygon(
899 aSource,
900 aClips.back(),
901 true, // inside
902 false)); // stroke
903
904 if(aResult.count())
905 {
906 if(aResult != aSource)
907 {
908 // add clipped geometry
909 aFilling.setPath(PolyPolygon(aResult));
910 addSvtGraphicFill(aFilling, aTarget);
911 bDone = true;
912 }
913 }
914 else
915 {
916 // exchange with empty polygon
917 aFilling.setPath(PolyPolygon());
918 addSvtGraphicFill(aFilling, aTarget);
919 bDone = true;
920 }
921 }
922 }
923 else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHSTROKE_SEQ_BEGIN"))
924 {
925 SvtGraphicStroke aStroke;
926 Polygon aPath;
927
928 { // read SvtGraphicFill
929 SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ);
930 aMemStm >> aStroke;
931 }
932
933 aStroke.getPath(aPath);
934
935 if(aPath.GetSize())
936 {
937 const basegfx::B2DPolygon aSource(aPath.getB2DPolygon());
938 const basegfx::B2DPolyPolygon aResult(
939 basegfx::tools::clipPolygonOnPolyPolygon(
940 aSource,
941 aClips.back(),
942 true, // inside
943 true)); // stroke
944
945 if(aResult.count())
946 {
947 if(aResult.count() > 1 || aResult.getB2DPolygon(0) != aSource)
948 {
949 // add clipped geometry
950 for(sal_uInt32 a(0); a < aResult.count(); a++)
951 {
952 aStroke.setPath(Polygon(aResult.getB2DPolygon(a)));
953 addSvtGraphicStroke(aStroke, aTarget);
954 }
955
956 bDone = true;
957 }
958 }
959 else
960 {
961 // exchange with empty polygon
962 aStroke.setPath(Polygon());
963 addSvtGraphicStroke(aStroke, aTarget);
964 bDone = true;
965 }
966
967 }
968 }
969 break;
970 }
971
972 //
973 // need to handle gradient fills (hopefully only unroated ones)
974 //
975
976 case META_GRADIENT_ACTION :
977 {
978 const MetaGradientAction* pA = static_cast< const MetaGradientAction* >(pAction);
979 const Rectangle& rRect = pA->GetRect();
980
981 if(rRect.IsEmpty())
982 {
983 bDone = true;
984 }
985 else
986 {
987 bDone = handleGradientContent(
988 aClips.back(),
989 basegfx::B2DPolyPolygon(
990 basegfx::tools::createPolygonFromRect(
991 basegfx::B2DRange(
992 rRect.Left(), rRect.Top(),
993 rRect.Right(), rRect.Bottom()))),
994 pA->GetGradient(),
995 aTarget);
996 }
997
998
999 break;
1000 }
1001
1002 case META_GRADIENTEX_ACTION :
1003 {
1004 const MetaGradientExAction* pA = static_cast< const MetaGradientExAction* >(pAction);
1005 const PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
1006
1007 bDone = handleGradientContent(
1008 aClips.back(),
1009 rPolyPoly.getB2DPolyPolygon(),
1010 pA->GetGradient(),
1011 aTarget);
1012 break;
1013 }
1014
1015 // not (yet) supported actions
1016 //
1017 // META_NULL_ACTION
1018 // META_TEXT_ACTION
1019 // META_TEXTARRAY_ACTION
1020 // META_STRETCHTEXT_ACTION
1021 // META_TEXTRECT_ACTION
1022 // META_MASK_ACTION
1023 // META_MASKSCALE_ACTION
1024 // META_MASKSCALEPART_ACTION
1025 // META_HATCH_ACTION
1026 // META_WALLPAPER_ACTION
1027 // META_FILLCOLOR_ACTION
1028 // META_TEXTCOLOR_ACTION
1029 // META_TEXTFILLCOLOR_ACTION
1030 // META_TEXTALIGN_ACTION
1031 // META_MAPMODE_ACTION
1032 // META_FONT_ACTION
1033 // META_TRANSPARENT_ACTION
1034 // META_EPS_ACTION
1035 // META_REFPOINT_ACTION
1036 // META_TEXTLINECOLOR_ACTION
1037 // META_TEXTLINE_ACTION
1038 // META_FLOATTRANSPARENT_ACTION
1039 // META_LAYOUTMODE_ACTION
1040 // META_TEXTLANGUAGE_ACTION
1041 // META_OVERLINECOLOR_ACTION
1042
1043 // if an action is not handled at all, it will simply get copied to the
1044 // target (see below). This is the default for all non-implemented actions
1045 default:
1046 {
1047 break;
1048 }
1049 }
1050 }
1051
1052 if(bDone)
1053 {
1054 bChanged = true;
1055 }
1056 else
1057 {
1058 const_cast< MetaAction* >(pAction)->Duplicate();
1059 aTarget.AddAction(const_cast< MetaAction* >(pAction));
1060 }
1061 }
1062
1063 if(bChanged)
1064 {
1065 // when changed, copy back and do not forget to set MapMode
1066 // and PrefSize
1067 aTarget.SetPrefMapMode(rSource.GetPrefMapMode());
1068 aTarget.SetPrefSize(rSource.GetPrefSize());
1069 rSource = aTarget;
1070 }
1071 }
1072
1073 //////////////////////////////////////////////////////////////////////////////
1074
usesClipActions(const GDIMetaFile & rSource)1075 bool VCL_DLLPUBLIC usesClipActions(const GDIMetaFile& rSource)
1076 {
1077 const sal_uLong nObjCount(rSource.GetActionCount());
1078
1079 for(sal_uLong i(0); i < nObjCount; ++i)
1080 {
1081 const MetaAction* pAction(rSource.GetAction(i));
1082 const sal_uInt16 nType(pAction->GetType());
1083
1084 switch(nType)
1085 {
1086 case META_CLIPREGION_ACTION :
1087 case META_ISECTRECTCLIPREGION_ACTION :
1088 case META_ISECTREGIONCLIPREGION_ACTION :
1089 case META_MOVECLIPREGION_ACTION :
1090 {
1091 return true;
1092 break;
1093 }
1094
1095 default: break;
1096 }
1097 }
1098
1099 return false;
1100 }
1101
1102 //////////////////////////////////////////////////////////////////////////////
1103 // eof
1104