static void set_clip_planes (CoglFramebuffer *framebuffer, CoglMatrixEntry *modelview_entry, float x_1, float y_1, float x_2, float y_2) { CoglMatrix modelview_matrix; CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglMatrix projection_matrix; CoglMatrix modelview_projection; float signed_area; float vertex_tl[4] = { x_1, y_1, 0, 1.0 }; float vertex_tr[4] = { x_2, y_1, 0, 1.0 }; float vertex_bl[4] = { x_1, y_2, 0, 1.0 }; float vertex_br[4] = { x_2, y_2, 0, 1.0 }; cogl_matrix_stack_get (projection_stack, &projection_matrix); cogl_matrix_entry_get (modelview_entry, &modelview_matrix); cogl_matrix_multiply (&modelview_projection, &projection_matrix, &modelview_matrix); project_vertex (&modelview_projection, vertex_tl); project_vertex (&modelview_projection, vertex_tr); project_vertex (&modelview_projection, vertex_bl); project_vertex (&modelview_projection, vertex_br); /* Calculate the signed area of the polygon formed by the four vertices so that we can know its orientation */ signed_area = (vertex_tl[0] * (vertex_tr[1] - vertex_bl[1]) + vertex_tr[0] * (vertex_br[1] - vertex_tl[1]) + vertex_br[0] * (vertex_bl[1] - vertex_tr[1]) + vertex_bl[0] * (vertex_tl[1] - vertex_br[1])); /* Set the clip planes to form lines between all of the vertices using the same orientation as we calculated */ if (signed_area > 0.0f) { /* counter-clockwise */ set_clip_plane (framebuffer, GL_CLIP_PLANE0, vertex_tl, vertex_bl); set_clip_plane (framebuffer, GL_CLIP_PLANE1, vertex_bl, vertex_br); set_clip_plane (framebuffer, GL_CLIP_PLANE2, vertex_br, vertex_tr); set_clip_plane (framebuffer, GL_CLIP_PLANE3, vertex_tr, vertex_tl); } else { /* clockwise */ set_clip_plane (framebuffer, GL_CLIP_PLANE0, vertex_tl, vertex_tr); set_clip_plane (framebuffer, GL_CLIP_PLANE1, vertex_tr, vertex_br); set_clip_plane (framebuffer, GL_CLIP_PLANE2, vertex_br, vertex_bl); set_clip_plane (framebuffer, GL_CLIP_PLANE3, vertex_bl, vertex_tl); } }
static void set_clip_plane (GLint plane_num, const float *vertex_a, const float *vertex_b) { #if defined (HAVE_COGL_GLES2) || defined (HAVE_COGL_GLES) GLfloat plane[4]; #else GLdouble plane[4]; #endif GLfloat angle; CoglHandle framebuffer = _cogl_get_framebuffer (); CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglMatrix inverse_projection; _COGL_GET_CONTEXT (ctx, NO_RETVAL); _cogl_matrix_stack_get_inverse (projection_stack, &inverse_projection); /* Calculate the angle between the axes and the line crossing the two points */ angle = atan2f (vertex_b[1] - vertex_a[1], vertex_b[0] - vertex_a[0]) * (180.0/G_PI); _cogl_matrix_stack_push (modelview_stack); /* Load the inverse of the projection matrix so we can specify the plane * in screen coordinates */ _cogl_matrix_stack_set (modelview_stack, &inverse_projection); /* Rotate about point a */ _cogl_matrix_stack_translate (modelview_stack, vertex_a[0], vertex_a[1], vertex_a[2]); /* Rotate the plane by the calculated angle so that it will connect the two points */ _cogl_matrix_stack_rotate (modelview_stack, angle, 0.0f, 0.0f, 1.0f); _cogl_matrix_stack_translate (modelview_stack, -vertex_a[0], -vertex_a[1], -vertex_a[2]); _cogl_matrix_stack_flush_to_gl (modelview_stack, COGL_MATRIX_MODELVIEW); plane[0] = 0; plane[1] = -1.0; plane[2] = 0; plane[3] = vertex_a[1]; #if defined (HAVE_COGL_GLES2) || defined (HAVE_COGL_GLES) GE( glClipPlanef (plane_num, plane) ); #else GE( glClipPlane (plane_num, plane) ); #endif _cogl_matrix_stack_pop (modelview_stack); }
void add_stencil_clip_rectangle (float x_1, float y_1, float x_2, float y_2, gboolean first) { CoglHandle current_source; CoglHandle framebuffer = _cogl_get_framebuffer (); _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* We don't log changes to the stencil buffer so need to flush any * batched geometry before we start... */ _cogl_journal_flush (); _cogl_framebuffer_flush_state (framebuffer, 0); /* temporarily swap in our special stenciling material */ current_source = cogl_handle_ref (ctx->source_material); cogl_set_source (ctx->stencil_material); if (first) { GE( glEnable (GL_STENCIL_TEST) ); /* Initially disallow everything */ GE( glClearStencil (0) ); GE( glClear (GL_STENCIL_BUFFER_BIT) ); /* Punch out a hole to allow the rectangle */ GE( glStencilFunc (GL_NEVER, 0x1, 0x1) ); GE( glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE) ); cogl_rectangle (x_1, y_1, x_2, y_2); } else { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); /* Add one to every pixel of the stencil buffer in the rectangle */ GE( glStencilFunc (GL_NEVER, 0x1, 0x3) ); GE( glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); cogl_rectangle (x_1, y_1, x_2, y_2); /* make sure our rectangle hits the stencil buffer before we * change the stencil operation */ _cogl_journal_flush (); /* Subtract one from all pixels in the stencil buffer so that only pixels where both the original stencil buffer and the rectangle are set will be valid */ GE( glStencilOp (GL_DECR, GL_DECR, GL_DECR) ); _cogl_matrix_stack_push (projection_stack); _cogl_matrix_stack_load_identity (projection_stack); _cogl_matrix_stack_push (modelview_stack); _cogl_matrix_stack_load_identity (modelview_stack); cogl_rectangle (-1.0, -1.0, 1.0, 1.0); _cogl_matrix_stack_pop (modelview_stack); _cogl_matrix_stack_pop (projection_stack); } /* make sure our rectangles hit the stencil buffer before we restore * the stencil function / operation */ _cogl_journal_flush (); /* Restore the stencil mode */ GE( glStencilFunc (GL_EQUAL, 0x1, 0x1) ); GE( glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); /* restore the original source material */ cogl_set_source (current_source); cogl_handle_unref (current_source); }
static void set_clip_plane (CoglFramebuffer *framebuffer, int plane_num, const float *vertex_a, const float *vertex_b) { CoglContext *ctx = framebuffer->context; float planef[4]; double planed[4]; float angle; CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglMatrix inverse_projection; cogl_matrix_stack_get_inverse (projection_stack, &inverse_projection); /* Calculate the angle between the axes and the line crossing the two points */ angle = atan2f (vertex_b[1] - vertex_a[1], vertex_b[0] - vertex_a[0]) * (180.0/G_PI); cogl_matrix_stack_push (modelview_stack); /* Load the inverse of the projection matrix so we can specify the plane * in screen coordinates */ cogl_matrix_stack_set (modelview_stack, &inverse_projection); /* Rotate about point a */ cogl_matrix_stack_translate (modelview_stack, vertex_a[0], vertex_a[1], vertex_a[2]); /* Rotate the plane by the calculated angle so that it will connect the two points */ cogl_matrix_stack_rotate (modelview_stack, angle, 0.0f, 0.0f, 1.0f); cogl_matrix_stack_translate (modelview_stack, -vertex_a[0], -vertex_a[1], -vertex_a[2]); /* Clip planes can only be used when a fixed function backend is in use so we know we can directly push this matrix to the builtin state */ _cogl_matrix_entry_flush_to_gl_builtins (ctx, modelview_stack->last_entry, COGL_MATRIX_MODELVIEW, framebuffer, FALSE /* don't disable flip */); planef[0] = 0; planef[1] = -1.0; planef[2] = 0; planef[3] = vertex_a[1]; switch (ctx->driver) { default: g_assert_not_reached (); break; case COGL_DRIVER_GLES1: GE( ctx, glClipPlanef (plane_num, planef) ); break; case COGL_DRIVER_GL: case COGL_DRIVER_GL3: planed[0] = planef[0]; planed[1] = planef[1]; planed[2] = planef[2]; planed[3] = planef[3]; GE( ctx, glClipPlane (plane_num, planed) ); break; } cogl_matrix_stack_pop (modelview_stack); }
static void add_stencil_clip_silhouette (CoglFramebuffer *framebuffer, SilhouettePaintCallback silhouette_callback, CoglMatrixEntry *modelview_entry, float bounds_x1, float bounds_y1, float bounds_x2, float bounds_y2, CoglBool merge, CoglBool need_clear, void *user_data) { CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); /* NB: This can be called while flushing the journal so we need * to be very conservative with what state we change. */ _cogl_context_set_current_projection_entry (ctx, projection_stack->last_entry); _cogl_context_set_current_modelview_entry (ctx, modelview_entry); _cogl_pipeline_flush_gl_state (ctx, ctx->stencil_pipeline, framebuffer, FALSE, FALSE); GE( ctx, glEnable (GL_STENCIL_TEST) ); GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) ); GE( ctx, glDepthMask (FALSE) ); if (merge) { GE (ctx, glStencilMask (2)); GE (ctx, glStencilFunc (GL_LEQUAL, 0x2, 0x6)); } else { /* If we're not using the stencil buffer for clipping then we don't need to clear the whole stencil buffer, just the area that will be drawn */ if (need_clear) /* If this is being called from the clip stack code then it will have set up a scissor for the minimum bounding box of all of the clips. That box will likely mean that this _cogl_clear won't need to clear the entire buffer. _cogl_framebuffer_clear_without_flush4f is used instead of cogl_clear because it won't try to flush the journal */ _cogl_framebuffer_clear_without_flush4f (framebuffer, COGL_BUFFER_BIT_STENCIL, 0, 0, 0, 0); else { /* Just clear the bounding box */ GE( ctx, glStencilMask (~(GLuint) 0) ); GE( ctx, glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) ); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, bounds_x1, bounds_y1, bounds_x2, bounds_y2); } GE (ctx, glStencilMask (1)); GE (ctx, glStencilFunc (GL_LEQUAL, 0x1, 0x3)); } GE (ctx, glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT)); silhouette_callback (framebuffer, ctx->stencil_pipeline, user_data); if (merge) { /* Now we have the new stencil buffer in bit 1 and the old stencil buffer in bit 0 so we need to intersect them */ GE (ctx, glStencilMask (3)); GE (ctx, glStencilFunc (GL_NEVER, 0x2, 0x3)); GE (ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR)); /* Decrement all of the bits twice so that only pixels where the value is 3 will remain */ _cogl_context_set_current_projection_entry (ctx, &ctx->identity_entry); _cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); } GE (ctx, glStencilMask (~(GLuint) 0)); GE (ctx, glDepthMask (TRUE)); GE (ctx, glColorMask (TRUE, TRUE, TRUE, TRUE)); GE (ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1)); GE (ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP)); }
static void add_stencil_clip_rectangle (CoglFramebuffer *framebuffer, CoglMatrixEntry *modelview_entry, float x_1, float y_1, float x_2, float y_2, CoglBool first) { CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); /* NB: This can be called while flushing the journal so we need * to be very conservative with what state we change. */ _cogl_context_set_current_projection_entry (ctx, projection_stack->last_entry); _cogl_context_set_current_modelview_entry (ctx, modelview_entry); if (first) { GE( ctx, glEnable (GL_STENCIL_TEST) ); /* Initially disallow everything */ GE( ctx, glClearStencil (0) ); GE( ctx, glClear (GL_STENCIL_BUFFER_BIT) ); /* Punch out a hole to allow the rectangle */ GE( ctx, glStencilFunc (GL_NEVER, 0x1, 0x1) ); GE( ctx, glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE) ); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, x_1, y_1, x_2, y_2); } else { /* Add one to every pixel of the stencil buffer in the rectangle */ GE( ctx, glStencilFunc (GL_NEVER, 0x1, 0x3) ); GE( ctx, glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, x_1, y_1, x_2, y_2); /* Subtract one from all pixels in the stencil buffer so that only pixels where both the original stencil buffer and the rectangle are set will be valid */ GE( ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR) ); _cogl_context_set_current_projection_entry (ctx, &ctx->identity_entry); _cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); } /* Restore the stencil mode */ GE( ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1) ); GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); }
/* Reads back the contents of a texture by rendering it to the framebuffer * and reading back the resulting pixels. * * NB: Normally this approach isn't normally used since we can just use * glGetTexImage, but may be used as a fallback in some circumstances. */ gboolean _cogl_texture_draw_and_read (CoglHandle handle, CoglBitmap *target_bmp, GLuint target_gl_format, GLuint target_gl_type) { int bpp; CoglFramebuffer *framebuffer; int viewport[4]; CoglBitmap *alpha_bmp; CoglMatrixStack *projection_stack; CoglMatrixStack *modelview_stack; int target_width = _cogl_bitmap_get_width (target_bmp); int target_height = _cogl_bitmap_get_height (target_bmp); int target_rowstride = _cogl_bitmap_get_rowstride (target_bmp); _COGL_GET_CONTEXT (ctx, FALSE); bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); framebuffer = _cogl_get_draw_buffer (); /* Viewport needs to have some size and be inside the window for this */ _cogl_framebuffer_get_viewport4fv (framebuffer, viewport); if (viewport[0] < 0 || viewport[1] < 0 || viewport[2] <= 0 || viewport[3] <= 0) return FALSE; /* Setup orthographic projection into current viewport (0,0 in top-left * corner to draw the texture upside-down so we match the way cogl_read_pixels * works) */ projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); _cogl_matrix_stack_push (projection_stack); _cogl_matrix_stack_load_identity (projection_stack); _cogl_matrix_stack_ortho (projection_stack, 0, (float)(viewport[2]), (float)(viewport[3]), 0, (float)(0), (float)(100)); modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); _cogl_matrix_stack_push (modelview_stack); _cogl_matrix_stack_load_identity (modelview_stack); /* Direct copy operation */ if (ctx->texture_download_pipeline == COGL_INVALID_HANDLE) { ctx->texture_download_pipeline = cogl_pipeline_new (); cogl_pipeline_set_blend (ctx->texture_download_pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL); } cogl_push_source (ctx->texture_download_pipeline); cogl_pipeline_set_layer_texture (ctx->texture_download_pipeline, 0, handle); cogl_pipeline_set_layer_combine (ctx->texture_download_pipeline, 0, /* layer */ "RGBA = REPLACE (TEXTURE)", NULL); cogl_pipeline_set_layer_filters (ctx->texture_download_pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); do_texture_draw_and_read (handle, target_bmp, viewport); /* Check whether texture has alpha and framebuffer not */ /* FIXME: For some reason even if ALPHA_BITS is 8, the framebuffer still doesn't seem to have an alpha buffer. This might be just a PowerVR issue. GLint r_bits, g_bits, b_bits, a_bits; GE( glGetIntegerv (GL_ALPHA_BITS, &a_bits) ); GE( glGetIntegerv (GL_RED_BITS, &r_bits) ); GE( glGetIntegerv (GL_GREEN_BITS, &g_bits) ); GE( glGetIntegerv (GL_BLUE_BITS, &b_bits) ); printf ("R bits: %d\n", r_bits); printf ("G bits: %d\n", g_bits); printf ("B bits: %d\n", b_bits); printf ("A bits: %d\n", a_bits); */ if ((cogl_texture_get_format (handle) & COGL_A_BIT)/* && a_bits == 0*/) { guint8 *srcdata; guint8 *dstdata; guint8 *srcpixel; guint8 *dstpixel; int x,y; int alpha_rowstride = bpp * target_width; if ((dstdata = _cogl_bitmap_map (target_bmp, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD)) == NULL) return FALSE; srcdata = g_malloc (alpha_rowstride * target_height); /* Create temp bitmap for alpha values */ alpha_bmp = _cogl_bitmap_new_from_data (srcdata, COGL_PIXEL_FORMAT_RGBA_8888, target_width, target_height, alpha_rowstride, (CoglBitmapDestroyNotify) g_free, NULL); /* Draw alpha values into RGB channels */ cogl_pipeline_set_layer_combine (ctx->texture_download_pipeline, 0, /* layer */ "RGBA = REPLACE (TEXTURE[A])", NULL); do_texture_draw_and_read (handle, alpha_bmp, viewport); /* Copy temp R to target A */ for (y=0; y<target_height; ++y) { for (x=0; x<target_width; ++x) { srcpixel = srcdata + x*bpp; dstpixel = dstdata + x*bpp; dstpixel[3] = srcpixel[0]; } srcdata += alpha_rowstride; dstdata += target_rowstride; } _cogl_bitmap_unmap (target_bmp); cogl_object_unref (alpha_bmp); } /* Restore old state */ _cogl_matrix_stack_pop (modelview_stack); _cogl_matrix_stack_pop (projection_stack); /* restore the original pipeline */ cogl_pop_source (); return TRUE; }