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