1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/region.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Modified: VZ at 05.10.00: use AllocExclusive(), comparison fixed
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21 #include "wx/region.h"
22
23 #include <gdk/gdk.h>
24
25 // ----------------------------------------------------------------------------
26 // wxRegionRefData: private class containing the information about the region
27 // ----------------------------------------------------------------------------
28
29 class wxRegionRefData : public wxGDIRefData
30 {
31 public:
wxRegionRefData()32 wxRegionRefData()
33 {
34 m_region = NULL;
35 }
36
wxRegionRefData(const wxRegionRefData & refData)37 wxRegionRefData(const wxRegionRefData& refData)
38 : wxGDIRefData()
39 {
40 #ifdef __WXGTK3__
41 m_region = cairo_region_copy(refData.m_region);
42 #else
43 m_region = gdk_region_copy(refData.m_region);
44 #endif
45 }
46
~wxRegionRefData()47 virtual ~wxRegionRefData()
48 {
49 if (m_region)
50 {
51 #ifdef __WXGTK3__
52 cairo_region_destroy(m_region);
53 #else
54 gdk_region_destroy( m_region );
55 #endif
56 }
57 }
58
59 #ifdef __WXGTK3__
60 cairo_region_t* m_region;
61 #else
62 GdkRegion *m_region;
63 #endif
64 };
65
66 // ----------------------------------------------------------------------------
67 // macros
68 // ----------------------------------------------------------------------------
69
70 #define M_REGIONDATA static_cast<wxRegionRefData*>(m_refData)
71 #define M_REGIONDATA_OF(r) static_cast<wxRegionRefData*>(r.m_refData)
72
73 wxIMPLEMENT_DYNAMIC_CLASS(wxRegion, wxGDIObject);
74 wxIMPLEMENT_DYNAMIC_CLASS(wxRegionIterator, wxObject);
75
76 // ----------------------------------------------------------------------------
77 // wxRegion construction
78 // ----------------------------------------------------------------------------
79
InitRect(wxCoord x,wxCoord y,wxCoord w,wxCoord h)80 void wxRegion::InitRect(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
81 {
82 // Rectangle needs to be defined in the canonical form,
83 // with (x,y) pointing to the top-left corner of the box
84 // and with non-negative width and height.
85 if ( w < 0 )
86 {
87 w = -w;
88 x -= (w - 1);
89 }
90 if ( h < 0 )
91 {
92 h = -h;
93 y -= (h - 1);
94 }
95
96 GdkRectangle rect;
97 rect.x = x;
98 rect.y = y;
99 rect.width = w;
100 rect.height = h;
101
102 m_refData = new wxRegionRefData();
103
104 #ifdef __WXGTK3__
105 M_REGIONDATA->m_region = cairo_region_create_rectangle(&rect);
106 #else
107 M_REGIONDATA->m_region = gdk_region_rectangle( &rect );
108 #endif
109 }
110
111 #ifndef __WXGTK3__
wxRegion(const GdkRegion * region)112 wxRegion::wxRegion(const GdkRegion* region)
113 {
114 m_refData = new wxRegionRefData();
115 M_REGIONDATA->m_region = gdk_region_copy(const_cast<GdkRegion*>(region));
116 }
117 #endif
118
wxRegion(size_t n,const wxPoint * points,wxPolygonFillMode fillStyle)119 wxRegion::wxRegion( size_t n, const wxPoint *points,
120 wxPolygonFillMode fillStyle )
121 {
122 #ifdef __WXGTK3__
123 // Make a cairo path from the points, draw it onto an image surface, use
124 // gdk_cairo_region_create_from_surface() to get a cairo region
125
126 // need at least 3 points to make a useful polygon
127 if (n < 3)
128 return;
129 // get bounding rect
130 int min_x = points[0].x;
131 int max_x = min_x;
132 int min_y = points[0].y;
133 int max_y = min_y;
134 size_t i;
135 for (i = 1; i < n; i++)
136 {
137 const int x = points[i].x;
138 if (min_x > x)
139 min_x = x;
140 else if (max_x < x)
141 max_x = x;
142 const int y = points[i].y;
143 if (min_y > y)
144 min_y = y;
145 else if (max_y < y)
146 max_y = y;
147 }
148 const int w = max_x - min_x + 1;
149 const int h = max_y - min_y + 1;
150 // make surface just big enough to contain polygon (A1 is native format
151 // for gdk_cairo_region_create_from_surface)
152 cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_A1, w, h);
153 memset(cairo_image_surface_get_data(surface), 0, cairo_image_surface_get_stride(surface) * h);
154 cairo_surface_mark_dirty(surface);
155 cairo_surface_set_device_offset(surface, -min_x, -min_y);
156 cairo_t* cr = cairo_create(surface);
157 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
158 if (fillStyle == wxODDEVEN_RULE)
159 cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
160 // make path
161 cairo_move_to(cr, points[0].x, points[0].y);
162 for (i = 1; i < n; i++)
163 cairo_line_to(cr, points[i].x, points[i].y);
164 cairo_close_path(cr);
165 cairo_fill(cr);
166 cairo_destroy(cr);
167 cairo_surface_flush(surface);
168 m_refData = new wxRegionRefData;
169 M_REGIONDATA->m_region = gdk_cairo_region_create_from_surface(surface);
170 cairo_surface_destroy(surface);
171 #else
172 GdkPoint *gdkpoints = new GdkPoint[n];
173 for ( size_t i = 0 ; i < n ; i++ )
174 {
175 gdkpoints[i].x = points[i].x;
176 gdkpoints[i].y = points[i].y;
177 }
178
179 m_refData = new wxRegionRefData();
180
181 GdkRegion* reg = gdk_region_polygon
182 (
183 gdkpoints,
184 n,
185 fillStyle == wxWINDING_RULE ? GDK_WINDING_RULE
186 : GDK_EVEN_ODD_RULE
187 );
188
189 M_REGIONDATA->m_region = reg;
190
191 delete [] gdkpoints;
192 #endif
193 }
194
~wxRegion()195 wxRegion::~wxRegion()
196 {
197 // m_refData unrefed in ~wxObject
198 }
199
CreateGDIRefData() const200 wxGDIRefData *wxRegion::CreateGDIRefData() const
201 {
202 // should never be called
203 wxFAIL;
204 return NULL;
205 }
206
CloneGDIRefData(const wxGDIRefData * data) const207 wxGDIRefData *wxRegion::CloneGDIRefData(const wxGDIRefData *data) const
208 {
209 return new wxRegionRefData(*static_cast<const wxRegionRefData*>(data));
210 }
211
212 // ----------------------------------------------------------------------------
213 // wxRegion comparison
214 // ----------------------------------------------------------------------------
215
DoIsEqual(const wxRegion & region) const216 bool wxRegion::DoIsEqual(const wxRegion& region) const
217 {
218 #ifdef __WXGTK3__
219 return cairo_region_equal(
220 M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
221 #else
222 return gdk_region_equal(M_REGIONDATA->m_region,
223 M_REGIONDATA_OF(region)->m_region) != 0;
224 #endif
225 }
226
227 // ----------------------------------------------------------------------------
228 // wxRegion operations
229 // ----------------------------------------------------------------------------
230
Clear()231 void wxRegion::Clear()
232 {
233 UnRef();
234 }
235
DoUnionWithRect(const wxRect & r)236 bool wxRegion::DoUnionWithRect(const wxRect& r)
237 {
238 // workaround for a strange GTK/X11 bug: taking union with an empty
239 // rectangle results in an empty region which is definitely not what we
240 // want
241 if ( r.IsEmpty() )
242 return true;
243
244 if ( !m_refData )
245 {
246 InitRect(r.x, r.y, r.width, r.height);
247 }
248 else
249 {
250 AllocExclusive();
251
252 GdkRectangle rect;
253 rect.x = r.x;
254 rect.y = r.y;
255 rect.width = r.width;
256 rect.height = r.height;
257
258 #ifdef __WXGTK3__
259 cairo_region_union_rectangle(M_REGIONDATA->m_region, &rect);
260 #else
261 gdk_region_union_with_rect( M_REGIONDATA->m_region, &rect );
262 #endif
263 }
264
265 return true;
266 }
267
DoUnionWithRegion(const wxRegion & region)268 bool wxRegion::DoUnionWithRegion( const wxRegion& region )
269 {
270 if (region.m_refData == NULL)
271 { }
272 else if (m_refData == NULL)
273 {
274 m_refData = new wxRegionRefData(*M_REGIONDATA_OF(region));
275 }
276 else
277 {
278 AllocExclusive();
279 #ifdef __WXGTK3__
280 cairo_region_union(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
281 #else
282 gdk_region_union( M_REGIONDATA->m_region, region.GetRegion() );
283 #endif
284 }
285
286 return true;
287 }
288
DoIntersect(const wxRegion & region)289 bool wxRegion::DoIntersect( const wxRegion& region )
290 {
291 if (region.m_refData == NULL || m_refData == NULL)
292 return false;
293
294 AllocExclusive();
295
296 #ifdef __WXGTK3__
297 cairo_region_intersect(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
298 #else
299 gdk_region_intersect( M_REGIONDATA->m_region, region.GetRegion() );
300 #endif
301
302 return true;
303 }
304
DoSubtract(const wxRegion & region)305 bool wxRegion::DoSubtract( const wxRegion& region )
306 {
307 if (region.m_refData == NULL || m_refData == NULL)
308 return false;
309
310 AllocExclusive();
311
312 #ifdef __WXGTK3__
313 cairo_region_subtract(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
314 #else
315 gdk_region_subtract( M_REGIONDATA->m_region, region.GetRegion() );
316 #endif
317
318 return true;
319 }
320
DoXor(const wxRegion & region)321 bool wxRegion::DoXor( const wxRegion& region )
322 {
323 if (region.m_refData == NULL)
324 { }
325 else if (m_refData == NULL)
326 {
327 // XOR-ing with an invalid region is the same as XOR-ing with an empty
328 // one, i.e. it is simply a copy.
329 m_refData = new wxRegionRefData(*M_REGIONDATA_OF(region));
330 }
331 else
332 {
333 AllocExclusive();
334
335 #ifdef __WXGTK3__
336 cairo_region_xor(M_REGIONDATA->m_region, M_REGIONDATA_OF(region)->m_region);
337 #else
338 gdk_region_xor( M_REGIONDATA->m_region, region.GetRegion() );
339 #endif
340 }
341
342 return true;
343 }
344
DoOffset(wxCoord x,wxCoord y)345 bool wxRegion::DoOffset( wxCoord x, wxCoord y )
346 {
347 wxCHECK_MSG( m_refData, false, wxS("invalid region") );
348
349 AllocExclusive();
350
351 #ifdef __WXGTK3__
352 cairo_region_translate(M_REGIONDATA->m_region, x, y);
353 #else
354 gdk_region_offset( M_REGIONDATA->m_region, x, y );
355 #endif
356
357 return true;
358 }
359
360 // ----------------------------------------------------------------------------
361 // wxRegion tests
362 // ----------------------------------------------------------------------------
363
DoGetBox(wxCoord & x,wxCoord & y,wxCoord & w,wxCoord & h) const364 bool wxRegion::DoGetBox( wxCoord &x, wxCoord &y, wxCoord &w, wxCoord &h ) const
365 {
366 if ( m_refData )
367 {
368 GdkRectangle rect;
369 #ifdef __WXGTK3__
370 cairo_region_get_extents(M_REGIONDATA->m_region, &rect);
371 #else
372 gdk_region_get_clipbox( M_REGIONDATA->m_region, &rect );
373 #endif
374 x = rect.x;
375 y = rect.y;
376 w = rect.width;
377 h = rect.height;
378
379 return true;
380 }
381 else
382 {
383 x = 0;
384 y = 0;
385 w = -1;
386 h = -1;
387
388 return false;
389 }
390 }
391
IsEmpty() const392 bool wxRegion::IsEmpty() const
393 {
394 #ifdef __WXGTK3__
395 return m_refData == NULL || cairo_region_is_empty(M_REGIONDATA->m_region);
396 #else
397 return m_refData == NULL || gdk_region_empty(M_REGIONDATA->m_region);
398 #endif
399 }
400
DoContainsPoint(wxCoord x,wxCoord y) const401 wxRegionContain wxRegion::DoContainsPoint( wxCoord x, wxCoord y ) const
402 {
403 #ifdef __WXGTK3__
404 if (m_refData == NULL || !cairo_region_contains_point(M_REGIONDATA->m_region, x, y))
405 #else
406 if (m_refData == NULL || !gdk_region_point_in(M_REGIONDATA->m_region, x, y))
407 #endif
408 return wxOutRegion;
409
410 return wxInRegion;
411 }
412
DoContainsRect(const wxRect & r) const413 wxRegionContain wxRegion::DoContainsRect(const wxRect& r) const
414 {
415 if (!m_refData)
416 return wxOutRegion;
417
418 GdkRectangle rect;
419 rect.x = r.x;
420 rect.y = r.y;
421 rect.width = r.width;
422 rect.height = r.height;
423 #ifdef __WXGTK3__
424 switch (cairo_region_contains_rectangle(M_REGIONDATA->m_region, &rect))
425 {
426 case CAIRO_REGION_OVERLAP_IN: return wxInRegion;
427 case CAIRO_REGION_OVERLAP_PART: return wxPartRegion;
428 default: break;
429 }
430 #else
431 GdkOverlapType res = gdk_region_rect_in( M_REGIONDATA->m_region, &rect );
432 switch (res)
433 {
434 case GDK_OVERLAP_RECTANGLE_IN: return wxInRegion;
435 case GDK_OVERLAP_RECTANGLE_OUT: return wxOutRegion;
436 case GDK_OVERLAP_RECTANGLE_PART: return wxPartRegion;
437 }
438 #endif
439 return wxOutRegion;
440 }
441
442 #ifdef __WXGTK3__
GetRegion() const443 cairo_region_t* wxRegion::GetRegion() const
444 #else
445 GdkRegion *wxRegion::GetRegion() const
446 #endif
447 {
448 if (!m_refData)
449 return NULL;
450
451 return M_REGIONDATA->m_region;
452 }
453
454 // ----------------------------------------------------------------------------
455 // wxRegionIterator
456 // ----------------------------------------------------------------------------
457
wxRegionIterator()458 wxRegionIterator::wxRegionIterator()
459 {
460 Init();
461 Reset();
462 }
463
wxRegionIterator(const wxRegion & region)464 wxRegionIterator::wxRegionIterator( const wxRegion& region )
465 {
466 Init();
467 Reset(region);
468 }
469
Init()470 void wxRegionIterator::Init()
471 {
472 m_rects = NULL;
473 m_numRects = 0;
474 }
475
~wxRegionIterator()476 wxRegionIterator::~wxRegionIterator()
477 {
478 wxDELETEA(m_rects);
479 }
480
CreateRects(const wxRegion & region)481 void wxRegionIterator::CreateRects( const wxRegion& region )
482 {
483 wxDELETEA(m_rects);
484 m_numRects = 0;
485
486 #ifdef __WXGTK3__
487 cairo_region_t* cairoRegion = region.GetRegion();
488 if (cairoRegion == NULL)
489 return;
490 m_numRects = cairo_region_num_rectangles(cairoRegion);
491
492 if (m_numRects)
493 {
494 m_rects = new wxRect[m_numRects];
495 for (int i = 0; i < m_numRects; i++)
496 {
497 GdkRectangle gr;
498 cairo_region_get_rectangle(cairoRegion, i, &gr);
499 wxRect &wr = m_rects[i];
500 wr.x = gr.x;
501 wr.y = gr.y;
502 wr.width = gr.width;
503 wr.height = gr.height;
504 }
505 }
506 #else
507 GdkRegion *gdkregion = region.GetRegion();
508 if (!gdkregion)
509 return;
510
511 GdkRectangle* gdkrects;
512 gdk_region_get_rectangles(gdkregion, &gdkrects, &m_numRects);
513
514 if (m_numRects)
515 {
516 m_rects = new wxRect[m_numRects];
517 for (int i = 0; i < m_numRects; ++i)
518 {
519 GdkRectangle &gr = gdkrects[i];
520 wxRect &wr = m_rects[i];
521 wr.x = gr.x;
522 wr.y = gr.y;
523 wr.width = gr.width;
524 wr.height = gr.height;
525 }
526 }
527 g_free( gdkrects );
528 #endif
529 }
530
Reset(const wxRegion & region)531 void wxRegionIterator::Reset( const wxRegion& region )
532 {
533 m_region = region;
534 CreateRects(region);
535 Reset();
536 }
537
HaveRects() const538 bool wxRegionIterator::HaveRects() const
539 {
540 return m_current < m_numRects;
541 }
542
operator ++()543 wxRegionIterator& wxRegionIterator::operator ++ ()
544 {
545 if (HaveRects())
546 ++m_current;
547
548 return *this;
549 }
550
operator ++(int)551 wxRegionIterator wxRegionIterator::operator ++ (int)
552 {
553 wxRegionIterator tmp = *this;
554
555 if (HaveRects())
556 ++m_current;
557
558 return tmp;
559 }
560
GetX() const561 wxCoord wxRegionIterator::GetX() const
562 {
563 wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
564
565 return m_rects[m_current].x;
566 }
567
GetY() const568 wxCoord wxRegionIterator::GetY() const
569 {
570 wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
571
572 return m_rects[m_current].y;
573 }
574
GetW() const575 wxCoord wxRegionIterator::GetW() const
576 {
577 wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
578
579 return m_rects[m_current].width;
580 }
581
GetH() const582 wxCoord wxRegionIterator::GetH() const
583 {
584 wxCHECK_MSG( HaveRects(), 0, wxT("invalid wxRegionIterator") );
585
586 return m_rects[m_current].height;
587 }
588
GetRect() const589 wxRect wxRegionIterator::GetRect() const
590 {
591 wxRect r;
592 if( HaveRects() )
593 r = m_rects[m_current];
594
595 return r;
596 }
597
operator =(const wxRegionIterator & ri)598 wxRegionIterator& wxRegionIterator::operator=(const wxRegionIterator& ri)
599 {
600 if (this != &ri)
601 {
602 wxDELETEA(m_rects);
603
604 m_current = ri.m_current;
605 m_numRects = ri.m_numRects;
606 if ( m_numRects )
607 {
608 m_rects = new wxRect[m_numRects];
609 memcpy(m_rects, ri.m_rects, m_numRects * sizeof m_rects[0]);
610 }
611 }
612 return *this;
613 }
614