[Piglit] [PATCH 7/9] util: Add utilities for KTX file format

Chad Versace chad.versace at linux.intel.com
Mon Jul 9 10:43:35 PDT 2012


The KTX (Khronos texture) file format specifies a simple format for
storing texture miptrees. This patch adds utility functions for reading
and inspecting KTX files, iterating over the contained images, and easily
loading the miptree with glTexImage().

A future test (oes_compressed_etc1_rgb8_test-miptree) will use this to
load an ETC1 miptree and a reference RGB miptree.

Signed-off-by: Chad Versace <chad.versace at linux.intel.com>
---
 tests/util/CMakeLists.txt |   1 +
 tests/util/piglit_ktx.c   | 854 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/util/piglit_ktx.h   | 270 +++++++++++++++
 3 files changed, 1125 insertions(+)
 create mode 100644 tests/util/piglit_ktx.c
 create mode 100644 tests/util/piglit_ktx.h

diff --git a/tests/util/CMakeLists.txt b/tests/util/CMakeLists.txt
index f0795b0..ca9d7e4 100644
--- a/tests/util/CMakeLists.txt
+++ b/tests/util/CMakeLists.txt
@@ -22,6 +22,7 @@ set(UTIL_GL_SOURCES
 	piglit-framework.c
 	piglit-framework-fbo.c
 	piglit-framework-glut.c
+        piglit_ktx.c
 	rgb9e5.c
 	)
 
