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