diff --git a/ChangeLog b/ChangeLog index 9bbb5680c..762f84531 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2008-10-27 Neil Roberts + + Bug 1196 - Texture border drawing problems + + * clutter/cogl/gl/cogl-texture.c (_cogl_texture_upload_subregion_to_gl) + (_cogl_texture_upload_to_gl): + + * clutter/cogl/gles/cogl-texture.c (_cogl_texture_upload_to_gl) + (_cogl_texture_upload_subregion_to_gl): + + When uploading data to a sliced texture, fill the waste pixels + with copies of the edge of the real texture data. Otherwise the + value of the waste pixels are undefined so it will show artifacts + when the texture is scaled with GL_LINEAR and the pixels are + blended in. + 2008-10-22 Thomas Wood * tests/test-actors.c: Don't adjust the radius based on the number of diff --git a/clutter/cogl/gl/cogl-texture.c b/clutter/cogl/gl/cogl-texture.c index b4d8e7adc..3bdf3bd9a 100644 --- a/clutter/cogl/gl/cogl-texture.c +++ b/clutter/cogl/gl/cogl-texture.c @@ -212,6 +212,35 @@ _cogl_subregion_gl_store_rules (gint bmp_rowstride, GE( glPixelStorei (SKIP_PIXELS, src_x) ); } +static guchar * +_cogl_texture_allocate_waste_buffer (CoglTexture *tex) +{ + CoglTexSliceSpan *last_x_span; + CoglTexSliceSpan *last_y_span; + guchar *waste_buf = NULL; + + /* If the texture has any waste then allocate a buffer big enough to + fill the gaps */ + last_x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, + tex->slice_x_spans->len - 1); + last_y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, + tex->slice_y_spans->len - 1); + if (last_x_span->waste > 0 || last_y_span->waste > 0) + { + gint bpp = _cogl_get_format_bpp (tex->bitmap.format); + CoglTexSliceSpan *first_x_span + = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + CoglTexSliceSpan *first_y_span + = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + guint right_size = first_y_span->size * last_x_span->waste; + guint bottom_size = first_x_span->size * last_y_span->waste; + + waste_buf = g_malloc (MAX (right_size, bottom_size) * bpp); + } + + return waste_buf; +} + static gboolean _cogl_texture_upload_to_gl (CoglTexture *tex) { @@ -220,9 +249,12 @@ _cogl_texture_upload_to_gl (CoglTexture *tex) GLuint gl_handle; gint bpp; gint x,y; + guchar *waste_buf; bpp = _cogl_get_format_bpp (tex->bitmap.format); - + + waste_buf = _cogl_texture_allocate_waste_buffer (tex); + /* Iterate vertical slices */ for (y = 0; y < tex->slice_y_spans->len; ++y) { @@ -253,9 +285,78 @@ _cogl_texture_upload_to_gl (CoglTexture *tex) y_span->size - y_span->waste, tex->gl_format, tex->gl_type, tex->bitmap.data) ); + + /* Fill the waste with a copies of the rightmost pixels */ + if (x_span->waste > 0) + { + const guchar *src = tex->bitmap.data + + y_span->start * tex->bitmap.rowstride + + (x_span->start + x_span->size - x_span->waste - 1) * bpp; + guchar *dst = waste_buf; + guint wx, wy; + + for (wy = 0; wy < y_span->size - y_span->waste; wy++) + { + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, src, bpp); + dst += bpp; + } + src += tex->bitmap.rowstride; + } + + _cogl_subregion_gl_store_rules (x_span->waste * bpp, + x_span->waste, + bpp, + 0, 0, FALSE); + + GE( glTexSubImage2D (tex->gl_target, 0, + x_span->size - x_span->waste, 0, + x_span->waste, + y_span->size - y_span->waste, + tex->gl_format, tex->gl_type, + waste_buf) ); + } + + if (y_span->waste > 0) + { + const guchar *src = tex->bitmap.data + + ((y_span->start + y_span->size - y_span->waste - 1) + * tex->bitmap.rowstride) + + x_span->start * bpp; + guchar *dst = waste_buf; + guint wy, wx; + + for (wy = 0; wy < y_span->waste; wy++) + { + memcpy (dst, src, (x_span->size - x_span->waste) * bpp); + dst += (x_span->size - x_span->waste) * bpp; + + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, dst - bpp, bpp); + dst += bpp; + } + } + + _cogl_subregion_gl_store_rules (x_span->size * bpp, + x_span->size, + bpp, + 0, 0, FALSE); + + GE( glTexSubImage2D (tex->gl_target, 0, + 0, y_span->size - y_span->waste, + x_span->size, + y_span->waste, + tex->gl_format, tex->gl_type, + waste_buf) ); + } } } - + + if (waste_buf) + g_free (waste_buf); + return TRUE; } @@ -374,16 +475,21 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, GLuint source_gl_format, GLuint source_gl_type) { - gint bpp; - CoglSpanIter x_iter; - CoglSpanIter y_iter; - GLuint gl_handle; - gint source_x = 0, source_y = 0; - gint inter_w = 0, inter_h = 0; - gint local_x = 0, local_y = 0; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + gint bpp; + CoglSpanIter x_iter; + CoglSpanIter y_iter; + GLuint gl_handle; + gint source_x = 0, source_y = 0; + gint inter_w = 0, inter_h = 0; + gint local_x = 0, local_y = 0; + guchar *waste_buf; bpp = _cogl_get_format_bpp (source_bmp->format); + waste_buf = _cogl_texture_allocate_waste_buffer (tex); + /* Iterate vertical spans */ for (source_y = src_y, _cogl_span_iter_begin (&y_iter, tex->slice_y_spans, @@ -402,6 +508,9 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, continue; } + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, + y_iter.index); + /* Iterate horizontal spans */ for (source_x = src_x, _cogl_span_iter_begin (&x_iter, tex->slice_x_spans, @@ -420,6 +529,9 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, continue; } + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, + x_iter.index); + /* Pick intersection width and height */ inter_w = CLUTTER_FIXED_TO_INT (x_iter.intersect_end - x_iter.intersect_start); @@ -455,9 +567,96 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, source_gl_format, source_gl_type, source_bmp->data) ); + + /* If the x_span is sliced and the upload touches the + rightmost pixels then fill the waste with copies of the + pixels */ + if (x_span->waste > 0 + && local_x < x_span->size - x_span->waste + && local_x + inter_w >= x_span->size - x_span->waste) + { + const guchar *src = source_bmp->data + + (src_y + CLUTTER_FIXED_TO_INT (y_iter.intersect_start) + - dst_y) * source_bmp->rowstride + + (src_x + x_span->start + x_span->size - x_span->waste + - dst_x - 1) * bpp; + guchar *dst = waste_buf; + guint wx, wy; + + for (wy = 0; wy < inter_h; wy++) + { + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, src, bpp); + dst += bpp; + } + src += source_bmp->rowstride; + } + + _cogl_subregion_gl_store_rules (x_span->waste * bpp, + x_span->waste, + bpp, + 0, 0, FALSE); + + GE( glTexSubImage2D (tex->gl_target, 0, + x_span->size - x_span->waste, local_y, + x_span->waste, + inter_h, + source_gl_format, + source_gl_type, + waste_buf) ); + } + + /* same for the bottom-most pixels */ + if (y_span->waste > 0 + && local_y < y_span->size - y_span->waste + && local_y + inter_h >= y_span->size - y_span->waste) + { + const guchar *src = source_bmp->data + + (src_x + CLUTTER_FIXED_TO_INT (x_iter.intersect_start) + - dst_x) * bpp + + (src_y + y_span->start + y_span->size - y_span->waste + - dst_y - 1) * source_bmp->rowstride; + guchar *dst = waste_buf; + guint wy, wx; + guint copy_width; + + if (local_x + inter_w >= x_span->size - x_span->waste) + copy_width = x_span->size - local_x; + else + copy_width = inter_w; + + for (wy = 0; wy < y_span->waste; wy++) + { + memcpy (dst, src, inter_w * bpp); + dst += inter_w * bpp; + + for (wx = inter_w; wx < copy_width; wx++) + { + memcpy (dst, dst - bpp, bpp); + dst += bpp; + } + } + + _cogl_subregion_gl_store_rules (copy_width * bpp, + copy_width, + bpp, + 0, 0, FALSE); + + GE( glTexSubImage2D (tex->gl_target, 0, + local_x, y_span->size - y_span->waste, + copy_width, + y_span->waste, + source_gl_format, + source_gl_type, + waste_buf) ); + } } } - + + if (waste_buf) + g_free (waste_buf); + return TRUE; } diff --git a/clutter/cogl/gles/cogl-texture.c b/clutter/cogl/gles/cogl-texture.c index df312e486..97cd258c0 100644 --- a/clutter/cogl/gles/cogl-texture.c +++ b/clutter/cogl/gles/cogl-texture.c @@ -174,6 +174,35 @@ _cogl_span_iter_end (CoglSpanIter *iter) return iter->pos >= iter->cover_end; } +static guchar * +_cogl_texture_allocate_waste_buffer (CoglTexture *tex) +{ + CoglTexSliceSpan *last_x_span; + CoglTexSliceSpan *last_y_span; + guchar *waste_buf = NULL; + + /* If the texture has any waste then allocate a buffer big enough to + fill the gaps */ + last_x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, + tex->slice_x_spans->len - 1); + last_y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, + tex->slice_y_spans->len - 1); + if (last_x_span->waste > 0 || last_y_span->waste > 0) + { + gint bpp = _cogl_get_format_bpp (tex->bitmap.format); + CoglTexSliceSpan *first_x_span + = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, 0); + CoglTexSliceSpan *first_y_span + = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, 0); + guint right_size = first_y_span->size * last_x_span->waste; + guint bottom_size = first_x_span->size * last_y_span->waste; + + waste_buf = g_malloc (MAX (right_size, bottom_size) * bpp); + } + + return waste_buf; +} + static gboolean _cogl_texture_upload_to_gl (CoglTexture *tex) { @@ -182,6 +211,7 @@ _cogl_texture_upload_to_gl (CoglTexture *tex) GLuint gl_handle; gint bpp; gint x,y; + guchar *waste_buf; CoglBitmap slice_bmp; /* FIXME: might optimize by not copying to intermediate slice @@ -190,6 +220,8 @@ _cogl_texture_upload_to_gl (CoglTexture *tex) bpp = _cogl_get_format_bpp (tex->bitmap.format); + waste_buf = _cogl_texture_allocate_waste_buffer (tex); + /* Iterate vertical slices */ for (y = 0; y < tex->slice_y_spans->len; ++y) { @@ -233,6 +265,62 @@ _cogl_texture_upload_to_gl (CoglTexture *tex) tex->gl_format, tex->gl_type, slice_bmp.data) ); + /* Fill the waste with a copies of the rightmost pixels */ + if (x_span->waste > 0) + { + const guchar *src = tex->bitmap.data + + y_span->start * tex->bitmap.rowstride + + (x_span->start + x_span->size - x_span->waste - 1) * bpp; + guchar *dst = waste_buf; + guint wx, wy; + + for (wy = 0; wy < y_span->size - y_span->waste; wy++) + { + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, src, bpp); + dst += bpp; + } + src += tex->bitmap.rowstride; + } + + GE( glTexSubImage2D (tex->gl_target, 0, + x_span->size - x_span->waste, 0, + x_span->waste, + y_span->size - y_span->waste, + tex->gl_format, tex->gl_type, + waste_buf) ); + } + + if (y_span->waste > 0) + { + const guchar *src = tex->bitmap.data + + ((y_span->start + y_span->size - y_span->waste - 1) + * tex->bitmap.rowstride) + + x_span->start * bpp; + guchar *dst = waste_buf; + guint wy, wx; + + for (wy = 0; wy < y_span->waste; wy++) + { + memcpy (dst, src, (x_span->size - x_span->waste) * bpp); + dst += (x_span->size - x_span->waste) * bpp; + + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, dst - bpp, bpp); + dst += bpp; + } + } + + GE( glTexSubImage2D (tex->gl_target, 0, + 0, y_span->size - y_span->waste, + x_span->size, + y_span->waste, + tex->gl_format, tex->gl_type, + waste_buf) ); + } + if (tex->auto_mipmap) cogl_wrap_glGenerateMipmap (tex->gl_target); @@ -240,7 +328,10 @@ _cogl_texture_upload_to_gl (CoglTexture *tex) g_free (slice_bmp.data); } } - + + if (waste_buf) + g_free (waste_buf); + return TRUE; } @@ -494,17 +585,22 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, GLuint source_gl_format, GLuint source_gl_type) { - gint bpp; - CoglSpanIter x_iter; - CoglSpanIter y_iter; - GLuint gl_handle; - gint source_x = 0, source_y = 0; - gint inter_w = 0, inter_h = 0; - gint local_x = 0, local_y = 0; - CoglBitmap slice_bmp; + CoglTexSliceSpan *x_span; + CoglTexSliceSpan *y_span; + gint bpp; + CoglSpanIter x_iter; + CoglSpanIter y_iter; + GLuint gl_handle; + gint source_x = 0, source_y = 0; + gint inter_w = 0, inter_h = 0; + gint local_x = 0, local_y = 0; + guchar *waste_buf; + CoglBitmap slice_bmp; bpp = _cogl_get_format_bpp (source_bmp->format); - + + waste_buf = _cogl_texture_allocate_waste_buffer (tex); + /* FIXME: might optimize by not copying to intermediate slice bitmap when source rowstride = bpp * width and the texture image is not sliced */ @@ -526,7 +622,10 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, inter_h = 0; continue; } - + + y_span = &g_array_index (tex->slice_y_spans, CoglTexSliceSpan, + y_iter.index); + /* Iterate horizontal spans */ for (source_x = src_x, _cogl_span_iter_begin (&x_iter, tex->slice_x_spans, @@ -544,7 +643,10 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, inter_w = 0; continue; } - + + x_span = &g_array_index (tex->slice_x_spans, CoglTexSliceSpan, + x_iter.index); + /* Pick intersection width and height */ inter_w = CLUTTER_FIXED_TO_INT (x_iter.intersect_end - x_iter.intersect_start); @@ -591,7 +693,81 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, source_gl_format, source_gl_type, slice_bmp.data) ); - + + /* If the x_span is sliced and the upload touches the + rightmost pixels then fill the waste with copies of the + pixels */ + if (x_span->waste > 0 + && local_x < x_span->size - x_span->waste + && local_x + inter_w >= x_span->size - x_span->waste) + { + const guchar *src = source_bmp->data + + (src_y + CLUTTER_FIXED_TO_INT (y_iter.intersect_start) + - dst_y) * source_bmp->rowstride + + (src_x + x_span->start + x_span->size - x_span->waste + - dst_x - 1) * bpp; + guchar *dst = waste_buf; + guint wx, wy; + + for (wy = 0; wy < inter_h; wy++) + { + for (wx = 0; wx < x_span->waste; wx++) + { + memcpy (dst, src, bpp); + dst += bpp; + } + src += source_bmp->rowstride; + } + + GE( glTexSubImage2D (tex->gl_target, 0, + x_span->size - x_span->waste, local_y, + x_span->waste, + inter_h, + source_gl_format, + source_gl_type, + waste_buf) ); + } + + /* same for the bottom-most pixels */ + if (y_span->waste > 0 + && local_y < y_span->size - y_span->waste + && local_y + inter_h >= y_span->size - y_span->waste) + { + const guchar *src = source_bmp->data + + (src_x + CLUTTER_FIXED_TO_INT (x_iter.intersect_start) + - dst_x) * bpp + + (src_y + y_span->start + y_span->size - y_span->waste + - dst_y - 1) * source_bmp->rowstride; + guchar *dst = waste_buf; + guint wy, wx; + guint copy_width; + + if (local_x + inter_w >= x_span->size - x_span->waste) + copy_width = x_span->size - local_x; + else + copy_width = inter_w; + + for (wy = 0; wy < y_span->waste; wy++) + { + memcpy (dst, src, inter_w * bpp); + dst += inter_w * bpp; + + for (wx = inter_w; wx < copy_width; wx++) + { + memcpy (dst, dst - bpp, bpp); + dst += bpp; + } + } + + GE( glTexSubImage2D (tex->gl_target, 0, + local_x, y_span->size - y_span->waste, + copy_width, + y_span->waste, + source_gl_format, + source_gl_type, + waste_buf) ); + } + if (tex->auto_mipmap) cogl_wrap_glGenerateMipmap (tex->gl_target); @@ -599,7 +775,10 @@ _cogl_texture_upload_subregion_to_gl (CoglTexture *tex, g_free (slice_bmp.data); } } - + + if (waste_buf) + g_free (waste_buf); + return TRUE; }