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 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_drawinglayer.hxx"
26 
27 #include <drawinglayer/processor2d/hittestprocessor2d.hxx>
28 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
29 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
30 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
31 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
32 #include <basegfx/polygon/b2dpolygontools.hxx>
33 #include <basegfx/polygon/b2dpolypolygontools.hxx>
34 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
35 #include <drawinglayer/primitive2d/maskprimitive2d.hxx>
36 #include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/pointarrayprimitive2d.hxx>
38 #include <basegfx/matrix/b3dhommatrix.hxx>
39 #include <drawinglayer/processor3d/cutfindprocessor3d.hxx>
40 #include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
41 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
42 
43 //////////////////////////////////////////////////////////////////////////////
44 
45 namespace drawinglayer
46 {
47 	namespace processor2d
48 	{
HitTestProcessor2D(const geometry::ViewInformation2D & rViewInformation,const basegfx::B2DPoint & rLogicHitPosition,double fLogicHitTolerance,bool bHitTextOnly)49 		HitTestProcessor2D::HitTestProcessor2D(const geometry::ViewInformation2D& rViewInformation,
50 			const basegfx::B2DPoint& rLogicHitPosition,
51 			double fLogicHitTolerance,
52             bool bHitTextOnly)
53 		:	BaseProcessor2D(rViewInformation),
54 			maDiscreteHitPosition(),
55 			mfDiscreteHitTolerance(0.0),
56 			mbHit(false),
57 			mbHitToleranceUsed(false),
58 			mbUseInvisiblePrimitiveContent(true),
59             mbHitTextOnly(bHitTextOnly)
60 		{
61 			// init hit tolerance
62 			mfDiscreteHitTolerance = fLogicHitTolerance;
63 
64 			if(basegfx::fTools::less(mfDiscreteHitTolerance, 0.0))
65 			{
66 				// ensure input parameter for hit tolerance is >= 0.0
67 				mfDiscreteHitTolerance = 0.0;
68 			}
69 			else if(basegfx::fTools::more(mfDiscreteHitTolerance, 0.0))
70 			{
71 				// generate discrete hit tolerance
72 				mfDiscreteHitTolerance = (getViewInformation2D().getObjectToViewTransformation()
73 					* basegfx::B2DVector(mfDiscreteHitTolerance, 0.0)).getLength();
74 			}
75 
76 			// gererate discrete hit position
77 			maDiscreteHitPosition = getViewInformation2D().getObjectToViewTransformation() * rLogicHitPosition;
78 
79 			// check if HitTolerance is used
80 			mbHitToleranceUsed = basegfx::fTools::more(getDiscreteHitTolerance(), 0.0);
81 		}
82 
~HitTestProcessor2D()83 		HitTestProcessor2D::~HitTestProcessor2D()
84 		{
85 		}
86 
checkHairlineHitWithTolerance(const basegfx::B2DPolygon & rPolygon,double fDiscreteHitTolerance)87 		bool HitTestProcessor2D::checkHairlineHitWithTolerance(
88 			const basegfx::B2DPolygon& rPolygon,
89 			double fDiscreteHitTolerance)
90 		{
91 			basegfx::B2DPolygon aLocalPolygon(rPolygon);
92 			aLocalPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
93 
94 			// get discrete range
95 			basegfx::B2DRange aPolygonRange(aLocalPolygon.getB2DRange());
96 
97 			if(basegfx::fTools::more(fDiscreteHitTolerance, 0.0))
98 			{
99 				aPolygonRange.grow(fDiscreteHitTolerance);
100 			}
101 
102 			// do rough range test first
103 			if(aPolygonRange.isInside(getDiscreteHitPosition()))
104 			{
105 				// check if a polygon edge is hit
106 				return basegfx::tools::isInEpsilonRange(
107 					aLocalPolygon,
108 					getDiscreteHitPosition(),
109 					fDiscreteHitTolerance);
110 			}
111 
112 			return false;
113 		}
114 
checkFillHitWithTolerance(const basegfx::B2DPolyPolygon & rPolyPolygon,double fDiscreteHitTolerance)115 		bool HitTestProcessor2D::checkFillHitWithTolerance(
116 			const basegfx::B2DPolyPolygon& rPolyPolygon,
117 			double fDiscreteHitTolerance)
118 		{
119 			bool bRetval(false);
120 			basegfx::B2DPolyPolygon aLocalPolyPolygon(rPolyPolygon);
121 			aLocalPolyPolygon.transform(getViewInformation2D().getObjectToViewTransformation());
122 
123 			// get discrete range
124 			basegfx::B2DRange aPolygonRange(aLocalPolyPolygon.getB2DRange());
125 			const bool bDiscreteHitToleranceUsed(basegfx::fTools::more(fDiscreteHitTolerance, 0.0));
126 
127 			if(bDiscreteHitToleranceUsed)
128 			{
129 				aPolygonRange.grow(fDiscreteHitTolerance);
130 			}
131 
132 			// do rough range test first
133 			if(aPolygonRange.isInside(getDiscreteHitPosition()))
134 			{
135 				// if a HitTolerance is given, check for polygon edge hit in epsilon first
136 				if(bDiscreteHitToleranceUsed &&
137 					basegfx::tools::isInEpsilonRange(
138 						aLocalPolyPolygon,
139 						getDiscreteHitPosition(),
140 						fDiscreteHitTolerance))
141 				{
142 					bRetval = true;
143 				}
144 
145 				// check for hit in filled polyPolygon
146 				if(!bRetval && basegfx::tools::isInside(
147 					aLocalPolyPolygon,
148 					getDiscreteHitPosition(),
149 					true))
150 				{
151 					bRetval = true;
152 				}
153 			}
154 
155 			return bRetval;
156 		}
157 
check3DHit(const primitive2d::ScenePrimitive2D & rCandidate)158 		void HitTestProcessor2D::check3DHit(const primitive2d::ScenePrimitive2D& rCandidate)
159 		{
160 			// calculate relative point in unified 2D scene
161             const basegfx::B2DPoint aLogicHitPosition(getViewInformation2D().getInverseObjectToViewTransformation() * getDiscreteHitPosition());
162 
163             // use bitmap check in ScenePrimitive2D
164             bool bTryFastResult(false);
165 
166             if(rCandidate.tryToCheckLastVisualisationDirectHit(aLogicHitPosition, bTryFastResult))
167             {
168                 mbHit = bTryFastResult;
169             }
170             else
171             {
172                 basegfx::B2DHomMatrix aInverseSceneTransform(rCandidate.getObjectTransformation());
173                 aInverseSceneTransform.invert();
174                 const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * aLogicHitPosition);
175 
176                 // check if test point is inside scene's unified area at all
177                 if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0
178                     && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
179                 {
180                     // get 3D view information
181     			    const geometry::ViewInformation3D& rObjectViewInformation3D = rCandidate.getViewInformation3D();
182 
183                     // create HitPoint Front and Back, transform to object coordinates
184                     basegfx::B3DHomMatrix aViewToObject(rObjectViewInformation3D.getObjectToView());
185                     aViewToObject.invert();
186                     const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
187                     const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
188 
189                     if(!aFront.equal(aBack))
190                     {
191         			    const primitive3d::Primitive3DSequence& rPrimitives = rCandidate.getChildren3D();
192 
193                         if(rPrimitives.hasElements())
194                         {
195                             // make BoundVolume empty and overlapping test for speedup
196                             const basegfx::B3DRange aObjectRange(
197                                 drawinglayer::primitive3d::getB3DRangeFromPrimitive3DSequence(
198                                     rPrimitives, rObjectViewInformation3D));
199 
200                             if(!aObjectRange.isEmpty())
201                             {
202                                 const basegfx::B3DRange aFrontBackRange(aFront, aBack);
203 
204                                 if(aObjectRange.overlaps(aFrontBackRange))
205                                 {
206                                     // bound volumes hit, geometric cut tests needed
207                                     drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(
208                                         rObjectViewInformation3D,
209                                         aFront,
210                                         aBack,
211                                         true);
212                                     aCutFindProcessor.process(rPrimitives);
213 
214                                     mbHit = (0 != aCutFindProcessor.getCutPoints().size());
215                                 }
216                             }
217                         }
218                     }
219                 }
220 
221 			    // This is needed to check hit with 3D shadows, too. HitTest is without shadow
222 			    // to keep compatible with previous versions. Keeping here as reference
223 			    //
224                 // if(!getHit())
225                 // {
226                 //     // if scene has shadow, check hit with shadow, too
227 		        //     const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rCandidate.getShadow2D(getViewInformation2D()));
228 			    //
229                 //     if(xExtracted2DSceneShadow.hasElements())
230 	            //     {
231 		        //         // process extracted 2D content
232 		        //         process(xExtracted2DSceneShadow);
233 	            //     }
234                 // }
235 
236                 if(!getHit())
237                 {
238 		            // empty 3D scene; Check for border hit
239 		            basegfx::B2DPolygon aOutline(basegfx::tools::createUnitPolygon());
240                     aOutline.transform(rCandidate.getObjectTransformation());
241 
242 		            mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
243                 }
244 
245 			    // This is what the previous version did. Keeping it here for reference
246 			    //
247 			    // // 2D Scene primitive containing 3D stuff; extract 2D contour in world coordinates
248 	            // // This may be refined later to an own 3D HitTest renderer which processes the 3D
249 	            // // geometry directly
250 	            // const primitive2d::ScenePrimitive2D& rScenePrimitive2DCandidate(static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate));
251 	            // const primitive2d::Primitive2DSequence xExtracted2DSceneGeometry(rScenePrimitive2DCandidate.getGeometry2D());
252 	            // const primitive2d::Primitive2DSequence xExtracted2DSceneShadow(rScenePrimitive2DCandidate.getShadow2D(getViewInformation2D()));
253 			    //
254 	            // if(xExtracted2DSceneGeometry.hasElements() || xExtracted2DSceneShadow.hasElements())
255 	            // {
256 		        //     // process extracted 2D content
257 		        //     process(xExtracted2DSceneGeometry);
258 		        //     process(xExtracted2DSceneShadow);
259 	            // }
260 	            // else
261 	            // {
262 		        //     // empty 3D scene; Check for border hit
263 		        //     const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
264                 //     if(!aRange.isEmpty())
265                 //     {
266 	            //          const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
267 	            //          mbHit = checkHairlineHitWithTolerance(aOutline, getDiscreteHitTolerance());
268                 //     }
269 	            // }
270             }
271 		}
272 
processBasePrimitive2D(const primitive2d::BasePrimitive2D & rCandidate)273 		void HitTestProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate)
274 		{
275             if(getHit())
276 			{
277 				// stop processing as soon as a hit was recognized
278 				return;
279 			}
280 
281 			switch(rCandidate.getPrimitive2DID())
282 			{
283 				case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D :
284 				{
285 					// remember current ViewInformation2D
286 					const primitive2d::TransformPrimitive2D& rTransformCandidate(static_cast< const primitive2d::TransformPrimitive2D& >(rCandidate));
287 					const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
288 
289 					// create new local ViewInformation2D containing transformation
290 					const geometry::ViewInformation2D aViewInformation2D(
291 						getViewInformation2D().getObjectTransformation() * rTransformCandidate.getTransformation(),
292 						getViewInformation2D().getViewTransformation(),
293 						getViewInformation2D().getViewport(),
294 						getViewInformation2D().getVisualizedPage(),
295 						getViewInformation2D().getViewTime(),
296 						getViewInformation2D().getExtendedInformationSequence());
297 					updateViewInformation(aViewInformation2D);
298 
299 					// process child content recursively
300 					process(rTransformCandidate.getChildren());
301 
302 					// restore transformations
303 					updateViewInformation(aLastViewInformation2D);
304 
305 					break;
306 				}
307 				case PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D :
308 				{
309                     if(!getHitTextOnly())
310                     {
311 					    // create hairline in discrete coordinates
312 					    const primitive2d::PolygonHairlinePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonHairlinePrimitive2D& >(rCandidate));
313 
314 					    // use hairline test
315     					mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
316                     }
317 
318 					break;
319 				}
320 				case PRIMITIVE2D_ID_POLYGONMARKERPRIMITIVE2D :
321 				{
322                     if(!getHitTextOnly())
323                     {
324 					    // handle marker like hairline; no need to decompose in dashes
325 					    const primitive2d::PolygonMarkerPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonMarkerPrimitive2D& >(rCandidate));
326 
327     					// use hairline test
328     					mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
329                     }
330 
331 					break;
332 				}
333 				case PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D :
334 				{
335                     if(!getHitTextOnly())
336                     {
337 					    // handle stroke evtl. directly; no need to decompose to filled polygon outlines
338 					    const primitive2d::PolygonStrokePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonStrokePrimitive2D& >(rCandidate));
339 					    const attribute::LineAttribute& rLineAttribute = rPolygonCandidate.getLineAttribute();
340 
341 					    if(basegfx::fTools::more(rLineAttribute.getWidth(), 0.0))
342 					    {
343 						    if(basegfx::B2DLINEJOIN_MITER == rLineAttribute.getLineJoin())
344 						    {
345 							    // if line is mitered, use decomposition since mitered line
346 							    // geometry may use more space than the geometry grown by half line width
347 							    process(rCandidate.get2DDecomposition(getViewInformation2D()));
348 						    }
349 						    else
350 						    {
351 							    // for all other B2DLINEJOIN_* do a hairline HitTest with expanded tolerance
352 							    const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
353 								    * basegfx::B2DVector(rLineAttribute.getWidth() * 0.5, 0.0));
354 							    mbHit = checkHairlineHitWithTolerance(
355 								    rPolygonCandidate.getB2DPolygon(),
356 								    getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
357 						    }
358 					    }
359 					    else
360 					    {
361 						    // hairline; fallback to hairline test. Do not decompose
362 						    // since this may decompose the hairline to dashes
363 						    mbHit = checkHairlineHitWithTolerance(rPolygonCandidate.getB2DPolygon(), getDiscreteHitTolerance());
364 					    }
365                     }
366 
367 					break;
368 				}
369 				case PRIMITIVE2D_ID_POLYGONWAVEPRIMITIVE2D :
370 				{
371                     if(!getHitTextOnly())
372                     {
373 					    // do not use decompose; just handle like a line with width
374 					    const primitive2d::PolygonWavePrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolygonWavePrimitive2D& >(rCandidate));
375 					    double fLogicHitTolerance(0.0);
376 
377 					    // if WaveHeight, grow by it
378 					    if(basegfx::fTools::more(rPolygonCandidate.getWaveHeight(), 0.0))
379 					    {
380 						    fLogicHitTolerance += rPolygonCandidate.getWaveHeight();
381 					    }
382 
383 					    // if line width, grow by it
384 					    if(basegfx::fTools::more(rPolygonCandidate.getLineAttribute().getWidth(), 0.0))
385 					    {
386 						    fLogicHitTolerance += rPolygonCandidate.getLineAttribute().getWidth() * 0.5;
387 					    }
388 
389 					    const basegfx::B2DVector aDiscreteHalfLineVector(getViewInformation2D().getObjectToViewTransformation()
390 						    * basegfx::B2DVector(fLogicHitTolerance, 0.0));
391 
392 					    mbHit = checkHairlineHitWithTolerance(
393 						    rPolygonCandidate.getB2DPolygon(),
394 						    getDiscreteHitTolerance() + aDiscreteHalfLineVector.getLength());
395                     }
396 
397 					break;
398 				}
399 				case PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D :
400 				{
401                     if(!getHitTextOnly())
402                     {
403 					    // create filled polyPolygon in discrete coordinates
404 					    const primitive2d::PolyPolygonColorPrimitive2D& rPolygonCandidate(static_cast< const primitive2d::PolyPolygonColorPrimitive2D& >(rCandidate));
405 
406 					    // use fill hit test
407     					mbHit = checkFillHitWithTolerance(rPolygonCandidate.getB2DPolyPolygon(), getDiscreteHitTolerance());
408                     }
409 
410 					break;
411 				}
412 				case PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D :
413 				{
414 					// sub-transparence group
415 					const primitive2d::TransparencePrimitive2D& rTransCandidate(static_cast< const primitive2d::TransparencePrimitive2D& >(rCandidate));
416 
417 					// Currently the transparence content is not taken into account; only
418 					// the children are recursively checked for hit. This may be refined for
419 					// parts where the content is completely transparent if needed.
420 					process(rTransCandidate.getChildren());
421 
422 					break;
423 				}
424 				case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
425 				{
426 					// create mask in discrete coordinates; only recursively continue
427 					// with content when HitTest position is inside the mask
428 					const primitive2d::MaskPrimitive2D& rMaskCandidate(static_cast< const primitive2d::MaskPrimitive2D& >(rCandidate));
429 
430 					// use fill hit test
431 					if(checkFillHitWithTolerance(rMaskCandidate.getMask(), getDiscreteHitTolerance()))
432 					{
433 						// recursively HitTest children
434 						process(rMaskCandidate.getChildren());
435 					}
436 
437 					break;
438 				}
439 				case PRIMITIVE2D_ID_SCENEPRIMITIVE2D :
440 				{
441                     if(!getHitTextOnly())
442                     {
443 				        const primitive2d::ScenePrimitive2D& rScenePrimitive2D(
444                             static_cast< const primitive2d::ScenePrimitive2D& >(rCandidate));
445 						check3DHit(rScenePrimitive2D);
446                     }
447 
448 					break;
449 				}
450                 case PRIMITIVE2D_ID_WRONGSPELLPRIMITIVE2D :
451 				case PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D :
452 				case PRIMITIVE2D_ID_GRIDPRIMITIVE2D :
453 				case PRIMITIVE2D_ID_HELPLINEPRIMITIVE2D :
454                 {
455 					// ignorable primitives
456 					break;
457 				}
458 				case PRIMITIVE2D_ID_SHADOWPRIMITIVE2D :
459                 {
460 					// Ignore shadows; we do not want to have shadows hittable.
461 					// Remove this one to make shadows hittable on demand.
462 					break;
463 				}
464 				case PRIMITIVE2D_ID_TEXTSIMPLEPORTIONPRIMITIVE2D :
465 				case PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D :
466                 {
467                     // for text use the BoundRect of the primitive itself
468 				    const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
469 
470                     if(!aRange.isEmpty())
471                     {
472 				        const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
473 				        mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
474                     }
475 
476 					break;
477 				}
478 				case PRIMITIVE2D_ID_BITMAPPRIMITIVE2D :
479                 {
480                     if(!getHitTextOnly())
481                     {
482                         // The recently added BitmapEx::GetTransparency() makes it easy to extend
483                         // the BitmapPrimitive2D HitTest to take the contained BotmapEx and it's
484                         // transparency into account
485 					    const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
486 
487                         if(!aRange.isEmpty())
488                         {
489     					    const primitive2d::BitmapPrimitive2D& rBitmapCandidate(static_cast< const primitive2d::BitmapPrimitive2D& >(rCandidate));
490             			    const BitmapEx& rBitmapEx = rBitmapCandidate.getBitmapEx();
491                             const Size& rSizePixel(rBitmapEx.GetSizePixel());
492 
493                             if(rSizePixel.Width() && rSizePixel.Height())
494                             {
495                                 basegfx::B2DHomMatrix aBackTransform(
496                                     getViewInformation2D().getObjectToViewTransformation() *
497                                     rBitmapCandidate.getTransform());
498                                 aBackTransform.invert();
499 
500                                 const basegfx::B2DPoint aRelativePoint(aBackTransform * getDiscreteHitPosition());
501                                 const basegfx::B2DRange aUnitRange(0.0, 0.0, 1.0, 1.0);
502 
503                                 if(aUnitRange.isInside(aRelativePoint))
504                                 {
505                                     const sal_Int32 nX(basegfx::fround(aRelativePoint.getX() * rSizePixel.Width()));
506                                     const sal_Int32 nY(basegfx::fround(aRelativePoint.getY() * rSizePixel.Height()));
507 
508                                     mbHit = (0xff != rBitmapEx.GetTransparency(nX, nY));
509                                 }
510                             }
511                             else
512                             {
513                                 // fallback to standard HitTest
514 					            const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
515 					            mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
516                             }
517                         }
518                     }
519 
520                     break;
521                 }
522 				case PRIMITIVE2D_ID_METAFILEPRIMITIVE2D :
523 				case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D :
524 				case PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D :
525 				case PRIMITIVE2D_ID_FILLHATCHPRIMITIVE2D :
526 				case PRIMITIVE2D_ID_PAGEPREVIEWPRIMITIVE2D :
527                 case PRIMITIVE2D_ID_MEDIAPRIMITIVE2D:
528                 {
529                     if(!getHitTextOnly())
530                     {
531 					    // Class of primitives for which just the BoundRect of the primitive itself
532 					    // will be used for HitTest currently.
533 					    //
534 					    // This may be refined in the future, e.g:
535 					    // - For Bitamps, the mask and/or transparence information may be used
536 					    // - For MetaFiles, the MetaFile content may be used
537 					    const basegfx::B2DRange aRange(rCandidate.getB2DRange(getViewInformation2D()));
538 
539                         if(!aRange.isEmpty())
540                         {
541 					        const basegfx::B2DPolygon aOutline(basegfx::tools::createPolygonFromRect(aRange));
542 					        mbHit = checkFillHitWithTolerance(basegfx::B2DPolyPolygon(aOutline), getDiscreteHitTolerance());
543                         }
544                     }
545 
546 					break;
547 				}
548 				case PRIMITIVE2D_ID_HIDDENGEOMETRYPRIMITIVE2D :
549 				{
550 					// HiddenGeometryPrimitive2D; the default decomposition would return an empty seqence,
551 					// so force this primitive to process it's children directly if the switch is set
552 					// (which is the default). Else, ignore invisible content
553 				    const primitive2d::HiddenGeometryPrimitive2D& rHiddenGeometry(static_cast< const primitive2d::HiddenGeometryPrimitive2D& >(rCandidate));
554        			    const primitive2d::Primitive2DSequence& rChildren = rHiddenGeometry.getChildren();
555 
556                     if(rChildren.hasElements())
557                     {
558                         if(getUseInvisiblePrimitiveContent())
559 					    {
560                             process(rChildren);
561 					    }
562                     }
563 
564 					break;
565 				}
566 				case PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D :
567 				{
568                     if(!getHitTextOnly())
569                     {
570 					    const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate(static_cast< const primitive2d::PointArrayPrimitive2D& >(rCandidate));
571 					    const std::vector< basegfx::B2DPoint >& rPositions = rPointArrayCandidate.getPositions();
572 					    const sal_uInt32 nCount(rPositions.size());
573 
574 					    for(sal_uInt32 a(0); !getHit() && a < nCount; a++)
575 					    {
576 						    const basegfx::B2DPoint aPosition(getViewInformation2D().getObjectToViewTransformation() * rPositions[a]);
577 						    const basegfx::B2DVector aDistance(aPosition - getDiscreteHitPosition());
578 
579 						    if(aDistance.getLength() <= getDiscreteHitTolerance())
580 						    {
581 							    mbHit = true;
582 						    }
583 					    }
584                     }
585 
586 					break;
587 				}
588 				default :
589 				{
590 					// process recursively
591 					process(rCandidate.get2DDecomposition(getViewInformation2D()));
592 
593 					break;
594 				}
595 			}
596 		}
597 
598 	} // end of namespace processor2d
599 } // end of namespace drawinglayer
600 
601 //////////////////////////////////////////////////////////////////////////////
602 // eof
603