/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2007,2008,2009,2011,2012 Intel Corporation.
* Copyright (C) 2010 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
*
*
* Authors:
* Matthew Allum
* Neil Roberts
* Robert Bragg
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl-context-private.h"
#include "cogl-texture.h"
#include "cogl-util.h"
#include "cogl-texture-2d.h"
#include "cogl-texture-2d-private.h"
#include "cogl-primitive-texture.h"
#include "cogl-texture-2d-sliced-private.h"
#include "cogl-private.h"
#include "cogl-object.h"
#include "cogl-bitmap-private.h"
#include "cogl-atlas-texture-private.h"
#include "cogl-error-private.h"
#include "cogl-texture-rectangle.h"
#include "cogl-sub-texture.h"
#include "cogl-texture-2d-gl.h"
static CoglTexture *
_cogl_texture_new_from_bitmap (CoglBitmap *bitmap,
CoglTextureFlags flags,
CoglPixelFormat internal_format,
CoglBool can_convert_in_place,
CoglError **error);
static void
set_auto_mipmap_cb (CoglTexture *sub_texture,
const float *sub_texture_coords,
const float *meta_coords,
void *user_data)
{
cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (sub_texture),
FALSE);
}
CoglTexture *
cogl_texture_new_with_size (unsigned int width,
unsigned int height,
CoglTextureFlags flags,
CoglPixelFormat internal_format)
{
CoglTexture *tex;
CoglError *skip_error = NULL;
_COGL_GET_CONTEXT (ctx, NULL);
if ((_cogl_util_is_pot (width) && _cogl_util_is_pot (height)) ||
(cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) &&
cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP)))
{
/* First try creating a fast-path non-sliced texture */
tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx,
width, height,
internal_format));
/* TODO: instead of allocating storage here it would be better
* if we had some api that let us just check that the size is
* supported by the hardware so storage could be allocated
* lazily when uploading data. */
if (!cogl_texture_allocate (tex, &skip_error))
{
cogl_error_free (skip_error);
cogl_object_unref (tex);
tex = NULL;
}
}
else
tex = NULL;
if (!tex)
{
/* If it fails resort to sliced textures */
int max_waste = flags & COGL_TEXTURE_NO_SLICING ? -1 : COGL_TEXTURE_MAX_WASTE;
tex = COGL_TEXTURE (cogl_texture_2d_sliced_new_with_size (ctx,
width,
height,
max_waste,
internal_format));
}
/* NB: This api existed before Cogl introduced lazy allocation of
* textures and so we maintain its original synchronous allocation
* semantics and return NULL if allocation fails... */
if (!cogl_texture_allocate (tex, &skip_error))
{
cogl_error_free (skip_error);
cogl_object_unref (tex);
return NULL;
}
if (tex &&
flags & COGL_TEXTURE_NO_AUTO_MIPMAP)
{
cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex),
0, 0, 1, 1,
COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE,
COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE,
set_auto_mipmap_cb,
NULL);
}
return tex;
}
static CoglTexture *
_cogl_texture_new_from_data (CoglContext *ctx,
int width,
int height,
CoglTextureFlags flags,
CoglPixelFormat format,
CoglPixelFormat internal_format,
int rowstride,
const uint8_t *data,
CoglError **error)
{
CoglBitmap *bmp;
CoglTexture *tex;
_COGL_RETURN_VAL_IF_FAIL (format != COGL_PIXEL_FORMAT_ANY, NULL);
_COGL_RETURN_VAL_IF_FAIL (data != NULL, NULL);
/* Rowstride from width if not given */
if (rowstride == 0)
rowstride = width * _cogl_pixel_format_get_bytes_per_pixel (format);
/* Wrap the data into a bitmap */
bmp = cogl_bitmap_new_for_data (ctx,
width, height,
format,
rowstride,
(uint8_t *) data);
tex = _cogl_texture_new_from_bitmap (bmp,
flags,
internal_format,
FALSE, /* can't convert in place */
error);
cogl_object_unref (bmp);
return tex;
}
CoglTexture *
cogl_texture_new_from_data (int width,
int height,
CoglTextureFlags flags,
CoglPixelFormat format,
CoglPixelFormat internal_format,
int rowstride,
const uint8_t *data)
{
CoglError *ignore_error = NULL;
CoglTexture *tex;
_COGL_GET_CONTEXT (ctx, NULL);
tex = _cogl_texture_new_from_data (ctx,
width, height,
flags,
format, internal_format,
rowstride,
data,
&ignore_error);
if (!tex)
cogl_error_free (ignore_error);
return tex;
}
static CoglTexture *
_cogl_texture_new_from_bitmap (CoglBitmap *bitmap,
CoglTextureFlags flags,
CoglPixelFormat internal_format,
CoglBool can_convert_in_place,
CoglError **error)
{
CoglContext *ctx = _cogl_bitmap_get_context (bitmap);
CoglAtlasTexture *atlas_tex;
CoglTexture *tex;
CoglError *internal_error = NULL;
if (!flags &&
!COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_ATLAS))
{
/* First try putting the texture in the atlas */
if ((atlas_tex = _cogl_atlas_texture_new_from_bitmap (bitmap,
internal_format,
can_convert_in_place,
&internal_error)))
return COGL_TEXTURE (atlas_tex);
cogl_error_free (internal_error);
internal_error = NULL;
}
/* If that doesn't work try a fast path 2D texture */
if ((_cogl_util_is_pot (bitmap->width) &&
_cogl_util_is_pot (bitmap->height)) ||
(cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_BASIC) &&
cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_NPOT_MIPMAP)))
{
tex = COGL_TEXTURE (_cogl_texture_2d_new_from_bitmap (bitmap,
internal_format,
can_convert_in_place,
&internal_error));
if (!tex)
{
cogl_error_free (internal_error);
internal_error = NULL;
}
}
else
tex = NULL;
if (!tex)
{
/* Otherwise create a sliced texture */
int max_waste = flags & COGL_TEXTURE_NO_SLICING ? -1 : COGL_TEXTURE_MAX_WASTE;
tex = COGL_TEXTURE (_cogl_texture_2d_sliced_new_from_bitmap (bitmap,
max_waste,
internal_format,
can_convert_in_place,
error));
}
if (tex &&
flags & COGL_TEXTURE_NO_AUTO_MIPMAP)
{
cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex),
0, 0, 1, 1,
COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE,
COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE,
set_auto_mipmap_cb,
NULL);
}
return tex;
}
CoglTexture *
cogl_texture_new_from_bitmap (CoglBitmap *bitmap,
CoglTextureFlags flags,
CoglPixelFormat internal_format)
{
CoglError *ignore_error = NULL;
CoglTexture *tex =
_cogl_texture_new_from_bitmap (bitmap,
flags,
internal_format,
FALSE, /* can't convert in-place */
&ignore_error);
if (!tex)
cogl_error_free (ignore_error);
return tex;
}
CoglTexture *
cogl_texture_new_from_file (const char *filename,
CoglTextureFlags flags,
CoglPixelFormat internal_format,
CoglError **error)
{
CoglBitmap *bmp;
CoglTexture *texture = NULL;
_COGL_GET_CONTEXT (ctx, NULL);
_COGL_RETURN_VAL_IF_FAIL (error == NULL || *error == NULL, NULL);
bmp = cogl_bitmap_new_from_file (filename, error);
if (bmp == NULL)
return NULL;
texture = _cogl_texture_new_from_bitmap (bmp, flags,
internal_format,
TRUE, /* can convert in-place */
error);
cogl_object_unref (bmp);
return texture;
}
CoglTexture *
cogl_texture_new_from_foreign (GLuint gl_handle,
GLenum gl_target,
GLuint width,
GLuint height,
GLuint x_pot_waste,
GLuint y_pot_waste,
CoglPixelFormat format)
{
_COGL_GET_CONTEXT (ctx, NULL);
#ifdef HAVE_COGL_GL
if (gl_target == GL_TEXTURE_RECTANGLE_ARB)
{
CoglTextureRectangle *texture_rectangle;
CoglSubTexture *sub_texture;
if (x_pot_waste != 0 || y_pot_waste != 0)
{
/* It shouldn't be necessary to have waste in this case since
* the texture isn't limited to power of two sizes. */
g_warning ("You can't create a foreign GL_TEXTURE_RECTANGLE cogl "
"texture with waste\n");
return NULL;
}
texture_rectangle = cogl_texture_rectangle_new_from_foreign (ctx,
gl_handle,
width,
height,
format,
NULL);
/* CoglTextureRectangle textures work with non-normalized
* coordinates, but the semantics for this function that people
* depend on are that all returned texture works with normalized
* coordinates so we wrap with a CoglSubTexture... */
sub_texture = cogl_sub_texture_new (ctx,
COGL_TEXTURE (texture_rectangle),
0, 0, width, height);
return COGL_TEXTURE (sub_texture);
}
#endif
if (x_pot_waste != 0 || y_pot_waste != 0)
return COGL_TEXTURE (_cogl_texture_2d_sliced_new_from_foreign (ctx,
gl_handle,
gl_target,
width,
height,
x_pot_waste,
y_pot_waste,
format,
NULL));
else
return COGL_TEXTURE (cogl_texture_2d_new_from_foreign (ctx,
gl_handle,
width,
height,
format,
NULL));
}