diff --git a/tests/util/piglit_ktx.c b/tests/util/piglit_ktx.c
new file mode 100644
index 0000000..5f49a9c
--- /dev/null
+++ b/tests/util/piglit_ktx.c
@@ -0,0 +1,854 @@
+/*
+ * Copyright 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <sys/mman.h>
+
+#include "piglit_ktx.h"
+
+/* FIXME: Remove #defines when piglit-dispatch gains support for GLES. */
+#define GL_TEXTURE_1D				0x0DE0
+#define GL_TEXTURE_1D_ARRAY			0x8C18
+#define GL_TEXTURE_2D				0x0DE1
+#define GL_TEXTURE_2D_ARRAY			0x8C1A
+#define GL_TEXTURE_3D				0x806F
+#define GL_TEXTURE_CUBE_MAP			0x8513
+#define GL_TEXTURE_CUBE_MAP_ARRAY		0x9009
+
+#define GL_TEXTURE_BINDING_1D			0x8068
+#define GL_TEXTURE_BINDING_1D_ARRAY		0x8C1C
+#define GL_TEXTURE_BINDING_2D			0x8069
+#define GL_TEXTURE_BINDING_2D_ARRAY		0x8C1D
+#define GL_TEXTURE_BINDING_3D			0x806A
+#define GL_TEXTURE_BINDING_CUBE_MAP		0x8514
+#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY	0x900A
+
+static const int piglit_ktx_header_length = 64;
+static const char piglit_ktx_magic_number[12] =
+	{ 0xab, 'K', 'T', 'X', ' ', '1', '1', 0xbb, '\r', '\n', 0x1a, '\n' };
+
+static void
+minify(uint32_t *n)
+{
+	assert(*n != 0);
+
+	if (*n > 1)
+		*n >>= 1;
+}
+
+/**
+ * \brief KTX data.
+ */
+struct piglit_ktx {
+	struct piglit_ktx_info info;
+
+	/** \brief The raw KTX data. */
+	void *data;
+
+	/**
+	 * \brief Array of images.
+	 *
+	 * Array length is piglit_ktx_info::num_images.
+	 */
+	struct piglit_ktx_image *images;
+};
+
+static void
+piglit_ktx_error(const char *format, ...)
+{
+	va_list va;
+	va_start(va, format);
+
+	printf("error: piglit_ktx: ");
+	vprintf(format, va);
+	printf("\n");
+	fflush(stdout);
+
+	va_end(va);
+}
+
+void
+piglit_ktx_destroy(struct piglit_ktx *self)
+{
+	if (self == NULL)
+		return;
+
+	if (self->images != NULL)
+		free(self->images);
+
+	if (self->data)
+		free(self->data);
+
+	free(self);
+}
+
+/**
+ * \brief Calculate and set self->info->target.
+ */
+static bool
+piglit_ktx_calc_target(struct piglit_ktx *self)
+{
+	struct piglit_ktx_info *info = &self->info;
+
+	if (info->pixel_width == 0) {
+		goto bad_target;
+	} else if (info->pixel_height == 0) {
+		if (info->pixel_depth != 0)
+			goto bad_target;
+		if (info->num_faces != 1)
+			goto bad_target;
+
+		if (info->array_length == 0) {
+			info->target = GL_TEXTURE_1D;
+		} else {
+			info->target = GL_TEXTURE_1D_ARRAY;
+		}
+	} else if (info->pixel_depth == 0) {
+		if (info->array_length == 0) {
+			if (info->num_faces == 1)
+				info->target = GL_TEXTURE_2D;
+			else if (info->num_faces == 6)
+				info->target = GL_TEXTURE_CUBE_MAP;
+			else
+				goto bad_target;
+		} else {
+			if (info->num_faces == 1)
+				info->target = GL_TEXTURE_2D_ARRAY;
+			else if (info->num_faces == 6)
+				info->target = GL_TEXTURE_CUBE_MAP_ARRAY;
+			else
+				goto bad_target;
+		}
+	} else {
+		if (info->array_length != 0)
+			goto bad_target;
+		if (info->num_faces != 0)
+			goto bad_target;
+
+		info->target = GL_TEXTURE_3D;
+	}
+
+	return true;
+
+bad_target:
+	piglit_ktx_error("%s", "invalid texture target: pixel_size, "
+			 "array_size, and num_faces are incompatible");
+	return false;
+}
+
+static bool
+piglit_ktx_parse_header(struct piglit_ktx *self)
+{
+	struct piglit_ktx_info *info = &self->info;
+	bool ok = true;
+
+	/*
+	 * Used to iterate through KTX header with a step of 32 bits. The KTX
+	 * spec declares all header fields as u32 values.
+	 */
+	const uint32_t *u32 = self->data;
+
+	if (info->size < piglit_ktx_header_length) {
+		piglit_ktx_error("data size must be at least length of KTX "
+				 "header, %d bytes", piglit_ktx_header_length);
+		return false;
+	}
+
+	if (memcmp(u32, piglit_ktx_magic_number,
+		   sizeof(piglit_ktx_magic_number)) != 0) {
+		piglit_ktx_error("%s", "KTX header does not begin with KTX "
+				 "magic number");
+		return false;
+	}
+
+	switch (u32[3]) {
+	case 0x04030201:
+		/* Little endian is supported. */
+		break;
+	case 0x01020304:
+		piglit_ktx_error("%s", "KTX header declares big endian data, "
+				 "but Piglit supports only little endian");
+		return false;
+	default:
+		piglit_ktx_error("KTX header has bad value (0x%x) for "
+				 "endianness flag", u32[3]);
+		return false;
+	}
+
+	info->gl_type = u32[4];
+	info->gl_type_size = u32[5];
+	info->gl_format = u32[6];
+	info->gl_internal_format = u32[7];
+	info->gl_base_internal_format = u32[8];
+	info->pixel_width = u32[9];
+	info->pixel_height = u32[10];
+	info->pixel_depth = u32[11];
+	info->array_length = u32[12];
+	info->num_faces = u32[13];
+	info->num_miplevels = u32[14];
+
+	if (info->num_miplevels == 0) {
+		piglit_ktx_error("%s", "KTX header requests automatic "
+		                 "mipmap generation, which Piglit does not "
+		                 "support");
+		return false;
+	}
+
+	if (u32[15] != 0) {
+		piglit_ktx_error("%s", "KTX header declares presence of "
+				 "arbitrary key/value data, which Piglit "
+				 "does not support");
+		return false;
+	}
+
+	ok = piglit_ktx_calc_target(self);
+	if (!ok)
+		return false;
+
+	if (info->target == GL_TEXTURE_CUBE_MAP)
+		info->num_images = 6 * info->num_miplevels;
+	else
+		info->num_images = info->num_miplevels;
+
+	return true;
+}
+
+static void
+piglit_ktx_calc_base_image_size(struct piglit_ktx *self,
+				uint32_t *width,
+				uint32_t *height,
+				uint32_t *depth)
+{
+	struct piglit_ktx_info *info = &self->info;
+
+	switch (info->target) {
+		case GL_TEXTURE_1D:
+			*width = info->pixel_width;
+			*height = 0;
+			*depth = 0;
+			break;
+		case GL_TEXTURE_1D_ARRAY:
+			*width = info->pixel_width;
+			*height = info->array_length;
+			*depth = 0;
+			break;
+		case GL_TEXTURE_2D:
+			*width = info->pixel_width;
+			*height = info->pixel_height;
+			*depth = 0;
+			break;
+		case GL_TEXTURE_2D_ARRAY:
+			*width = info->pixel_width;
+			*height = info->pixel_height;
+			*depth = info->array_length;
+			break;
+		case GL_TEXTURE_CUBE_MAP:
+			*width = info->pixel_width;
+			*height = info->pixel_height;
+			*depth = 0;
+			break;
+		case GL_TEXTURE_CUBE_MAP_ARRAY:
+			*width = info->pixel_width;
+			*height = info->pixel_height;
+			*depth = 6 * info->array_length;
+			break;
+		case GL_TEXTURE_3D:
+			*width = info->pixel_width;
+			*height = info->pixel_height;
+			*depth = info->pixel_depth;
+			break;
+		default:
+			assert(0);
+			break;
+	}
+}
+
+static bool
+piglit_ktx_parse_images(struct piglit_ktx *self)
+{
+	struct piglit_ktx_info *info = &self->info;
+
+	/* Current image being parsed. */
+	struct piglit_ktx_image *image;
+
+	/*
+	 * Current byte being parsed. Used to traverse data with a step of
+	 * either 8 or 32 bits.
+	 */
+	union piglit_ktx_position {
+		uint8_t *u8;
+		uint32_t *u32;
+	} p;
+
+	/* Size of image, as passed to glTexImage(). */
+	uint32_t pixel_width;
+	uint32_t pixel_height;
+	uint32_t pixel_depth;
+
+	/* Loop counters */
+	int miplevel;
+	int face;
+
+	piglit_ktx_calc_base_image_size(self,
+					&pixel_width,
+					&pixel_height,
+					&pixel_depth);
+
+	self->images = calloc(info->num_images, sizeof(*self->images));
+
+	/* Skip header. */
+	p.u8 = self->data;
+	p.u8 += piglit_ktx_header_length;
+
+	/* Begin parsing first image. */
+	image = &self->images[0];
+
+#define CUR_SIZE ((void*) p.u8 - self->data)
+
+	for (miplevel = 0; miplevel < info->num_miplevels; ++miplevel) {
+		uint32_t image_size;
+
+		if (info->size < CUR_SIZE + 1) {
+			/*
+			 * Reading the image size below would access
+			 * out-of-bounds memory.
+			 */
+			piglit_ktx_error("size of data stream must be at "
+					 "least %u", CUR_SIZE + 1);
+			return false;
+		}
+
+		image_size = *p.u32;
+		++p.u32;
+
+		for (face = 0; face < 6; ++face) {
+			assert(image - self->images < info->num_images);
+
+			image->data = p.u8;
+			image->size = image_size;
+			image->miplevel = miplevel;
+			image->face = face;
+			image->pixel_width = pixel_width;
+			image->pixel_height = pixel_height;
+			image->pixel_depth = pixel_depth;
+
+			p.u8 += image_size;
+			++image;
+
+			/* Padding */
+			while (CUR_SIZE % 4 != 0)
+				++p.u8;
+
+			if (info->target != GL_TEXTURE_CUBE_MAP)
+				break;
+		}
+
+		switch (info->target) {
+			case GL_TEXTURE_3D:
+				minify(&pixel_width);
+				minify(&pixel_height);
+				minify(&pixel_depth);
+				break;
+			case GL_TEXTURE_2D:
+			case GL_TEXTURE_2D_ARRAY:
+			case GL_TEXTURE_CUBE_MAP:
+			case GL_TEXTURE_CUBE_MAP_ARRAY:
+				minify(&pixel_width);
+				minify(&pixel_height);
+				break;
+			case GL_TEXTURE_1D:
+			case GL_TEXTURE_1D_ARRAY:
+				minify(&pixel_width);
+				break;
+			default:
+				assert(0);
+				break;
+		}
+	}
+
+	if (info->size < CUR_SIZE) {
+		/*
+		 * The last image's data lies, at least partially, in
+		 * out-of-bounds memory.
+		 */
+		piglit_ktx_error("size of data stream must be at least %zd",
+				 CUR_SIZE);
+		return false;
+	}
+
+
+	/*
+	 * Up until now, info->size was an upper bound on the data size. Now
+	 * the actual data size is known.
+	 */
+	info->size = CUR_SIZE;
+
+	return true;
+
+#undef CUR_SIZE
+}
+
+static bool
+piglit_ktx_parse_data(struct piglit_ktx *self)
+{
+	bool ok = true;
+
+	ok = ok && piglit_ktx_parse_header(self);
+	ok = ok && piglit_ktx_parse_images(self);
+
+	return ok;
+}
+
+struct piglit_ktx*
+piglit_ktx_read_file(const char *filename)
+{
+	struct piglit_ktx *self;
+
+	FILE *file = NULL;
+	size_t size_read = 0;
+
+	bool ok = true;
+	int error = 0;
+
+	self = calloc(1, sizeof(*self));
+	if (self == NULL)
+		goto out_of_memory;
+
+	file = fopen(filename, "r");
+	if (file == NULL)
+		goto bad_open;
+
+	error = fseek(file, 0, SEEK_END);
+	if (error)
+		goto bad_read;
+	self->info.size = ftell(file);
+	error = fseek(file, 0, SEEK_SET);
+	if (error)
+		goto bad_read;
+
+	self->data = malloc(self->info.size);
+	if (self->data == NULL)
+		goto out_of_memory;
+
+	/*
+	 * On Unix, one should mmap() here rather than copy. But this code
+	 * needs to support Windows too.
+	 */
+	size_read = fread(self->data, 1, self->info.size, file);
+	if (size_read < self->info.size)
+		goto bad_read;
+
+	ok = piglit_ktx_parse_data(self);
+	goto end;
+
+out_of_memory:
+	ok = false;
+	piglit_ktx_error("%s", "out of memory");
+	goto end;
+
+bad_open:
+	ok = false;
+	piglit_ktx_error("failed to open file: %s", filename);
+	goto end;
+
+bad_read:
+	ok = false;
+	piglit_ktx_error("errors in reading file: %s", filename);
+	goto end;
+
+end:
+	if (file != NULL)
+		fclose(file);
+
+	if (!ok) {
+		piglit_ktx_destroy(self);
+		self = NULL;
+	}
+
+	return self;
+}
+
+struct piglit_ktx*
+ktx_file_read_bytes(const void *bytes , size_t size)
+{
+	struct piglit_ktx *self;
+	bool ok = true;
+
+	self = calloc(1, sizeof(*self));
+	if (self == NULL) {
+		piglit_ktx_error("%s", "out of memory");
+		return NULL;
+	}
+
+	self->info.size = size;
+	memcpy(self->data, bytes, size);
+
+	ok = piglit_ktx_parse_data(self);
+	if (!ok) {
+		piglit_ktx_destroy(self);
+		return NULL;
+	}
+
+	return self;
+}
+
+bool
+piglit_ktx_write_file(struct piglit_ktx *self, const char *filename)
+{
+	FILE *file = NULL;
+	size_t size_written = 0;
+	bool ok = true;
+
+	file = fopen(filename, "w");
+	if (file == NULL)
+		goto bad_open;
+
+	size_written = fwrite(self->data, self->info.size, 1, file);
+	if (size_written < self->info.size)
+		goto bad_write;
+
+	goto end;
+
+bad_open:
+	ok = false;
+	piglit_ktx_error("failed to open file: %s", filename);
+	goto end;
+
+bad_write:
+	ok = false;
+	piglit_ktx_error("errors in writing file: %s", filename);
+	goto end;
+
+end:
+	if (file != NULL)
+		fclose(file);
+
+	return ok;
+}
+
+bool
+piglit_ktx_write_bytes(struct piglit_ktx *self, void *bytes)
+{
+	memcpy(bytes, self->data, self->info.size);
+	return true;
+}
+
+const struct piglit_ktx_image*
+piglit_ktx_get_image(struct piglit_ktx *self,
+		     int miplevel,
+		     int cube_face)
+{
+	const struct piglit_ktx_info *info = &self->info;
+
+	if (miplevel < 0 || miplevel >= info->num_miplevels) {
+		piglit_ktx_error("bad miplevel %d", miplevel);
+		return NULL;
+	}
+
+	if (cube_face < 0 || cube_face >= 6) {
+		piglit_ktx_error("bad cube_face %d", cube_face);
+		return NULL;
+	}
+
+	if (cube_face != 0 && info->target != GL_TEXTURE_CUBE_MAP) {
+		piglit_ktx_error("cube face %d was requested. cube face may "
+				 "be requested only for non-array cubemaps",
+				 cube_face);
+		return NULL;
+	}
+
+	if (info->target == GL_TEXTURE_CUBE_MAP)
+		return &self->images[6 * miplevel + cube_face];
+	else
+		return &self->images[miplevel];
+}
+
+static bool
+piglit_ktx_load_cubeface(struct piglit_ktx *self,
+                         int image,
+                         GLenum *gl_error)
+{
+	const struct piglit_ktx_info *info = &self->info;
+	const struct piglit_ktx_image *img = &self->images[image];
+
+	GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + (image % 6);
+	int level = image / 6;
+
+	if (info->gl_type == 0)
+		glCompressedTexImage2D(face,
+				       level,
+				       info->gl_internal_format,
+				       img->pixel_width,
+				       img->pixel_height,
+				       0 /*border*/,
+				       img->size,
+				       img->data);
+	else
+		glTexImage2D(face,
+			     level,
+			     info->gl_internal_format,
+			     img->pixel_width,
+			     img->pixel_height,
+			     0 /*border*/,
+			     info->gl_format,
+			     info->gl_type,
+			     img->data);
+
+	*gl_error = glGetError();
+	return *gl_error == 0;
+}
+
+static bool
+piglit_ktx_load_noncubeface(struct piglit_ktx *self,
+                            int image,
+                            GLenum *gl_error)
+{
+	const struct piglit_ktx_info *info = &self->info;
+	const struct piglit_ktx_image *img = &self->images[image];
+	int level = image;
+
+	switch (info->target) {
+	case GL_TEXTURE_1D:
+#ifdef USE_OPENGL
+		if (info->gl_type == 0)
+			glCompressedTexImage1D(info->target,
+					       level,
+					       info->gl_internal_format,
+					       img->pixel_width,
+					       0 /*border*/,
+					       img->size,
+					       img->data);
+		else
+			glTexImage1D(info->target,
+				     level,
+				     info->gl_internal_format,
+				     img->pixel_width,
+				     0 /*border*/,
+				     info->gl_format,
+				     info->gl_type,
+				     img->data);
+		break;
+#else
+		goto unsupported_on_gles;
+#endif
+	case GL_TEXTURE_1D_ARRAY:
+	case GL_TEXTURE_2D:
+	case GL_TEXTURE_CUBE_MAP:
+		if (info->gl_type == 0)
+			glCompressedTexImage2D(info->target,
+					       level,
+					       info->gl_internal_format,
+					       img->pixel_width,
+					       img->pixel_height,
+					       0 /*border*/,
+					       img->size,
+					       img->data);
+		else
+			glTexImage2D(info->target,
+				     level,
+				     info->gl_internal_format,
+				     img->pixel_width,
+				     img->pixel_height,
+				     0 /*border*/,
+				     info->gl_format,
+				     info->gl_type,
+				     img->data);
+		break;
+	case GL_TEXTURE_2D_ARRAY:
+	case GL_TEXTURE_3D:
+	case GL_TEXTURE_CUBE_MAP_ARRAY:
+#ifdef USE_OPENGL
+		if (info->gl_type == 0)
+			glCompressedTexImage3D(info->target,
+					       level,
+					       info->gl_internal_format,
+					       img->pixel_width,
+					       img->pixel_height,
+					       img->pixel_depth,
+					       0 /*border*/,
+					       img->size,
+					       img->data);
+		else
+			glTexImage3D(info->target,
+				     level,
+				     info->gl_internal_format,
+				     img->pixel_width,
+				     img->pixel_height,
+				     img->pixel_depth,
+				     0 /*border*/,
+				     info->gl_format,
+				     info->gl_type,
+				     img->data);
+		break;
+#else
+		goto unsupported_on_gles;
+#endif
+	default:
+		*gl_error = 0;
+		piglit_ktx_error("bad texture target 0x%x",
+		                 info->target);
+		return false;
+	}
+
+	*gl_error = glGetError();
+	return *gl_error == 0;
+
+#ifndef USE_OPENGL
+unsupported_on_gles:
+	*gl_error = 0;
+	piglit_ktx_error("%s", "GLES supports only GL_TEXTURE_2D and "
+			 "GL_TEXTURE_CUBE_MAP");
+	return false;
+#endif
+}
+
+static bool
+piglit_ktx_load_image(struct piglit_ktx *self,
+                      int image,
+                      GLenum *gl_error)
+{
+	if (self->info.target == GL_TEXTURE_CUBE_MAP)
+		return piglit_ktx_load_cubeface(self, image, gl_error);
+	else
+		return piglit_ktx_load_noncubeface(self, image, gl_error);
+}
+
+static GLuint
+target_to_texture_binding(GLuint target)
+{
+	switch (target) {
+		case GL_TEXTURE_1D:
+			return GL_TEXTURE_BINDING_1D;
+		case GL_TEXTURE_1D_ARRAY:
+			return GL_TEXTURE_BINDING_1D_ARRAY;
+		case GL_TEXTURE_2D:
+			return GL_TEXTURE_BINDING_2D;
+		case GL_TEXTURE_2D_ARRAY:
+			return GL_TEXTURE_BINDING_2D_ARRAY;
+		case GL_TEXTURE_CUBE_MAP:
+			return GL_TEXTURE_BINDING_2D;
+		case GL_TEXTURE_CUBE_MAP_ARRAY:
+			return GL_TEXTURE_BINDING_CUBE_MAP_ARRAY;
+			break;
+		case GL_TEXTURE_3D:
+			return GL_TEXTURE_BINDING_3D;
+		default:
+			assert(0);
+			break;
+	}
+}
+
+bool
+piglit_ktx_load_texture(struct piglit_ktx *self,
+			GLuint *tex_name,
+			GLenum *gl_error)
+{
+	const struct piglit_ktx_info *info = &self->info;
+
+	/*
+	 * Local store for glGetError(). A local store is needed because
+	 * gl_error may be null.
+	 */
+	GLenum my_gl_error = GL_NO_ERROR;
+
+	/*
+	 * The texture object bound to the texture target before this function call.
+	 * Before returning, we rebind the texture.
+	 */
+	GLint old_bound_tex;
+
+	/*
+	 * The GL_UNPACK_ALIGNMENT before this function call.
+	 * For KTX, the alignment must be set to 4. Before returning, we restore the unpack alignment
+	 *
+	 */
+	GLint old_unpack_alignment;
+
+	bool made_texture = false;
+
+	bool ok = true;
+	int i;
+
+	assert(tex_name != NULL);
+
+	glGetIntegerv(target_to_texture_binding(info->target),
+	              &old_bound_tex);
+	glGetIntegerv(GL_UNPACK_ALIGNMENT, &old_unpack_alignment);
+
+	/* Reset GL error state. */
+	while (glGetError())
+		;;
+
+	if (*tex_name == 0) {
+		glGenTextures(1, tex_name);
+		made_texture = true;
+
+		my_gl_error = glGetError();
+		if (my_gl_error)
+			goto fail;
+	}
+
+	glBindTexture(info->target, *tex_name);
+	my_gl_error = glGetError();
+	if (my_gl_error)
+		goto fail;
+
+	for (i = 0; i < info->num_images; ++i) {
+		ok = piglit_ktx_load_image(self, i, &my_gl_error);
+		if (!ok)
+			goto fail;
+	}
+
+	goto cleanup;
+
+fail:
+	ok = false;
+
+	if (gl_error != NULL)
+		*gl_error = my_gl_error;
+
+	if (made_texture && *tex_name != 0) {
+		glDeleteTextures(1, tex_name);
+		*tex_name = 0;
+	}
+
+cleanup:
+	/* Reset GL error state. */
+	while (glGetError())
+		;;
+
+	glBindTexture(info->target, old_bound_tex);
+	glPixelStorei(GL_UNPACK_ALIGNMENT, old_unpack_alignment);
+	return ok;
+}
+
+const struct piglit_ktx_info*
+piglit_ktx_get_info(struct piglit_ktx *self)
+{
+	return &self->info;
+}
diff --git a/tests/util/piglit_ktx.h b/tests/util/piglit_ktx.h
new file mode 100644
index 0000000..d150423
--- /dev/null
+++ b/tests/util/piglit_ktx.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/**
+ * \file
+ *
+ * \brief Utilities for the KTX file format.
+ *
+ * The KTX (Khronos texture) file format specifies a simple format for
+ * storing texture miptrees. The file format allows texture data for any
+ * GL texture format and any GL texture target.
+ *
+ * \see http://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include <piglit/gl_wrap.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct piglit_ktx;
+
+struct piglit_ktx_info {
+	/** \brief Size in bytes of the raw KTX data. */
+	size_t size;
+
+	/**
+	 * \brief GL texture target.
+	 *
+	 * This is the `target` agument passed to glTexImage() It is
+	 * completely determined by pixel_size, array_length, and num_faces.
+	 * Valid values are
+	 *   - GL_TEXTURE_1D
+	 *   - GL_TEXTURE_1D_ARRAY
+	 *   - GL_TEXTURE_2D
+	 *   - GL_TEXTURE_2D_ARRAY
+	 *   - GL_TEXTURE_3D
+	 *   - GL_TEXTURE_CUBE_MAP
+	 *   - GL_TEXTURE_CUBE_MAP_ARRAY
+	 */
+	GLenum target;
+
+	/**
+	 * For compressed textures, gl_type is 0. For non-compressed textures,
+	 * gl_type is the 'type' argument passed to glTexImage(). For example,
+	 * GL_FLOAT.
+	 */
+	uint32_t gl_type;
+
+	/**
+	 * For compressed textures, gl_type_size is 1. For non-compressed
+	 * textures, gl_type_size is the size in bytes of gl_type. For example,
+	 * if gl_type is GL_FLOAT, gl_type_size is 4.
+	 */
+	uint32_t gl_type_size;
+
+	/**
+	 * For compressed textures, gl_format is 0. For non-compressed textures,
+	 * gl_format is the 'format' argument passed to glTexImage(). For
+	 * example, GL_RGBA.
+	 */
+	uint32_t gl_format;
+
+	/**
+	 * For compressed and non-compressed textures, gl_internal_format is the
+	 * 'internal_format' argument passed to glTexImage(). For
+	 * non-compressed textures, this is always a sized format. For example,
+	 * GL_RGBA32F.
+	 */
+	uint32_t gl_internal_format;
+
+	/**
+	 * For compressed textures, gl_base_internal_format is the same as
+	 * gl_internal_format. For non-compressed textures,
+	 * gl_base_internal_format is the same as gl_internal_format.
+	 *
+	 * (I (chadv) dont' understand what purpose this field serves, But
+	 * the KTX spec requires it in the header).
+	 */
+	uint32_t gl_base_internal_format;
+
+	/**
+	 * \name Size of texture, in pixels.
+	 * \{
+	 *
+	 * For 1D textures, height and depth are 0. For 2D and cube textures,
+	 * depth is 0.  For block compressed textures, the sizes are not
+	 * rounded to block size.
+	 *
+	 * Note: The sizes here are those in the KTX header, which differ from
+	 * the sizes passed to glTexImage().
+	 */
+	uint32_t pixel_width;
+	uint32_t pixel_height;
+	uint32_t pixel_depth;
+	/** \} */
+
+	/**
+	 * If the texture is not an array texture, then array_lengthis 0.
+	 */
+	uint32_t array_length;
+
+	/**
+	 * For cubemaps and cubemap arrays, num_faces is 6. For all other
+	 * textures, it is 1.
+	 */
+	uint32_t num_faces;
+
+	/**
+	 * For non-mipmapped textures, num_miplevels is 1.
+	 */
+	uint32_t num_miplevels;
+
+	/**
+	 * For non-array cubemaps, the number of images is 6 * num_miplevels.
+	 * For all other textures, the number of images and miplevels is the
+	 * same.
+	 */
+	uint32_t num_images;
+};
+
+struct piglit_ktx_image {
+	/**
+	 * \brief The raw image data.
+	 *
+	 * This points to the image located in piglit_ktx_info::data. It may
+	 * be passed as the 'data' argument to glTexImage().
+	 */
+	const void *data;
+
+	/**
+	 * \brief Size of image, in bytes.
+	 *
+	 * This is 'imageSize' argument passed to glTexImage(). It does not
+	 * include any padding which may be present in ktx_info::bytes.
+	 */
+	size_t size;
+
+	/**
+	 * In range [0, num_miplevels).
+	 */
+	uint32_t miplevel;
+
+	/**
+	 * For non-array cubemap textures, `face` is in range [0, 6). For all
+	 * other textures, it is 0.
+	 */
+	uint32_t face;
+
+	/**
+	 * \name Size of image, in pixels.
+	 * \{
+	 *
+	 * These are the sizes passed to glTexImage().
+	 * Note: These sizes differ from those in the KTX header.
+	 */
+	uint32_t pixel_width;
+	uint32_t pixel_height;
+	uint32_t pixel_depth;
+	/** \} */
+};
+
+void
+piglit_ktx_destroy(struct piglit_ktx *self);
+
+/**
+ * \brief Read KTX data from a file.
+ *
+ * The file is read until EOF.
+ *
+ * Return null on error, including I/O error and invalid data.
+ */
+struct piglit_ktx*
+piglit_ktx_read_file(const char *filename);
+
+/**
+ * \brief Read KTX data from a byte array.
+ *
+ * Read at most \a size bytes.
+ *
+ * The given \a size is not used to calculate the expected length of the KTX
+ * data; that is completely determined by the content of the KTX header.
+ * Instead, the given \a size is a safeguard against reading out-of-bounds
+ * memory when incorrect data is present into the header.
+ *
+ * Return null on error, including invalid data and insufficient \a size.
+ */
+struct piglit_ktx*
+piglit_ktx_read_bytes(const void *bytes, size_t size);
+
+/**
+ * \brief Write KTX data to a file.
+ *
+ * The number of bytes written is `piglit_ktx_get_info()->size`.
+ */
+bool
+piglit_ktx_write_file(struct piglit_ktx *self, const char *filename);
+
+/**
+ * \brief Write KTX data to a byte array.
+ *
+ * The number of bytes written is `piglit_ktx_get_info()->size`.
+ */
+bool
+piglit_ktx_write_bytes(struct piglit_ktx *self, void *bytes);
+
+const struct piglit_ktx_info*
+piglit_ktx_get_info(struct piglit_ktx *self);
+
+/**
+ * \brief Get a texture image from a KTX file.
+ *
+ * The given \a miplevel must be in the range `[0,
+ * piglit_ktx_info::num_miplevels)`.  For cubemap non-array textures, \a
+ * cube_face must be in the range [0, 5].  For all other textures, \a
+ * cube_face must be 0. If the above is not satisfied, an error is produced.
+ *
+ * Note: For cubemap array textures, \a cube_face must be 0 because
+ * piglit_ktx_image::data is the data that would be passed to glTexImage3D(),
+ * which is not separated into individual faces.
+ */
+const struct piglit_ktx_image*
+piglit_ktx_get_image(struct piglit_ktx *self,
+		     int miplevel,
+		     int cube_face);
+
+/**
+ * \brief Load texture into the GL with glTexImage().
+ *
+ * If \a *tex_name is non-zero, then that texture is bound to
+ * `piglit_ktx_info::target` and the texture images are loaded into it with
+ * glTexImage().  If \a *tex_name is 0, then a new texture is first created.
+ * The new texture name is returned \a tex_name.
+ *
+ * Return false on failure. If failure is due to a GL error and \a gl_error is
+ * not null, then the value of glGetError() is returned in \a gl_error.
+ */
+bool
+piglit_ktx_load_texture(struct piglit_ktx *self,
+			GLuint *tex_name,
+			GLenum *gl_error);
+
+#ifdef __cplusplus
+}
+#endif
-- 
1.7.11.1



More information about the Piglit mailing list