Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <vcl/gdimetafiletools.hxx>
21 : #include <vcl/metaact.hxx>
22 : #include <basegfx/polygon/b2dpolygonclipper.hxx>
23 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
24 : #include <basegfx/polygon/b2dpolypolygontools.hxx>
25 : #include <basegfx/polygon/b2dpolygontools.hxx>
26 : #include <vcl/virdev.hxx>
27 : #include <vcl/svapp.hxx>
28 : #include <vcl/graphictools.hxx>
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 : tools::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 : tools::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 : ScopedVclPtrInstance< 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 : // tools::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 0 : 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 0 : basegfx::fround(aLogicBitmapRange.getMinX()),
222 0 : basegfx::fround(aLogicBitmapRange.getMinY())),
223 : Size(
224 0 : basegfx::fround(aLogicBitmapRange.getWidth()),
225 0 : 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 : WriteSvtGraphicStroke( 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 : WriteSvtGraphicFill( 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 : // #i121267# Tooling to internally clip geometry against internal clip regions
259 :
260 38 : void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource)
261 : {
262 38 : const sal_uLong nObjCount(rSource.GetActionSize());
263 :
264 38 : if(!nObjCount)
265 : {
266 38 : return;
267 : }
268 :
269 : // prepare target data container and push/pop stack data
270 38 : GDIMetaFile aTarget;
271 38 : bool bChanged(false);
272 76 : std::vector< basegfx::B2DPolyPolygon > aClips;
273 76 : std::vector< PushFlags > aPushFlags;
274 76 : std::vector< MapMode > aMapModes;
275 :
276 : // start with empty region
277 38 : aClips.push_back(basegfx::B2DPolyPolygon());
278 :
279 : // start with default MapMode (MAP_PIXEL)
280 38 : aMapModes.push_back(MapMode());
281 :
282 4013 : for(sal_uLong i(0); i < nObjCount; ++i)
283 : {
284 3975 : const MetaAction* pAction(rSource.GetAction(i));
285 3975 : const MetaActionType nType(pAction->GetType());
286 3975 : bool bDone(false);
287 :
288 : // basic operation takes care of clipregion actions (four) and push/pop of these
289 : // to steer the currently set clip region. There *is* an active
290 : // clip region when (aClips.size() && aClips.back().count()), see
291 : // below
292 3975 : switch(nType)
293 : {
294 : case MetaActionType::CLIPREGION :
295 : {
296 0 : const MetaClipRegionAction* pA = static_cast< const MetaClipRegionAction* >(pAction);
297 :
298 0 : if(pA->IsClipping())
299 : {
300 0 : const vcl::Region& rRegion = pA->GetRegion();
301 0 : const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
302 :
303 0 : aClips.back() = aNewClip;
304 : }
305 : else
306 : {
307 0 : aClips.back() = basegfx::B2DPolyPolygon();
308 : }
309 :
310 0 : break;
311 : }
312 :
313 : case MetaActionType::ISECTRECTCLIPREGION :
314 : {
315 45 : const MetaISectRectClipRegionAction* pA = static_cast< const MetaISectRectClipRegionAction* >(pAction);
316 45 : const Rectangle& rRect = pA->GetRect();
317 :
318 45 : if(!rRect.IsEmpty() && aClips.size() && aClips.back().count())
319 : {
320 : const basegfx::B2DRange aClipRange(
321 0 : rRect.Left(), rRect.Top(),
322 0 : rRect.Right(), rRect.Bottom());
323 :
324 0 : aClips.back() = basegfx::tools::clipPolyPolygonOnRange(
325 0 : aClips.back(),
326 : aClipRange,
327 : true, // inside
328 0 : false); // stroke
329 : }
330 45 : break;
331 : }
332 :
333 : case MetaActionType::ISECTREGIONCLIPREGION :
334 : {
335 7 : const MetaISectRegionClipRegionAction* pA = static_cast< const MetaISectRegionClipRegionAction* >(pAction);
336 7 : const vcl::Region& rRegion = pA->GetRegion();
337 :
338 7 : if(!rRegion.IsEmpty() && aClips.size() && aClips.back().count())
339 : {
340 0 : const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon());
341 :
342 0 : aClips.back() = basegfx::tools::clipPolyPolygonOnPolyPolygon(
343 0 : aClips.back(),
344 : aNewClip,
345 : true, // inside
346 0 : false); // stroke
347 : }
348 7 : break;
349 : }
350 :
351 : case MetaActionType::MOVECLIPREGION :
352 : {
353 0 : const MetaMoveClipRegionAction* pA = static_cast< const MetaMoveClipRegionAction* >(pAction);
354 0 : const long aHorMove(pA->GetHorzMove());
355 0 : const long aVerMove(pA->GetVertMove());
356 :
357 0 : if((aHorMove || aVerMove) && aClips.size() && aClips.back().count())
358 : {
359 0 : aClips.back().transform(
360 : basegfx::tools::createTranslateB2DHomMatrix(
361 : aHorMove,
362 0 : aVerMove));
363 : }
364 0 : break;
365 : }
366 :
367 : case MetaActionType::PUSH :
368 : {
369 476 : const MetaPushAction* pA = static_cast< const MetaPushAction* >(pAction);
370 476 : const PushFlags nFlags(pA->GetFlags());
371 :
372 476 : aPushFlags.push_back(nFlags);
373 :
374 476 : if(nFlags & PushFlags::CLIPREGION)
375 : {
376 103 : aClips.push_back(aClips.back());
377 : }
378 :
379 476 : if(nFlags & PushFlags::MAPMODE)
380 : {
381 401 : aMapModes.push_back(aMapModes.back());
382 : }
383 476 : break;
384 : }
385 :
386 : case MetaActionType::POP :
387 : {
388 :
389 476 : if(aPushFlags.size())
390 : {
391 476 : const PushFlags nFlags(aPushFlags.back());
392 476 : aPushFlags.pop_back();
393 :
394 476 : if(nFlags & PushFlags::CLIPREGION)
395 : {
396 103 : if(aClips.size() > 1)
397 : {
398 103 : aClips.pop_back();
399 : }
400 : else
401 : {
402 : OSL_ENSURE(false, "Wrong POP() in ClipRegions (!)");
403 : }
404 : }
405 :
406 476 : if(nFlags & PushFlags::MAPMODE)
407 : {
408 401 : if(aMapModes.size() > 1)
409 : {
410 401 : aMapModes.pop_back();
411 : }
412 : else
413 : {
414 : OSL_ENSURE(false, "Wrong POP() in MapModes (!)");
415 : }
416 : }
417 : }
418 : else
419 : {
420 : OSL_ENSURE(false, "Invalid pop() without push() (!)");
421 : }
422 :
423 476 : break;
424 : }
425 :
426 : case MetaActionType::MAPMODE :
427 : {
428 6 : const MetaMapModeAction* pA = static_cast< const MetaMapModeAction* >(pAction);
429 :
430 6 : aMapModes.back() = pA->GetMapMode();
431 6 : break;
432 : }
433 :
434 : default:
435 : {
436 2965 : break;
437 : }
438 : }
439 :
440 : // this area contains all actions which could potentially be clipped. Since
441 : // this tooling is only a fallback (see comments in header), only the needed
442 : // actions will be implemented. Extend using the pattern for the already
443 : // implemented actions.
444 3975 : if(aClips.size() && aClips.back().count())
445 : {
446 0 : switch(nType)
447 : {
448 :
449 : // pixel actions, just check on inside
450 :
451 : case MetaActionType::PIXEL :
452 : {
453 0 : const MetaPixelAction* pA = static_cast< const MetaPixelAction* >(pAction);
454 0 : const Point& rPoint = pA->GetPoint();
455 :
456 0 : if(!basegfx::tools::isInside(
457 0 : aClips.back(),
458 0 : basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
459 : {
460 : // when not inside, do not add original
461 0 : bDone = true;
462 : }
463 0 : break;
464 : }
465 :
466 : case MetaActionType::POINT :
467 : {
468 0 : const MetaPointAction* pA = static_cast< const MetaPointAction* >(pAction);
469 0 : const Point& rPoint = pA->GetPoint();
470 :
471 0 : if(!basegfx::tools::isInside(
472 0 : aClips.back(),
473 0 : basegfx::B2DPoint(rPoint.X(), rPoint.Y())))
474 : {
475 : // when not inside, do not add original
476 0 : bDone = true;
477 : }
478 0 : break;
479 : }
480 :
481 : // geometry actions
482 :
483 : case MetaActionType::LINE :
484 : {
485 0 : const MetaLineAction* pA = static_cast< const MetaLineAction* >(pAction);
486 0 : const Point& rStart(pA->GetStartPoint());
487 0 : const Point& rEnd(pA->GetEndPoint());
488 0 : basegfx::B2DPolygon aLine;
489 :
490 0 : aLine.append(basegfx::B2DPoint(rStart.X(), rStart.Y()));
491 0 : aLine.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y()));
492 :
493 : bDone = handleGeometricContent(
494 0 : aClips.back(),
495 : basegfx::B2DPolyPolygon(aLine),
496 : aTarget,
497 0 : true); // stroke
498 0 : break;
499 : }
500 :
501 : case MetaActionType::RECT :
502 : {
503 0 : const MetaRectAction* pA = static_cast< const MetaRectAction* >(pAction);
504 0 : const Rectangle& rRect = pA->GetRect();
505 :
506 0 : if(rRect.IsEmpty())
507 : {
508 0 : bDone = true;
509 : }
510 : else
511 : {
512 :
513 : bDone = handleGeometricContent(
514 0 : aClips.back(),
515 : basegfx::B2DPolyPolygon(
516 : basegfx::tools::createPolygonFromRect(
517 : basegfx::B2DRange(
518 0 : rRect.Left(), rRect.Top(),
519 0 : rRect.Right(), rRect.Bottom()))),
520 : aTarget,
521 0 : false); // stroke
522 : }
523 0 : break;
524 : }
525 :
526 : case MetaActionType::ROUNDRECT :
527 : {
528 0 : const MetaRoundRectAction* pA = static_cast< const MetaRoundRectAction* >(pAction);
529 0 : const Rectangle& rRect = pA->GetRect();
530 :
531 0 : if(rRect.IsEmpty())
532 : {
533 0 : bDone = true;
534 : }
535 : else
536 : {
537 0 : const sal_uInt32 nHor(pA->GetHorzRound());
538 0 : const sal_uInt32 nVer(pA->GetVertRound());
539 0 : const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom());
540 0 : basegfx::B2DPolygon aOutline;
541 :
542 0 : if(nHor || nVer)
543 : {
544 0 : double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0));
545 0 : double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0));
546 0 : fRadiusX = std::max(0.0, std::min(1.0, fRadiusX));
547 0 : fRadiusY = std::max(0.0, std::min(1.0, fRadiusY));
548 :
549 0 : aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY);
550 : }
551 : else
552 : {
553 0 : aOutline = basegfx::tools::createPolygonFromRect(aRange);
554 : }
555 :
556 : bDone = handleGeometricContent(
557 0 : aClips.back(),
558 : basegfx::B2DPolyPolygon(aOutline),
559 : aTarget,
560 0 : false); // stroke
561 : }
562 0 : break;
563 : }
564 :
565 : case MetaActionType::ELLIPSE :
566 : {
567 0 : const MetaEllipseAction* pA = static_cast< const MetaEllipseAction* >(pAction);
568 0 : const Rectangle& rRect = pA->GetRect();
569 :
570 0 : if(rRect.IsEmpty())
571 : {
572 0 : bDone = true;
573 : }
574 : else
575 : {
576 0 : const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom());
577 :
578 : bDone = handleGeometricContent(
579 0 : aClips.back(),
580 : basegfx::B2DPolyPolygon(
581 : basegfx::tools::createPolygonFromEllipse(
582 : aRange.getCenter(),
583 0 : aRange.getWidth() * 0.5,
584 0 : aRange.getHeight() * 0.5)),
585 : aTarget,
586 0 : false); // stroke
587 : }
588 0 : break;
589 : }
590 :
591 : case MetaActionType::ARC :
592 : {
593 0 : const MetaArcAction* pA = static_cast< const MetaArcAction* >(pAction);
594 0 : const Rectangle& rRect = pA->GetRect();
595 :
596 0 : if(rRect.IsEmpty())
597 : {
598 0 : bDone = true;
599 : }
600 : else
601 : {
602 : const Polygon aToolsPoly(
603 : rRect,
604 0 : pA->GetStartPoint(),
605 0 : pA->GetEndPoint(),
606 0 : POLY_ARC);
607 :
608 : bDone = handleGeometricContent(
609 0 : aClips.back(),
610 : basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
611 : aTarget,
612 0 : true); // stroke
613 : }
614 0 : break;
615 : }
616 :
617 : case MetaActionType::PIE :
618 : {
619 0 : const MetaPieAction* pA = static_cast< const MetaPieAction* >(pAction);
620 0 : const Rectangle& rRect = pA->GetRect();
621 :
622 0 : if(rRect.IsEmpty())
623 : {
624 0 : bDone = true;
625 : }
626 : else
627 : {
628 : const Polygon aToolsPoly(
629 : rRect,
630 0 : pA->GetStartPoint(),
631 0 : pA->GetEndPoint(),
632 0 : POLY_PIE);
633 :
634 : bDone = handleGeometricContent(
635 0 : aClips.back(),
636 : basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
637 : aTarget,
638 0 : false); // stroke
639 : }
640 0 : break;
641 : }
642 :
643 : case MetaActionType::CHORD :
644 : {
645 0 : const MetaChordAction* pA = static_cast< const MetaChordAction* >(pAction);
646 0 : const Rectangle& rRect = pA->GetRect();
647 :
648 0 : if(rRect.IsEmpty())
649 : {
650 0 : bDone = true;
651 : }
652 : else
653 : {
654 : const Polygon aToolsPoly(
655 : rRect,
656 0 : pA->GetStartPoint(),
657 0 : pA->GetEndPoint(),
658 0 : POLY_CHORD);
659 :
660 : bDone = handleGeometricContent(
661 0 : aClips.back(),
662 : basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()),
663 : aTarget,
664 0 : false); // stroke
665 : }
666 0 : break;
667 : }
668 :
669 : case MetaActionType::POLYLINE :
670 : {
671 0 : const MetaPolyLineAction* pA = static_cast< const MetaPolyLineAction* >(pAction);
672 :
673 : bDone = handleGeometricContent(
674 0 : aClips.back(),
675 0 : basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()),
676 : aTarget,
677 0 : true); // stroke
678 0 : break;
679 : }
680 :
681 : case MetaActionType::POLYGON :
682 : {
683 0 : const MetaPolygonAction* pA = static_cast< const MetaPolygonAction* >(pAction);
684 :
685 : bDone = handleGeometricContent(
686 0 : aClips.back(),
687 0 : basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()),
688 : aTarget,
689 0 : false); // stroke
690 0 : break;
691 : }
692 :
693 : case MetaActionType::POLYPOLYGON :
694 : {
695 0 : const MetaPolyPolygonAction* pA = static_cast< const MetaPolyPolygonAction* >(pAction);
696 0 : const tools::PolyPolygon& rPoly = pA->GetPolyPolygon();
697 :
698 : bDone = handleGeometricContent(
699 0 : aClips.back(),
700 : rPoly.getB2DPolyPolygon(),
701 : aTarget,
702 0 : false); // stroke
703 0 : break;
704 : }
705 :
706 : // bitmap actions, create BitmapEx with alpha channel derived
707 : // from clipping
708 :
709 : case MetaActionType::BMPEX :
710 : {
711 0 : const MetaBmpExAction* pA = static_cast< const MetaBmpExAction* >(pAction);
712 0 : const BitmapEx& rBitmapEx = pA->GetBitmapEx();
713 :
714 : // the logical size depends on the PrefSize of the given bitmap in
715 : // combination with the current MapMode
716 0 : Size aLogicalSize(rBitmapEx.GetPrefSize());
717 :
718 0 : if(MAP_PIXEL == rBitmapEx.GetPrefMapMode().GetMapUnit())
719 : {
720 0 : aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit());
721 : }
722 : else
723 : {
724 0 : aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmapEx.GetPrefMapMode(), aMapModes.back().GetMapUnit());
725 : }
726 :
727 : bDone = handleBitmapContent(
728 0 : aClips.back(),
729 0 : pA->GetPoint(),
730 : aLogicalSize,
731 : rBitmapEx,
732 0 : aTarget);
733 0 : break;
734 : }
735 :
736 : case MetaActionType::BMP :
737 : {
738 0 : const MetaBmpAction* pA = static_cast< const MetaBmpAction* >(pAction);
739 0 : const Bitmap& rBitmap = pA->GetBitmap();
740 :
741 : // the logical size depends on the PrefSize of the given bitmap in
742 : // combination with the current MapMode
743 0 : Size aLogicalSize(rBitmap.GetPrefSize());
744 :
745 0 : if(MAP_PIXEL == rBitmap.GetPrefMapMode().GetMapUnit())
746 : {
747 0 : aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit());
748 : }
749 : else
750 : {
751 0 : aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmap.GetPrefMapMode(), aMapModes.back().GetMapUnit());
752 : }
753 :
754 : bDone = handleBitmapContent(
755 0 : aClips.back(),
756 0 : pA->GetPoint(),
757 : aLogicalSize,
758 : BitmapEx(rBitmap),
759 0 : aTarget);
760 0 : break;
761 : }
762 :
763 : case MetaActionType::BMPEXSCALE :
764 : {
765 0 : const MetaBmpExScaleAction* pA = static_cast< const MetaBmpExScaleAction* >(pAction);
766 :
767 : bDone = handleBitmapContent(
768 0 : aClips.back(),
769 0 : pA->GetPoint(),
770 0 : pA->GetSize(),
771 0 : pA->GetBitmapEx(),
772 0 : aTarget);
773 0 : break;
774 : }
775 :
776 : case MetaActionType::BMPSCALE :
777 : {
778 0 : const MetaBmpScaleAction* pA = static_cast< const MetaBmpScaleAction* >(pAction);
779 :
780 : bDone = handleBitmapContent(
781 0 : aClips.back(),
782 0 : pA->GetPoint(),
783 0 : pA->GetSize(),
784 0 : BitmapEx(pA->GetBitmap()),
785 0 : aTarget);
786 0 : break;
787 : }
788 :
789 : case MetaActionType::BMPEXSCALEPART :
790 : {
791 0 : const MetaBmpExScalePartAction* pA = static_cast< const MetaBmpExScalePartAction* >(pAction);
792 0 : const BitmapEx& rBitmapEx = pA->GetBitmapEx();
793 :
794 0 : if(rBitmapEx.IsEmpty())
795 : {
796 : // empty content
797 0 : bDone = true;
798 : }
799 : else
800 : {
801 0 : BitmapEx aCroppedBitmapEx(rBitmapEx);
802 0 : const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
803 :
804 0 : if(aCropRectangle.IsEmpty())
805 : {
806 : // empty content
807 0 : bDone = true;
808 : }
809 : else
810 : {
811 0 : aCroppedBitmapEx.Crop(aCropRectangle);
812 : bDone = handleBitmapContent(
813 0 : aClips.back(),
814 0 : pA->GetDestPoint(),
815 0 : pA->GetDestSize(),
816 : aCroppedBitmapEx,
817 0 : aTarget);
818 0 : }
819 : }
820 0 : break;
821 : }
822 :
823 : case MetaActionType::BMPSCALEPART :
824 : {
825 0 : const MetaBmpScalePartAction* pA = static_cast< const MetaBmpScalePartAction* >(pAction);
826 0 : const Bitmap& rBitmap = pA->GetBitmap();
827 :
828 0 : if(rBitmap.IsEmpty())
829 : {
830 : // empty content
831 0 : bDone = true;
832 : }
833 : else
834 : {
835 0 : Bitmap aCroppedBitmap(rBitmap);
836 0 : const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize());
837 :
838 0 : if(aCropRectangle.IsEmpty())
839 : {
840 : // empty content
841 0 : bDone = true;
842 : }
843 : else
844 : {
845 0 : aCroppedBitmap.Crop(aCropRectangle);
846 : bDone = handleBitmapContent(
847 0 : aClips.back(),
848 0 : pA->GetDestPoint(),
849 0 : pA->GetDestSize(),
850 : BitmapEx(aCroppedBitmap),
851 0 : aTarget);
852 0 : }
853 : }
854 0 : break;
855 : }
856 :
857 : // need to handle all those 'hacks' which hide data in comments
858 :
859 : case MetaActionType::COMMENT :
860 : {
861 0 : const MetaCommentAction* pA = static_cast< const MetaCommentAction* >(pAction);
862 0 : const OString& rComment = pA->GetComment();
863 :
864 0 : if(rComment.equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
865 : {
866 : // nothing to do; this just means that between here and XGRAD_SEQ_END
867 : // exists a MetaActionType::GRADIENTEX mixed with Xor-tricked painiting
868 : // commands. This comment is used to scan over these and filter for
869 : // the gradient action. It is needed to support MetaActionType::GRADIENTEX
870 : // in this processor to solve usages.
871 : }
872 0 : else if(rComment.equalsIgnoreAsciiCase("XPATHFILL_SEQ_BEGIN"))
873 : {
874 0 : SvtGraphicFill aFilling;
875 0 : tools::PolyPolygon aPath;
876 :
877 : { // read SvtGraphicFill
878 0 : SvMemoryStream aMemStm(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(),StreamMode::READ);
879 0 : ReadSvtGraphicFill( aMemStm, aFilling );
880 : }
881 :
882 0 : aFilling.getPath(aPath);
883 :
884 0 : if(aPath.Count())
885 : {
886 0 : const basegfx::B2DPolyPolygon aSource(aPath.getB2DPolyPolygon());
887 : const basegfx::B2DPolyPolygon aResult(
888 : basegfx::tools::clipPolyPolygonOnPolyPolygon(
889 : aSource,
890 0 : aClips.back(),
891 : true, // inside
892 0 : false)); // stroke
893 :
894 0 : if(aResult.count())
895 : {
896 0 : if(aResult != aSource)
897 : {
898 : // add clipped geometry
899 0 : aFilling.setPath(tools::PolyPolygon(aResult));
900 0 : addSvtGraphicFill(aFilling, aTarget);
901 0 : bDone = true;
902 : }
903 : }
904 : else
905 : {
906 : // exchange with empty polygon
907 0 : aFilling.setPath(tools::PolyPolygon());
908 0 : addSvtGraphicFill(aFilling, aTarget);
909 0 : bDone = true;
910 0 : }
911 0 : }
912 : }
913 0 : else if(rComment.equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_BEGIN"))
914 : {
915 0 : SvtGraphicStroke aStroke;
916 0 : Polygon aPath;
917 :
918 : { // read SvtGraphicFill
919 0 : SvMemoryStream aMemStm(const_cast<sal_uInt8 *>(pA->GetData()), pA->GetDataSize(),StreamMode::READ);
920 0 : ReadSvtGraphicStroke( aMemStm, aStroke );
921 : }
922 :
923 0 : aStroke.getPath(aPath);
924 :
925 0 : if(aPath.GetSize())
926 : {
927 0 : const basegfx::B2DPolygon aSource(aPath.getB2DPolygon());
928 : const basegfx::B2DPolyPolygon aResult(
929 : basegfx::tools::clipPolygonOnPolyPolygon(
930 : aSource,
931 0 : aClips.back(),
932 : true, // inside
933 0 : true)); // stroke
934 :
935 0 : if(aResult.count())
936 : {
937 0 : if(aResult.count() > 1 || aResult.getB2DPolygon(0) != aSource)
938 : {
939 : // add clipped geometry
940 0 : for(sal_uInt32 a(0); a < aResult.count(); a++)
941 : {
942 0 : aStroke.setPath(Polygon(aResult.getB2DPolygon(a)));
943 0 : addSvtGraphicStroke(aStroke, aTarget);
944 : }
945 :
946 0 : bDone = true;
947 : }
948 : }
949 : else
950 : {
951 : // exchange with empty polygon
952 0 : aStroke.setPath(Polygon());
953 0 : addSvtGraphicStroke(aStroke, aTarget);
954 0 : bDone = true;
955 0 : }
956 :
957 0 : }
958 : }
959 0 : break;
960 : }
961 :
962 : // need to handle gradient fills (hopefully only unroated ones)
963 :
964 : case MetaActionType::GRADIENT :
965 : {
966 0 : const MetaGradientAction* pA = static_cast< const MetaGradientAction* >(pAction);
967 0 : const Rectangle& rRect = pA->GetRect();
968 :
969 0 : if(rRect.IsEmpty())
970 : {
971 0 : bDone = true;
972 : }
973 : else
974 : {
975 : bDone = handleGradientContent(
976 0 : aClips.back(),
977 : basegfx::B2DPolyPolygon(
978 : basegfx::tools::createPolygonFromRect(
979 : basegfx::B2DRange(
980 0 : rRect.Left(), rRect.Top(),
981 0 : rRect.Right(), rRect.Bottom()))),
982 0 : pA->GetGradient(),
983 0 : aTarget);
984 : }
985 :
986 0 : break;
987 : }
988 :
989 : case MetaActionType::GRADIENTEX :
990 : {
991 0 : const MetaGradientExAction* pA = static_cast< const MetaGradientExAction* >(pAction);
992 0 : const tools::PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
993 :
994 : bDone = handleGradientContent(
995 0 : aClips.back(),
996 : rPolyPoly.getB2DPolyPolygon(),
997 0 : pA->GetGradient(),
998 0 : aTarget);
999 0 : break;
1000 : }
1001 :
1002 : // not (yet) supported actions
1003 :
1004 : // MetaActionType::NONE
1005 : // MetaActionType::TEXT
1006 : // MetaActionType::TEXTARRAY
1007 : // MetaActionType::STRETCHTEXT
1008 : // MetaActionType::TEXTRECT
1009 : // MetaActionType::MASK
1010 : // MetaActionType::MASKSCALE
1011 : // MetaActionType::MASKSCALEPART
1012 : // MetaActionType::HATCH
1013 : // MetaActionType::WALLPAPER
1014 : // MetaActionType::FILLCOLOR
1015 : // MetaActionType::TEXTCOLOR
1016 : // MetaActionType::TEXTFILLCOLOR
1017 : // MetaActionType::TEXTALIGN
1018 : // MetaActionType::MAPMODE
1019 : // MetaActionType::FONT
1020 : // MetaActionType::Transparent
1021 : // MetaActionType::EPS
1022 : // MetaActionType::REFPOINT
1023 : // MetaActionType::TEXTLINECOLOR
1024 : // MetaActionType::TEXTLINE
1025 : // MetaActionType::FLOATTRANSPARENT
1026 : // MetaActionType::LAYOUTMODE
1027 : // MetaActionType::TEXTLANGUAGE
1028 : // MetaActionType::OVERLINECOLOR
1029 :
1030 : // if an action is not handled at all, it will simply get copied to the
1031 : // target (see below). This is the default for all non-implemented actions
1032 : default:
1033 : {
1034 0 : break;
1035 : }
1036 : }
1037 : }
1038 :
1039 3975 : if(bDone)
1040 : {
1041 0 : bChanged = true;
1042 : }
1043 : else
1044 : {
1045 3975 : const_cast< MetaAction* >(pAction)->Duplicate();
1046 3975 : aTarget.AddAction(const_cast< MetaAction* >(pAction));
1047 : }
1048 : }
1049 :
1050 38 : if(bChanged)
1051 : {
1052 : // when changed, copy back and do not forget to set MapMode
1053 : // and PrefSize
1054 0 : aTarget.SetPrefMapMode(rSource.GetPrefMapMode());
1055 0 : aTarget.SetPrefSize(rSource.GetPrefSize());
1056 0 : rSource = aTarget;
1057 38 : }
1058 : }
1059 :
1060 79 : bool usesClipActions(const GDIMetaFile& rSource)
1061 : {
1062 79 : const sal_uLong nObjCount(rSource.GetActionSize());
1063 :
1064 950 : for(sal_uLong i(0); i < nObjCount; ++i)
1065 : {
1066 909 : const MetaAction* pAction(rSource.GetAction(i));
1067 909 : const MetaActionType nType(pAction->GetType());
1068 :
1069 909 : switch(nType)
1070 : {
1071 : case MetaActionType::CLIPREGION :
1072 : case MetaActionType::ISECTRECTCLIPREGION :
1073 : case MetaActionType::ISECTREGIONCLIPREGION :
1074 : case MetaActionType::MOVECLIPREGION :
1075 : {
1076 38 : return true;
1077 : }
1078 :
1079 871 : default: break;
1080 : }
1081 : }
1082 :
1083 41 : return false;
1084 : }
1085 :
1086 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|