/**
 * @name	context_2d_setClip
 * @brief	sets the clipping rectangle on the given context
 * @param	ctx - (context_2d *) context to set the clipping rectangle on
 * @param	clip - (rect_2d) the clipping rectangle
 * @retval	NONE
 */
void context_2d_setClip(context_2d *ctx, rect_2d clip) {
	matrix_3x3 *modelView = GET_MODEL_VIEW_MATRIX(ctx);
	//  LOG("setClip: %f %f %f %f", clip.x, clip.y, clip.width, clip.height);
	// TODO: clipping with rectangles doesn't work so great with rotated or scaled coordinates...
	float x1, y1, x2, y2;
	matrix_3x3_multiply(modelView, clip.x, clip.y, &x1, &y1);
	matrix_3x3_multiply(modelView, clip.x + clip.width, clip.y + clip.height, &x2, &y2);
	clip.x = x1;
	clip.y = y1;
	clip.width = x2 - x1;
	clip.height = y2 - y1;
	int i = ctx->mvp - 1;
	rect_2d ctx_clip;
	ctx_clip.x = ctx->clipStack[i].x;
	ctx_clip.y = ctx->clipStack[i].y;
	ctx_clip.width = ctx->clipStack[i].width;
	ctx_clip.height = ctx->clipStack[i].height;

	//flip the y to be back at top left for easier readability of intersection testing
	if (ctx->on_screen && clip.height > 0) {
		ctx_clip.y = -ctx_clip.y +  ctx->canvas->framebuffer_height + ctx->canvas->framebuffer_offset_bottom - ctx_clip.height;
	}

	//width = -1 if view is not clipping so ignore it
	if (ctx_clip.width > -1) {
		//set clip rect to empty if the clip coming in is outside of the clip stack's top clipping rect
		if (clip.x >= ctx_clip.x + ctx_clip.width || clip.x + clip.width <= ctx_clip.x ||
		        clip.y >= ctx_clip.y + ctx_clip.height || clip.y + clip.height <= ctx_clip.y) {
			clip.x = clip.y = clip.height = clip.width = 0;
		} else {
			clip.x = ctx_clip.x > x1 ?   ctx_clip.x :  x1;
			clip.y = ctx_clip.y > y1 ?   ctx_clip.y :  y1;
			clip.width = (ctx_clip.x + ctx_clip.width < x2 ? ctx_clip.x + ctx_clip.width :  x2) - clip.x;
			clip.height = (ctx_clip.y + ctx_clip.height < y2 ? ctx_clip.y + ctx_clip.height :   y2) - clip.y;
		}
	}

	// scissor is with respect to lower-left corner
	// activeFrameBufferHeight is the height of the off-screen buffer
	// activeFrameBufferOffsetBottom -- the viewport actually goes past the bottom of the texture
	//   to the nearest power of two, so when we convert to y-coordinates from the lower-left viewport
	//   corner, we need to add the offsetBottom to get to the bottom of the viewable texture
	if (ctx->on_screen && clip.height > 0) {
		clip.y = ctx->canvas->framebuffer_height - (clip.height + clip.y) + ctx->canvas->framebuffer_offset_bottom;
	}

	rect_2d bounds = {clip.x, clip.y, clip.width, clip.height};

	if (rect_2d_equals(GET_CLIPPING_BOUNDS(ctx), &bounds)) {
		return;
	}

	*GET_CLIPPING_BOUNDS(ctx) = bounds;
	enable_scissor(ctx);
}
示例#2
0
bool matrix_3x3_quad_to_quad(const float dx0, const float dy0,
                             const float dx1, const float dy1,
                             const float dx2, const float dy2,
                             const float dx3, const float dy3,
                             const float sx0, const float sy0,
                             const float sx1, const float sy1,
                             const float sx2, const float sy2,
                             const float sx3, const float sy3,
                             math_matrix_3x3 *mat)
{
   math_matrix_3x3 quad_to_square, square_to_quad;

   if (!matrix_3x3_square_to_quad(dx0, dy0, dx1, dy1,
                                  dx2, dy2, dx3, dy3,
                                  &square_to_quad))
      return false;

   if (!matrix_3x3_quad_to_square(sx0, sy0, sx1, sy1,
                                  sx2, sy2, sx3, sy3,
                                  &quad_to_square))
      return false;

   matrix_3x3_multiply(mat, &quad_to_square, &square_to_quad);

   return true;
}
/**
 * @name	context_2d_clearRect
 * @brief	clears the given rect on the given context
 * @param	ctx - (context_2d *) context to clear a rect from
 * @param	rect - (const rect_2d *) rect to clear from the context
 * @retval	NONE
 */
