1 /*
2    (c) Copyright 2001-2009  The world wide DirectFB Open Source Community (directfb.org)
3    (c) Copyright 2000-2004  Convergence (integrated media) GmbH
4 
5    All rights reserved.
6 
7    Written by Denis Oliver Kropp <dok@directfb.org>,
8               Andreas Hundt <andi@fischlustig.de>,
9               Sven Neumann <neo@directfb.org>,
10               Ville Syrjälä <syrjala@sci.fi> and
11               Claudio Ciccani <klan@users.sf.net>.
12 
13    This library is free software; you can redistribute it and/or
14    modify it under the terms of the GNU Lesser General Public
15    License as published by the Free Software Foundation; either
16    version 2 of the License, or (at your option) any later version.
17 
18    This library is distributed in the hope that it will be useful,
19    but WITHOUT ANY WARRANTY; without even the implied warranty of
20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21    Lesser General Public License for more details.
22 
23    You should have received a copy of the GNU Lesser General Public
24    License along with this library; if not, write to the
25    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26    Boston, MA 02111-1307, USA.
27 */
28 
29 #include <config.h>
30 
31 #include <string.h>
32 
33 #include <directfb.h>
34 
35 #include <direct/util.h>
36 
37 #include <misc/util.h>
38 
39 #include <gfx/clip.h>
40 #include <gfx/util.h>
41 
42 D_DEBUG_DOMAIN( Core_Clipping,    "Core/Clipping",    "DirectFB Software Clipping" );
43 
44 #define REGION_CODE(x,y,cx1,cx2,cy1,cy2) ( ( (y) > (cy2) ? 8 : 0) | \
45                                            ( (y) < (cy1) ? 4 : 0) | \
46                                            ( (x) > (cx2) ? 2 : 0) | \
47                                            ( (x) < (cx1) ? 1 : 0) )
48 
49 
50 DFBBoolean
dfb_clip_line(const DFBRegion * clip,DFBRegion * line)51 dfb_clip_line( const DFBRegion *clip, DFBRegion *line )
52 {
53      unsigned char region_code1 = REGION_CODE( line->x1, line->y1,
54                                                clip->x1,
55                                                clip->x2,
56                                                clip->y1,
57                                                clip->y2 );
58 
59      unsigned char region_code2 = REGION_CODE( line->x2, line->y2,
60                                                clip->x1,
61                                                clip->x2,
62                                                clip->y1,
63                                                clip->y2 );
64 
65      while (region_code1 | region_code2) {
66           /* line completely outside the clipping rectangle */
67           if (region_code1 & region_code2)
68                return DFB_FALSE;
69 
70 
71           if (region_code1) {
72                if (region_code1 & 8) { /* divide line at bottom*/
73                     line->x1 = line->x1 +(line->x2-line->x1) * (clip->y2 - line->y1) / (line->y2-line->y1);
74                     line->y1 = clip->y2;
75                }
76                else
77                     if (region_code1 & 4) { /* divide line at top*/
78                     line->x1 = line->x1 +(line->x2-line->x1) * (clip->y1 - line->y1) / (line->y2-line->y1);
79                     line->y1 = clip->y1;
80                }
81                else
82                     if (region_code1 & 2) { /* divide line at right*/
83                     line->y1 = line->y1 +(line->y2-line->y1) * (clip->x2 - line->x1) / (line->x2-line->x1);
84                     line->x1 = clip->x2;
85                }
86                else
87                     if (region_code1 & 1) { /* divide line at right*/
88                     line->y1 = line->y1 +(line->y2-line->y1) * (clip->x1 - line->x1) / (line->x2-line->x1);
89                     line->x1 = clip->x1;
90                }
91                region_code1 = REGION_CODE( line->x1, line->y1,
92                                            clip->x1,
93                                            clip->x2,
94                                            clip->y1,
95                                            clip->y2 );
96           }
97           else {
98                if (region_code2 & 8) {  /* divide line at bottom*/
99                     line->x2 = line->x1 +(line->x2-line->x1) * (clip->y2 - line->y1) / (line->y2-line->y1);
100                     line->y2 = clip->y2;
101                }
102                else
103                     if (region_code2 & 4) { /* divide line at top*/
104                     line->x2 = line->x1 +(line->x2-line->x1) * (clip->y1 - line->y1) / (line->y2-line->y1);
105                     line->y2 = clip->y1;
106                }
107                else
108                     if (region_code2 & 2) { /* divide line at right*/
109                     line->y2 = line->y1 +(line->y2-line->y1) * (clip->x2 - line->x1) / (line->x2-line->x1);
110                     line->x2 = clip->x2;
111                }
112                else
113                     if (region_code2 & 1) { /* divide line at right*/
114                     line->y2 = line->y1 +(line->y2-line->y1) * (clip->x1 - line->x1) / (line->x2-line->x1);
115                     line->x2 = clip->x1;
116                }
117                region_code2 = REGION_CODE( line->x2, line->y2, clip->x1,
118                                                      clip->x2,
119                                                      clip->y1,
120                                                      clip->y2 );
121           }
122      }
123 
124      /* successfully clipped or clipping not neccessary */
125      return DFB_TRUE;
126 }
127 
128 DFBEdgeFlags
dfb_clip_edges(const DFBRegion * clip,DFBRectangle * rect)129 dfb_clip_edges( const DFBRegion *clip, DFBRectangle *rect )
130 {
131      DFBEdgeFlags flags = DFEF_ALL;
132 
133      if ((clip->x1 >= rect->x + rect->w) ||
134          (clip->x2 < rect->x) ||
135          (clip->y1 >= rect->y + rect->h) ||
136          (clip->y2 < rect->y))
137           return DFEF_NONE;
138 
139      if (clip->x1 > rect->x) {
140           rect->w += rect->x - clip->x1;
141           rect->x = clip->x1;
142 
143           flags &= ~DFEF_LEFT;
144      }
145 
146      if (clip->y1 > rect->y) {
147           rect->h += rect->y - clip->y1;
148           rect->y = clip->y1;
149 
150           flags &= ~DFEF_TOP;
151      }
152 
153      if (clip->x2 < rect->x + rect->w - 1) {
154           rect->w = clip->x2 - rect->x + 1;
155 
156           flags &= ~DFEF_RIGHT;
157      }
158 
159      if (clip->y2 < rect->y + rect->h - 1) {
160           rect->h = clip->y2 - rect->y + 1;
161 
162           flags &= ~DFEF_BOTTOM;
163      }
164 
165      return flags;
166 }
167 
168 DFBBoolean
dfb_clip_rectangle(const DFBRegion * clip,DFBRectangle * rect)169 dfb_clip_rectangle( const DFBRegion *clip, DFBRectangle *rect )
170 {
171      if ((clip->x1 >= rect->x + rect->w) ||
172          (clip->x2 < rect->x) ||
173          (clip->y1 >= rect->y + rect->h) ||
174          (clip->y2 < rect->y))
175           return DFB_FALSE;
176 
177      if (clip->x1 > rect->x) {
178           rect->w += rect->x - clip->x1;
179           rect->x = clip->x1;
180      }
181 
182      if (clip->y1 > rect->y) {
183           rect->h += rect->y - clip->y1;
184           rect->y = clip->y1;
185      }
186 
187      if (clip->x2 < rect->x + rect->w - 1)
188           rect->w = clip->x2 - rect->x + 1;
189 
190      if (clip->y2 < rect->y + rect->h - 1)
191           rect->h = clip->y2 - rect->y + 1;
192 
193      return DFB_TRUE;
194 }
195 
196 DFBBoolean
dfb_clip_triangle_precheck(const DFBRegion * clip,const DFBTriangle * tri)197 dfb_clip_triangle_precheck( const DFBRegion *clip, const DFBTriangle *tri )
198 {
199     int x, y, w, h;
200 
201     x = MIN (MIN (tri->x1, tri->x2), tri->x3);
202     y = MIN (MIN (tri->y1, tri->y2), tri->y3);
203     w = MAX (MAX (tri->x1, tri->x2), tri->x3) - x;
204     h = MAX (MAX (tri->y1, tri->y2), tri->y3) - y;
205 
206     if (clip->x1 > x ||
207         clip->x2 < x + w ||
208         clip->y1 > y ||
209         clip->y2 < y + h)
210       return DFB_FALSE;
211 
212     return DFB_TRUE;
213 }
214 
215 DFBBoolean
dfb_clip_triangle(const DFBRegion * clip,const DFBTriangle * tri,DFBPoint p[6],int * num)216 dfb_clip_triangle( const DFBRegion *clip, const DFBTriangle *tri, DFBPoint p[6], int *num )
217 {
218      DFBRegion edges[3];
219      int       num_edges;
220      int       i, n;
221      DFBPoint  p1 = {0, 0}, p2 = {0, 0};
222 
223      /* Initialize edges. */
224      edges[0].x1 = tri->x1; edges[0].y1 = tri->y1;
225      edges[0].x2 = tri->x2; edges[0].y2 = tri->y2;
226      edges[1].x1 = tri->x2; edges[1].y1 = tri->y2;
227      edges[1].x2 = tri->x3; edges[1].y2 = tri->y3;
228      edges[2].x1 = tri->x3; edges[2].y1 = tri->y3;
229      edges[2].x2 = tri->x1; edges[2].y2 = tri->y1;
230      num_edges = 3;
231 
232      for (i = 0; i < num_edges; i++) {
233           DFBRegion *reg = &edges[i];
234           DFBRegion  line;
235           bool       i1, i2;
236 
237           /* Clip the edge to the clipping region. */
238           line = *reg;
239           if (dfb_clip_line( clip, &line )) {
240                *reg = line;
241                continue;
242           }
243 
244           /* If the edge doesn't intersect clipping region, then
245            * intersect the edge with the diagonals of the clipping
246            * rectangle. If intersection point exits, add the nearest
247            * corner of the clipping region to the list of vertices.
248            */
249 
250           /* Diagonal (x1,y1) (x2,y2). */
251           line = (DFBRegion) { clip->x1, clip->y1, clip->x2, clip->y2 };
252           i1 = dfb_line_segment_intersect( &line, reg, &p1.x, &p1.y );
253           if (i1) {
254                /* Get nearest corner. */
255                if (p1.x <= clip->x1 || p1.y <= clip->y1) {
256                     p1.x = clip->x1;
257                     p1.y = clip->y1;
258                } else {
259                     p1.x = clip->x2;
260                     p1.y = clip->y2;
261                }
262           }
263 
264           /* Diagonal (x2,y1) (x1,y2). */
265           line = (DFBRegion) { clip->x2, clip->y1, clip->x1, clip->y2 };
266           i2 = dfb_line_segment_intersect( &line, reg, &p2.x, &p2.y );
267           if (i2) {
268                /* Get nearest corner. */
269                if (p2.x >= clip->x2 || p2.y <= clip->y1) {
270                     p2.x = clip->x2;
271                     p2.y = clip->y1;
272                } else {
273                     p2.x = clip->x1;
274                     p2.y = clip->y2;
275                }
276           }
277 
278           if (i1 && i2) {
279                reg->x1 = p1.x;
280                reg->y1 = p1.y;
281                reg->x2 = p2.x;
282                reg->y2 = p2.y;
283           }
284           else if (i1) {
285                reg->x1 = reg->x2 = p1.x;
286                reg->y1 = reg->y2 = p1.y;
287           }
288           else if (i2) {
289                reg->x1 = reg->x2 = p2.x;
290                reg->y1 = reg->y2 = p2.y;
291           }
292           else {
293                /* Redudant edge. Remote it. */
294                memmove( reg, &edges[i+1], (num_edges-i-1) * sizeof(DFBRegion) );
295                num_edges--;
296                i--;
297           }
298      }
299 
300      if (num_edges < 1) {
301           *num = 0;
302           return DFB_FALSE;
303      }
304 
305      /* Get vertices from edges. */
306      p[0].x = edges[0].x1; p[0].y = edges[0].y1;
307      n = 1;
308      if (edges[0].x2 != edges[0].x1 || edges[0].y2 != edges[0].y1) {
309           p[1].x = edges[0].x2; p[1].y = edges[0].y2;
310           n++;
311      }
312 
313      for (i = 1; i < num_edges; i++) {
314           if (edges[i].x1 != p[n-1].x || edges[i].y1 != p[n-1].y) {
315                p[n].x = edges[i].x1; p[n].y = edges[i].y1;
316                n++;
317           }
318           if (edges[i].x2 != p[n-1].x || edges[i].y2 != p[n-1].y) {
319                p[n].x = edges[i].x2; p[n].y = edges[i].y2;
320                n++;
321           }
322      }
323 
324      if (p[n-1].x == p[0].x && p[n-1].y == p[0].y)
325           n--;
326 
327      *num = n;
328 
329      /* Actually fail if the number of vertices is below 3. */
330      return (n >= 3);
331 }
332 
333 
334 void
dfb_clip_blit(const DFBRegion * clip,DFBRectangle * srect,int * dx,int * dy)335 dfb_clip_blit( const DFBRegion *clip,
336                DFBRectangle *srect, int *dx, int *dy )
337 {
338      if (clip->x1 > *dx ) {
339           srect->w = MIN( (clip->x2 - clip->x1) + 1,
340                     (*dx + srect->w) - clip->x1);
341 
342           srect->x+= clip->x1 - *dx;
343           *dx = clip->x1;
344      }
345      else if (clip->x2 < *dx + srect->w - 1) {
346           srect->w = clip->x2 - *dx + 1;
347      }
348 
349      if (clip->y1 > *dy ) {
350           srect->h = MIN( (clip->y2 - clip->y1) + 1,
351                           (*dy + srect->h) - clip->y1);
352           srect->y+= clip->y1 - *dy;
353           *dy = clip->y1;
354      }
355      else if (clip->y2 < *dy + srect->h - 1) {
356           srect->h = clip->y2 - *dy + 1;
357      }
358 }
359 
360 void
dfb_clip_stretchblit(const DFBRegion * clip,DFBRectangle * srect,DFBRectangle * drect)361 dfb_clip_stretchblit( const DFBRegion *clip,
362                       DFBRectangle *srect, DFBRectangle *drect )
363 {
364      DFBRectangle orig_dst = *drect;
365 
366      dfb_clip_rectangle( clip, drect );
367 
368      if (drect->x != orig_dst.x)
369           srect->x += (int)( (drect->x - orig_dst.x) *
370                              (srect->w / (float)orig_dst.w) );
371 
372      if (drect->y != orig_dst.y)
373           srect->y += (int)( (drect->y - orig_dst.y) *
374                              (srect->h / (float)orig_dst.h) );
375 
376      if (drect->w != orig_dst.w)
377           srect->w = (int)( srect->w * (drect->w / (float)orig_dst.w) );
378 
379      if (drect->h != orig_dst.h)
380           srect->h = (int)( srect->h * (drect->h / (float)orig_dst.h) );
381 }
382 
383 
384 void
dfb_clip_blit_flipped_rotated(const DFBRegion * clip,DFBRectangle * srect,DFBRectangle * drect,DFBSurfaceBlittingFlags flags)385 dfb_clip_blit_flipped_rotated( const DFBRegion *clip,
386                          DFBRectangle *srect, DFBRectangle *drect, DFBSurfaceBlittingFlags flags )
387 {
388 
389      DFBRegion dest    = DFB_REGION_INIT_FROM_RECTANGLE( drect );
390      DFBRegion clipped = dest;
391 
392      D_ASSERT( !(flags & (DSBLIT_ROTATE270 | DSBLIT_ROTATE180)) );
393 
394      if (flags & DSBLIT_ROTATE90) {
395           D_ASSERT( srect->w == drect->h );
396           D_ASSERT( srect->h == drect->w );
397      }
398      else {
399           D_ASSERT( srect->w == drect->w );
400           D_ASSERT( srect->h == drect->h );
401      }
402 
403      dfb_region_region_intersect( &clipped, clip );
404      dfb_rectangle_from_region( drect, &clipped );
405 
406      switch (flags & (DSBLIT_FLIP_HORIZONTAL | DSBLIT_FLIP_VERTICAL | DSBLIT_ROTATE90)) {
407           case DSBLIT_NOFX:
408                srect->x += clipped.x1 - dest.x1;
409                srect->y += clipped.y1 - dest.y1;
410                break;
411           case DSBLIT_FLIP_HORIZONTAL:
412                srect->x += dest.x2 - clipped.x2;
413                srect->y += clipped.y1 - dest.y1;
414                break;
415           case DSBLIT_FLIP_VERTICAL:
416                srect->x += clipped.x1 - dest.x1;
417                srect->y += dest.y2 - clipped.y2;
418                break;
419           case DSBLIT_ROTATE90:
420                srect->x += dest.y2 - clipped.y2;
421                srect->y += clipped.x1 - dest.x1;
422                break;
423           case (DSBLIT_FLIP_HORIZONTAL | DSBLIT_FLIP_VERTICAL): // ROTATE180
424                srect->x += dest.x2 - clipped.x2;
425                srect->y += dest.y2 - clipped.y2;
426                break;
427           case (DSBLIT_ROTATE90 | DSBLIT_FLIP_VERTICAL | DSBLIT_FLIP_HORIZONTAL): // ROTATE270
428                srect->x += clipped.y1 - dest.y1;
429                srect->y += dest.x2 - clipped.x2;
430                break;
431           case (DSBLIT_ROTATE90 | DSBLIT_FLIP_HORIZONTAL):
432                srect->x += clipped.y1 - dest.y1;
433                srect->y += clipped.x1 - dest.x1;
434                break;
435           case (DSBLIT_ROTATE90 | DSBLIT_FLIP_VERTICAL):
436                srect->x += dest.y2 - clipped.y2;
437                srect->y += dest.x2 - clipped.x2;
438                break;
439      }
440 
441      if (flags & DSBLIT_ROTATE90) {
442           srect->w  = drect->h;
443           srect->h  = drect->w;
444      }
445      else {
446           srect->w = drect->w;
447           srect->h = drect->h;
448      }
449 
450      D_DEBUG_AT( Core_Clipping, "  => %4d,%4d-%4dx%4d -> %4d,%4d-%4dx%4d\n",
451                  DFB_RECTANGLE_VALS(srect), DFB_RECTANGLE_VALS(drect) );
452 }
453