diff --git a/cogl.h.in b/cogl.h.in index 594fc77ca..b3b0584b0 100644 --- a/cogl.h.in +++ b/cogl.h.in @@ -490,6 +490,27 @@ void cogl_set_source_texture (CoglHandle texture_handle); * intersected with the previous region. */ +/** + * cogl_clip_push_window_rect: + * @x_offset: left edge of the clip rectangle in window coordinates + * @y_offset: top edge of the clip rectangle in window coordinates + * @width: width of the clip rectangle + * @height: height of the clip rectangle + * + * Specifies a rectangular clipping area for all subsequent drawing + * operations. Any drawing commands that extend outside the rectangle + * will be clipped so that only the portion inside the rectangle will + * be displayed. The rectangle dimensions are not transformed by the + * current model-view matrix. + * + * The rectangle is intersected with the current clip region. To undo + * the effect of this function, call cogl_clip_pop(). + */ +void cogl_clip_push_window_rect (float x_offset, + float y_offset, + float width, + float height); + /** * cogl_clip_push: * @x_offset: left edge of the clip rectangle diff --git a/common/cogl-clip-stack.c b/common/cogl-clip-stack.c index 7f3dcdfc0..896fe9bae 100644 --- a/common/cogl-clip-stack.c +++ b/common/cogl-clip-stack.c @@ -26,10 +26,14 @@ #endif #include + +#include + #include "cogl.h" #include "cogl-clip-stack.h" #include "cogl-primitives.h" #include "cogl-context.h" +#include "cogl-internal.h" /* These are defined in the particular backend (float in GL vs fixed in GL ES) */ @@ -55,11 +59,13 @@ void _cogl_set_matrix (const CoglMatrix *matrix); typedef struct _CoglClipStack CoglClipStack; typedef struct _CoglClipStackEntryRect CoglClipStackEntryRect; +typedef struct _CoglClipStackEntryWindowRect CoglClipStackEntryWindowRect; typedef struct _CoglClipStackEntryPath CoglClipStackEntryPath; typedef enum { COGL_CLIP_STACK_RECT, + COGL_CLIP_STACK_WINDOW_RECT, COGL_CLIP_STACK_PATH } CoglClipStackEntryType; @@ -70,7 +76,7 @@ struct _CoglClipStack struct _CoglClipStackEntryRect { - CoglClipStackEntryType type; + CoglClipStackEntryType type; /* The rectangle for this clip */ float x_offset; @@ -82,20 +88,126 @@ struct _CoglClipStackEntryRect CoglMatrix matrix; }; +struct _CoglClipStackEntryWindowRect +{ + CoglClipStackEntryType type; + + /* The window space rectangle for this clip */ + float x0; + float y0; + float x1; + float y1; +}; + struct _CoglClipStackEntryPath { - CoglClipStackEntryType type; + CoglClipStackEntryType type; /* The matrix that was current when the clip was set */ - CoglMatrix matrix; + CoglMatrix matrix; floatVec2 path_nodes_min; floatVec2 path_nodes_max; - guint path_size; - CoglPathNode path[1]; + guint path_size; + CoglPathNode path[1]; }; +void +cogl_clip_push_window_rect (float x_offset, + float y_offset, + float width, + float height) +{ + CoglClipStackEntryWindowRect *entry; + CoglClipStack *stack; + float v[4]; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + stack = (CoglClipStack *) ctx->clip.stacks->data; + + cogl_get_viewport (v); + + entry = g_slice_new (CoglClipStackEntryWindowRect); + + /* We convert from coords with (0,0) at top left to coords + * with (0,0) at bottom left. */ + entry->type = COGL_CLIP_STACK_WINDOW_RECT; + entry->x0 = x_offset; + entry->y0 = v[3] - y_offset - height; + entry->x1 = x_offset + width; + entry->y1 = v[3] - y_offset; + + /* Store it in the stack */ + stack->stack_top = g_list_prepend (stack->stack_top, entry); + + ctx->clip.stack_dirty = TRUE; +} + +/* Scale from OpenGL <-1,1> coordinates system to window coordinates + * <0,window-size> with (0,0) being top left. */ +#define VIEWPORT_SCALE_X(x, w, width, origin) \ + ((((((x) / (w)) + 1.0) / 2) * (width)) + (origin)) +#define VIEWPORT_SCALE_Y(y, w, height, origin) \ + ((height) - (((((y) / (w)) + 1.0) / 2) * (height)) + (origin)) + +static void +transform_point (CoglMatrix *matrix_mv, + CoglMatrix *matrix_p, + float *viewport, + float *x, + float *y) +{ + float z = 0; + float w = 1; + + /* Apply the model view matrix */ + cogl_matrix_transform_point (matrix_mv, x, y, &z, &w); + + /* Apply the projection matrix */ + cogl_matrix_transform_point (matrix_p, x, y, &z, &w); + /* Apply viewport transform */ + *x = VIEWPORT_SCALE_X (*x, w, viewport[2], viewport[0]); + *y = VIEWPORT_SCALE_Y (*y, w, viewport[3], viewport[1]); +} + +#undef VIEWPORT_SCALE_X +#undef VIEWPORT_SCALE_Y + +/* Try to push a rectangle given in object coordinates as a rectangle in window + * coordinates instead of object coordinates */ +gboolean +try_pushing_rect_as_window_rect (float x_offset, + float y_offset, + float width, + float height) +{ + CoglMatrix matrix; + CoglMatrix matrix_p; + float v[4]; + float _x0 = x_offset; + float _y0 = y_offset; + float _x1 = x_offset + width; + float _y1 = y_offset + height; + + cogl_get_modelview_matrix (&matrix); + + if (matrix.xy != 0 || matrix.xz != 0 || + matrix.yx != 0 || matrix.yz != 0 || + matrix.zx != 0 || matrix.zy != 0) + return FALSE; + + cogl_get_projection_matrix (&matrix_p); + cogl_get_viewport (v); + + transform_point (&matrix, &matrix_p, v, &_x0, &_y0); + transform_point (&matrix, &matrix_p, v, &_x1, &_y1); + + cogl_clip_push_window_rect (_x0, _y0, _x1 - _x0, _y1 - _y0); + return TRUE; +} + void cogl_clip_push (float x_offset, float y_offset, @@ -107,6 +219,11 @@ cogl_clip_push (float x_offset, _COGL_GET_CONTEXT (ctx, NO_RETVAL); + /* Try and catch window space rectangles so we can redirect to + * cogl_clip_push_window_rect which will use scissoring. */ + if (try_pushing_rect_as_window_rect (x_offset, y_offset, width, height)) + return; + stack = (CoglClipStack *) ctx->clip.stacks->data; entry = g_slice_new (CoglClipStackEntryRect); @@ -167,6 +284,7 @@ cogl_clip_pop (void) { gpointer entry; CoglClipStack *stack; + CoglClipStackEntryType type; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -175,10 +293,13 @@ cogl_clip_pop (void) g_return_if_fail (stack->stack_top != NULL); entry = stack->stack_top->data; + type = *(CoglClipStackEntryType *) entry; /* Remove the top entry from the stack */ - if (*(CoglClipStackEntryType *) entry == COGL_CLIP_STACK_RECT) + if (type == COGL_CLIP_STACK_RECT) g_slice_free (CoglClipStackEntryRect, entry); + else if (type == COGL_CLIP_STACK_WINDOW_RECT) + g_slice_free (CoglClipStackEntryWindowRect, entry); else g_free (entry); @@ -196,6 +317,10 @@ _cogl_clip_stack_rebuild (void) gboolean using_stencil_buffer = FALSE; GList *node; CoglClipStack *stack; + guint scissor_x0 = 0; + guint scissor_y0 = 0; + guint scissor_x1 = G_MAXUINT; + guint scissor_y1 = G_MAXUINT; _COGL_GET_CONTEXT (ctx, NO_RETVAL); @@ -206,6 +331,7 @@ _cogl_clip_stack_rebuild (void) _cogl_disable_clip_planes (); _cogl_disable_stencil_buffer (); + GE (glDisable (GL_SCISSOR_TEST)); /* If the stack is empty then there's nothing else to do */ if (stack->stack_top == NULL) @@ -218,8 +344,9 @@ _cogl_clip_stack_rebuild (void) for (; node; node = node->prev) { gpointer entry = node->data; + CoglClipStackEntryType type = *(CoglClipStackEntryType *) entry; - if (*(CoglClipStackEntryType *) entry == COGL_CLIP_STACK_PATH) + if (type == COGL_CLIP_STACK_PATH) { CoglClipStackEntryPath *path = (CoglClipStackEntryPath *) entry; @@ -239,7 +366,7 @@ _cogl_clip_stack_rebuild (void) /* We can't use clip planes any more */ has_clip_planes = FALSE; } - else + else if (type == COGL_CLIP_STACK_RECT) { CoglClipStackEntryRect *rect = (CoglClipStackEntryRect *) entry; @@ -270,6 +397,16 @@ _cogl_clip_stack_rebuild (void) cogl_pop_matrix (); } + else + { + /* Get the intersection of all window space rectangles in the clip + * stack */ + CoglClipStackEntryWindowRect *window_rect = entry; + scissor_x0 = MAX (scissor_x0, window_rect->x0); + scissor_y0 = MAX (scissor_y0, window_rect->y0); + scissor_x1 = MIN (scissor_x1, window_rect->x1); + scissor_y1 = MIN (scissor_y1, window_rect->y1); + } } /* Enabling clip planes is delayed to now so that they won't affect @@ -277,6 +414,18 @@ _cogl_clip_stack_rebuild (void) if (using_clip_planes) _cogl_enable_clip_planes (); + if (scissor_x0 >= scissor_x1 || scissor_y0 >= scissor_y1) + scissor_x0 = scissor_y0 = scissor_x1 = scissor_y1 = 0; + + if (!(scissor_x0 == 0 && scissor_y0 == 0 && + scissor_x1 == G_MAXUINT && scissor_y1 == G_MAXUINT)) + { + GE (glEnable (GL_SCISSOR_TEST)); + GE (glScissor (scissor_x0, scissor_y0, + scissor_x1 - scissor_x0, + scissor_y1 - scissor_y0)); + } + ctx->clip.stencil_used = using_stencil_buffer; } diff --git a/common/cogl.c b/common/cogl.c index 3e1b33bbf..e6341a8ac 100644 --- a/common/cogl.c +++ b/common/cogl.c @@ -89,6 +89,8 @@ cogl_clear (const CoglColor *color, gulong buffers) fprintf(stderr, "\n ============== Paint Start ================ \n"); #endif + cogl_clip_ensure (); + if (buffers & COGL_BUFFER_BIT_COLOR) { GE( glClearColor (cogl_color_get_red_float (color),