void context_2d_clearRect(context_2d *ctx, const rect_2d *rect) {
	draw_textures_flush();
	context_2d_bind(ctx);
	// Draw a rectangle using triangle strip:
	//    (0,1)-(2,3)-(4,5) and (2,3)-(4,5)-(6,7)
	//
	// With coordinates:
	//    4,5  -  6,7
	//     |   \   |
	//    0,1  -  2,3
	GLfloat v[8];
	matrix_3x3_multiply(GET_MODEL_VIEW_MATRIX(ctx), rect, (float *)&v[4], (float *)&v[5], (float *)&v[6], (float *)&v[7], (float *)&v[2], (float *)&v[3], (float *)&v[0], (float *)&v[1]);
	glBlendFunc(GL_ONE, GL_ZERO);
	glUniform4f(global_shaders[PRIMARY_SHADER].draw_color, 0, 0, 0, 0); // set color to 0
	glVertexAttribPointer(global_shaders[PRIMARY_SHADER].vertex_coords, 2, GL_FLOAT, GL_FALSE, 0, v);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
/**
 * @name	context_2d_setClip
 * @brief	sets the clipping rectangle on the given context
 * @param	ctx - (context_2d *) context to set the clipping rectangle on
 * @param	clip - (rect_2d) the clipping rectangle
 * @retval	NONE
 */
void context_2d_setClip(context_2d *ctx, rect_2d clip) {
	matrix_3x3 *modelView = GET_MODEL_VIEW_MATRIX(ctx);

#ifdef MATRIX_3x3_ALLOW_SKEW
	// TODO: Can this be done more efficiently?
	float clip_x, clip_y, clip_w, clip_h;
	float x1, y1, x2, y2, x3, y3, x4, y4;

	matrix_3x3_multiply(modelView, clip.x, clip.y, &x1, &y1);
	matrix_3x3_multiply(modelView, clip.x + clip.width, clip.y + clip.height, &x2, &y2);
	matrix_3x3_multiply(modelView, clip.x, clip.y + clip.height, &x3, &y3);
	matrix_3x3_multiply(modelView, clip.x + clip.width, clip.y, &x4, &y4);

	clip_x = x1;
	if (x2 < clip_x) {
		clip_x = x2;
	}
	if (x3 < clip_x) {
		clip_x = x3;
	}
	if (x4 < clip_x) {
		clip_x = x4;
	}
	
	clip_y = y1;
	if (y2 < clip_y) {
		clip_y = y2;
	}
	if (y3 < clip_y) {
		clip_y = y3;
	}
	if (y4 < clip_y) {
		clip_y = y4;
	}
	
	clip_w = x1;
	if (x2 > clip_w) {
		clip_w = x2;
	}
	if (x3 > clip_w) {
		clip_w = x3;
	}
	if (x4 > clip_w) {
		clip_w = x4;
	}
	
	clip_h = y1;
	if (y2 > clip_h) {
		clip_h = y2;
	}
	if (y3 > clip_h) {
		clip_h = y3;
	}
	if (y4 > clip_h) {
		clip_h = y4;
	}
	
	clip.x = clip_x;
	clip.y = clip_y;
	clip.width = clip_w - clip_x;
	clip.height = clip_h - clip_y;
#else
	float clip0x = clip.x, clip0y = clip.y;
	float clip1x = clip0x + clip.width, clip1y = clip0y + clip.height;

	// float x1, y1, x2, y2, x3, y3, x4, y4;
	// x1 = clip0x * modelView->m00 + clip0y * modelView->m01 + modelView->m02;
	// y1 = clip0x * modelView->m10 + clip0y * modelView->m11 + modelView->m12;
	// x2 = clip1x * modelView->m00 + clip0y * modelView->m01 + modelView->m02;
	// y2 = clip1x * modelView->m10 + clip0y * modelView->m11 + modelView->m12;
	// x3 = clip1x * modelView->m00 + clip1y * modelView->m01 + modelView->m02;
	// y3 = clip1x * modelView->m10 + clip1y * modelView->m11 + modelView->m12;
	// x4 = clip0x * modelView->m00 + clip1y * modelView->m01 + modelView->m02;
	// y4 = clip0x * modelView->m10 + clip1y * modelView->m11 + modelView->m12;

	float m00 = modelView->m00, m01 = modelView->m01, m10 = modelView->m10, m11 = modelView->m11;
	float a = clip0x * m00;
	float b = clip1x * m10;
	float c = clip0y * m11;
	float d = clip0y * m01;
	float e = clip1x * m00;
	float f = clip1y * m01;
	float g = clip0x * m10;
	float h = clip1y * m11;

	// If x1 < x2,
	if ((clip0x < clip1x) ^ (m00 < 0)) {
		// If x2 < x3,
		if ((clip0y < clip1y) ^ (m01 < 0)) {
			// (x1, y2) -> (x3, y4)
			clip.x = a + d;
			clip.y = b + c;
			clip.width = e + f;
			clip.height = g + h;
		} else {
			// (x4, y1) -> (x2, y3)
			clip.x = a + f;
			clip.y = g + c;
			clip.width = e + d;
			clip.height = b + h;
		}
	} else {
		// If x2 < x3,
		if ((clip0y < clip1y) ^ (m01 < 0)) {
			// (x2, y3) -> (x4, y1)
			clip.x = e + d;
			clip.y = b + h;
			clip.width = a + f;
			clip.height = g + c;
		} else {
			// (x3, y4) -> (x1, y2)
			clip.x = e + f;
			clip.y = g + h;
			clip.width = a + d;
			clip.height = b + c;
		}
	}

	clip.width -= clip.x;
	clip.height -= clip.y;
	clip.x += modelView->m02;
	clip.y += modelView->m12;
#endif

	// Clip with screen bounds
	if (clip.x < 0) {
		clip.width += clip.x;
		if (clip.width < 0) {
			clip.width = 0;
		}
		clip.x = 0;
	}
	if (clip.y < 0) {
		clip.height += clip.y;
		if (clip.height < 0) {
			clip.height = 0;
		}
		clip.y = 0;
	}

	// If entirely clipped,
	if (clip.width <= 0 || clip.height <= 0) {
		clip.x = clip.y = clip.width = clip.height = 0;
	} else {
		// Lookup parent bounds
		int i = ctx->mvp - 1;
		rect_2d ctx_clip;
		ctx_clip.x = ctx->clipStack[i].x;
		ctx_clip.y = ctx->clipStack[i].y;
		ctx_clip.width = ctx->clipStack[i].width;
		ctx_clip.height = ctx->clipStack[i].height;

		// If context is on screen,
		if (ctx->on_screen) {
			// Flip to frame buffer sense
			ctx_clip.y = -ctx_clip.y +  ctx->canvas->framebuffer_height + ctx->canvas->framebuffer_offset_bottom - ctx_clip.height;
		}

		// If parent is clipping,
		if (ctx_clip.width > -1) {
			// Calculate (x1, y1) for new and old clip regions
			float clip1x = clip.x + clip.width, clip1y = clip.y + clip.height;
			float ctx1x = ctx_clip.x + ctx_clip.width, ctx1y = ctx_clip.y + ctx_clip.height;

			// If new clip is entirely outside parent,
			if (clip.x >= ctx1x || clip1x <= ctx_clip.x ||
				clip.y >= ctx1y || clip1y <= ctx_clip.y) {
				// Empty clip region
				clip.x = clip.y = clip.width = clip.height = 0;
			} else {
				// Trim new clip with parent
				clip.x = ctx_clip.x > clip.x ? ctx_clip.x : clip.x;
				clip.y = ctx_clip.y > clip.y ? ctx_clip.y : clip.y;
				clip.width = (ctx1x < clip1x ? ctx1x : clip1x) - clip.x;
				clip.height = (ctx1y < clip1y ? ctx1y : clip1y) - clip.y;
			}
		}

		// scissor is with respect to lower-left corner
		// activeFrameBufferHeight is the height of the off-screen buffer
		// activeFrameBufferOffsetBottom -- the viewport actually goes past the bottom of the texture
		//   to the nearest power of two, so when we convert to y-coordinates from the lower-left viewport
		//   corner, we need to add the offsetBottom to get to the bottom of the viewable texture
		if (ctx->on_screen && clip.height > 0) {
			// Flip from frame buffer sense
			clip.y = ctx->canvas->framebuffer_height - (clip.height + clip.y) + ctx->canvas->framebuffer_offset_bottom;
		}
	}

	rect_2d bounds = {
		clip.x, clip.y,
		clip.width, clip.height
	};

	if (rect_2d_equals(GET_CLIPPING_BOUNDS(ctx), &bounds)) {
		return;
	}

	*GET_CLIPPING_BOUNDS(ctx) = bounds;
	enable_scissor(ctx);
}