Skip to content
Snippets Groups Projects
Commit 57826816 authored by Marc-Oliver Westerburg's avatar Marc-Oliver Westerburg Committed by Jenkins
Browse files

LTP tests:Added OpenGL Bouncing-Ball display test

* Merge from jethro
** linux-mx:r8393: added dummy test-app @bball@; currently just a
   modified copy of mesa-demos's @glxdemo@
** linux-mx:r8396: updated previous "dummy" @bball@ test with a static
   display using proper background and ball via OpenGL
   No animation, yet, and code still requires clean-up
** linux-mx:r8397:
*** added basic ball animation
*** added configurable frame-rate limit for smoother animation
*** added configurable ball size
*** moved static code out of main rendering loop
*** using (client) attribute array, now, instead of single attribute calls
*** Color animation, as in reference videos, is still missing
** linux-mx:r8398: added 16x9 background image (so far only 4x3 version was included)
** linux-mx:r8399: scaled background image for maximum 1920 horizontal resolution
** linux-mx:r8403
*** replaced GL_POINT-rendering for ball with GL_TRIANGLE_FAN, because GL_POINT for some reason ignored color values
*** added color-cycling of the ball color similar to the reference videos
*** some code clean-up
** linux-mx:r8404: some code clean-up
** linux-mx:r8406: Some code clean-up and performance optimization.
      In fullscreen mode on i.MX6Solo with WVGA-display now requires
*** <=10.5% CPU-load in unrestricted mode; reaching > 120 fps
*** <=3.9% CPU-load with default restriction of 40fps
** linux-mx:r8407:
    (Hopefully) fixed stability issues on start esp. on multi-core CPUs.
    Issue apparently was a synchronization problem in the setup-code between X Window initialization and OpenGL context initialization.
    Using @XFlush()@ and @XSync()@ now to ensure that X Windows initialization is finished before initializing OpenGL.
** linux-mx:r8412: ltp-tests, Bouncing-Ball display test, X11 deps
*** The bball make non X11 targets fail as x11 is needed here
*** To solve this the ltp Makefile now has two targets: all_tests and all_tests_x11
*** The receipe now calls those targets dependent on the distro features
** linux-mx:8414: most recent changes to build @bball@ conditionally only if X11
    is enabled had a syntax error in its if-clause causing @bball@ to never build. fixed.
** linux-mx:r8420: LTP guf-tests @bball@ app textures
*** replaced previous textures with new ones scaled for optimal performance
**** previously the textures were rectangluar and used non-power-of-two resulotions in both dimensions
**** while even the low-end Vivante GPUs do support this, performance is significantly
     lower (and memory bandwidth for texture look-up) significantly higher, than for
     optimized resolutions.
**** now the textures have been scaled to 1024x1024, which results in much better performance
     with lower memory bandwidth
**** This fixes issues with the GPU blocking the RAM-bus for the IPU display-refresh during
     temperature throttling.

* Fixed include path in rocko, needed because of changed ltp base version
* Fixed minor build errors and warnings

Change-Id: I2f068c7a44f52f8fe1cbb522afa6223dcbb54610
Reviewed-on: http://gfweb/gerrit/489
parent d7718839
No related branches found
No related tags found
No related merge requests found
File added
File added
SUBDIRECTORIES = aio ambprox backlight bit bwrite can flashled fwrite gpio i2c iptest lm73temp mmc mstick qwheel rs485pingpong rtccheck serial touchmon v4l2capture watchdog timerapi spi
.PHONY: subdirs $(SUBDIRECTORIES)
SUBDIRECTORIES_X11 = bball
.PHONY: subdirs $(SUBDIRECTORIES) $(SUBDIRECTORIES_X11)
subdirs: $(SUBDIRECTORIES)
$(SUBDIRECTORIES):
all_tests: $(SUBDIRECTORIES)
all_tests_x11: $(SUBDIRECTORIES_X11)
$(SUBDIRECTORIES) $(SUBDIRECTORIES_X11):
$(MAKE) -C $@ all
##########################################################################
## ##
## Copyright (C) 2010 Garz & Fricke GmbH ##
## ##
## No use or disclosure of this information in any form without ##
## the written permission of the author ##
## ##
##########################################################################
TARGET=bball
top_srcdir ?= $(SYSROOT)/usr
CFLAGS+= -I$(top_srcdir)/include -Wall -Werror
LOADLIBS+= -L$(top_srcdir)/lib -lltp -lGL -lX11 -lXext -lm
SRCS=$(TARGET).c tga_reader.c
all: $(TARGET)
$(TARGET): $(SRCS)
@mkdir -p ../bin
@$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LOADLIBS) -o ../bin/$@
clean:
rm -f $(TARGET) *.o *.bak *~
\ No newline at end of file
/******************************************************************************/
/* */
/* File: bball.c */
/* */
/* Description: This test shows a test-image and and animated ball on screen */
/* */
/* Total Tests: 1 */
/* */
/* Test Name: bball */
/* */
/* Test Assertion */
/* & Strategy: No "real tests" are performed. The intention of this test is */
/* only to cause "load" on the display so that EMC and be testes */
/* */
/* Author: Marc-Oliver Westerburg <westerburg@garz-fricke.com> */
/* */
/******************************************************************************/
/*
* A demonstration of using the GLX functions. This program is in the
* public domain.
*
* Brian Paul
*/
#include <GL/gl.h>
#include <GL/glx.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "tga_reader.h"
/* Harness Specific Include Files. */
#include "old/test.h"
#include "config.h"
#include <sys/time.h>
#include <unistd.h>
/* return current time (in seconds) */
static double
current_time(void)
{
struct timeval tv;
#ifdef __VMS
(void) gettimeofday(&tv, NULL );
#else
struct timezone tz;
(void) gettimeofday(&tv, &tz);
#endif
return (double) tv.tv_sec + tv.tv_usec / 1000000.0;
}
/* Local Defines */
#if !defined(TRUE) && !defined(FALSE)
#define TRUE 1
#define FALSE 0
#endif
#if LTP_VERSION >= 20140115
#define Tst_count tst_count
#endif
#ifndef PI
#define PI 3.141592653589793238
#endif
/* Extern Global Variables */
extern int Tst_count; /* counter for tst_xxx routines. */
extern char *TESTDIR; /* temporary dir created by tst_tmpdir() */
/* Global Variables */
char * TCID = "bball "; /* test program identifier. */
int TST_TOTAL = 1; /* total number of tests in this file. */
char * conf_filename = "/opt/ltp/images/itu-r-bt1729-colorbar-4x3.tga"; /* default name of background-image */
int conf_fullscreen = 0; /* render in fullscreen if != 0 */
int conf_verbose = 0;
double conf_limit = 0.025; /* limit frame-rate; <0.0 : off */
double conf_size = 20.0; /* ball size */
int conf_ballsegs = 12; /* number of ball segments */
// vector and texture coordinates for background
static float bkgr_vcoords[8] = {-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0 };
static float bkgr_tcoords[8] = { 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0 };
GLuint texture = 0;
// vector coordinates for bouncing ball (generated during setup())
static float *ball_vcoords = NULL;
// starting point for bouncing ball
static float pX = -0.8;
static float pY = 0.5;
// starting velocity vector for bouncing ball
static float vX = 0.6;
static float vY = 0.4;
// handle for X and OpenGL window and context creation
static Display *dpy = NULL;
static Window win = 0;
static GLXContext ctx = NULL;
static XVisualInfo *visinfo = NULL;
/* Extern Global Functions */
/******************************************************************************/
/* */
/* Function: cleanup */
/* */
/* Description: Performs all one time clean up for this test on successful */
/* completion, premature exit or failure. Closes all temporary */
/* files, removes all temporary directories exits the test with */
/* appropriate return code by calling tst_exit() function. */
/* */
/* Input: None. */
/* */
/* Output: None. */
/* */
/* Return: On failure - Exits calling tst_exit(). Non '0' return code. */
/* On success - Exits calling tst_exit(). With '0' return code. */
/* */
/******************************************************************************/
void cleanup()
{
if (ball_vcoords)
free(ball_vcoords);
if (dpy) {
if (ctx)
glXDestroyContext(dpy, ctx);
if (visinfo)
XFree(visinfo);
if (win)
XDestroyWindow(dpy, win);
XSync(dpy,1);
XCloseDisplay(dpy);
}
tst_exit();
}
/* Local Functions */
/**
* Remove window border/decorations.
*/
static void
no_border( Display *dpy, Window w)
{
static const unsigned MWM_HINTS_DECORATIONS = (1 << 1);
static const int PROP_MOTIF_WM_HINTS_ELEMENTS = 5;
typedef struct
{
unsigned long flags;
unsigned long functions;
unsigned long decorations;
long inputMode;
unsigned long status;
} PropMotifWmHints;
PropMotifWmHints motif_hints;
Atom prop, proptype;
unsigned long flags = 0;
/* setup the property */
motif_hints.flags = MWM_HINTS_DECORATIONS;
motif_hints.decorations = flags;
/* get the atom for the property */
prop = XInternAtom( dpy, "_MOTIF_WM_HINTS", True );
if (!prop) {
/* something went wrong! */
return;
}
/* not sure this is correct, seems to work, XA_WM_HINTS didn't work */
proptype = prop;
XChangeProperty( dpy, w, /* display, window */
prop, proptype, /* property, type */
32, /* format: 32-bit datums */
PropModeReplace, /* mode */
(unsigned char *) &motif_hints, /* data */
PROP_MOTIF_WM_HINTS_ELEMENTS /* nelements */
);
}
/******************************************************************************/
/* */
/* Function: setup */
/* */
/* Description: Performs all one time setup for this test. This function is */
/* typically used to capture signals, create temporary dirs */
/* and temporary files that may be used in the course of this */
/* test. */
/* */
/* Input: None */
/* */
/* Output: None. */
/* */
/* Return: On failure - Exits by calling cleanup(). */
/* On success - Nothing. */
/* */
/******************************************************************************/
void setup()
{
int scrnum;
XSetWindowAttributes attr;
XWindowAttributes getWinAttr;
unsigned long mask;
// allocate space for ball vector coordinates
ball_vcoords = (float *)calloc(sizeof(float), (conf_ballsegs+2) * 2);
if (!ball_vcoords) {
tst_brkm(TBROK, cleanup,
"Unable to allocate memory for ball vectors.");
}
// open connection to X-Server
dpy = XOpenDisplay(NULL);
scrnum = DefaultScreen( dpy );
Window root = RootWindow( dpy, scrnum );
XGetWindowAttributes(dpy, root, &getWinAttr);
// select X visual for OpenGL
int attrib[] = { GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_DOUBLEBUFFER,
None };
visinfo = glXChooseVisual( dpy, scrnum, attrib );
if (!visinfo) {
tst_brkm(TBROK, cleanup,
"Unable to obtain RGB, double-buffered X-Visual.");
}
// prepare X windows attributes
attr.background_pixel = 0;
attr.border_pixel = 0;
attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
attr.event_mask = StructureNotifyMask | ExposureMask;
mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
// create X window
win = XCreateWindow( dpy, root, 0, 0, getWinAttr.width, getWinAttr.height,
0, visinfo->depth, InputOutput,
visinfo->visual, mask, &attr );
if (conf_fullscreen)
no_border(dpy, win);
// update X windows name
XStoreName(dpy, win, TCID );
// show X windows on screen
XMapWindow( dpy, win );
XFlush(dpy);
XSync(dpy, False);
// create OpenGL context
ctx = glXCreateContext( dpy, visinfo, NULL, True );
if (!ctx) {
tst_brkm(TBROK, cleanup,
"glXCreateContext failed.");
}
glXMakeCurrent( dpy, win, ctx );
// initialize some global OpenGL parameters, which are
// not going to change during runtime
glShadeModel( GL_FLAT );
glClearColor( 0.5, 0.5, 0.5, 1.0 );
// generate ball vertices
int i;
double r;
for (i = 0, r = 0.0f; i <= conf_ballsegs ; r += (2*PI/conf_ballsegs), i++) {
ball_vcoords[(1+i)*2 ] = (float)(sin(r) / getWinAttr.width);
ball_vcoords[(1+i)*2+1] = (float)(cos(r) / getWinAttr.height);
}
if (conf_verbose) {
tst_resm(TINFO, "Ball vector coordinates:");
for (i = 0; i < conf_ballsegs+2; i++)
tst_resm(TINFO, "%u : %f %f", i, ball_vcoords[i*2], ball_vcoords[i*2+1]);
}
// load textures from external file into OpenGL context
FILE *file = fopen(conf_filename, "rb");
if(file) {
fseek(file, 0, SEEK_END);
int size = ftell(file);
fseek(file, 0, SEEK_SET);
unsigned char *buffer = (unsigned char *)tgaMalloc(size);
fread(buffer, 1, size, file);
fclose(file);
int width = tgaGetWidth(buffer);
int height = tgaGetHeight(buffer);
int *pixels = tgaRead(buffer, TGA_READER_ABGR);
tgaFree(buffer);
glGenTextures(1, &texture);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
tgaFree(pixels);
}
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer(2, GL_FLOAT, 0, bkgr_tcoords);
}
static void redraw( Display *dpy, Window w )
{
static unsigned color = 0xff0000;
static int frames = 0;
static double rStart0 = -1.0, tRate0 = -1.0;
double dt, t = current_time();
if (rStart0 < 0.0)
rStart0 = t;
dt = t - rStart0;
rStart0 = t;
// move point for next frame
pX += dt * vX;
pY += dt * vY;
if (pX > 1.0) {
pX = 1.0; vX *= -1.0;
}
if (pX < -1.0) {
pX = -1.0; vX *= -1.0;
}
if (pY > 1.0) {
pY = 1.0; vY *= -1.0;
}
if (pY < -1.0) {
pY = -1.0; vY *= -1.0;
}
// Don't bother clearing the color buffer, because
// we're going to overwrite the whole buffer anyway.
// start with identity matrix for GL_MODELVIEW
glLoadIdentity();
// draw background
glVertexPointer(2, GL_FLOAT, 0, bkgr_vcoords);
glEnable(GL_TEXTURE_2D);
glColor3f( 1.0, 1.0, 0.0 );
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
glDisable(GL_TEXTURE_2D);
// draw ball
glVertexPointer(2, GL_FLOAT, 0, ball_vcoords);
glTranslatef(pX, pY, 0.0);
glScalef(conf_size, conf_size, 0.0);
glColor3ub(((color >> 16) & 0xff), ((color >> 8) & 0xff), ((color >> 0) & 0xff));
color += 0x010203;
glDrawArrays( GL_TRIANGLE_FAN, 0, conf_ballsegs+2 );
// submit buffer
glXSwapBuffers( dpy, w );
// calculate frame-rate
frames++;
if (tRate0 < 0.0)
tRate0 = t;
if (t - tRate0 >= 2.0) {
char str[100];
GLfloat seconds = t - tRate0;
GLfloat fps = frames / seconds;
snprintf(str, 99, "%d frames in %3.1f seconds = %6.3f FPS", frames, seconds,
fps);
XStoreName(dpy, win, str );
if (conf_verbose)
tst_resm(TINFO, "%s", str);
fflush(stdout);
tRate0 = t;
frames = 0;
}
// limit frame-rate
if (conf_limit > 0.0f) {
double tend = current_time();
double rTime = conf_limit - (tend - t);
if (rTime > 0.0)
usleep( rTime * 1000000.0 );
}
}
static void resize( unsigned int width, unsigned int height )
{
glViewport( 0, 0, width, height );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho( -1.0, 1.0, -1.0, 1.0, -1.0, 1.0 );
glMatrixMode( GL_MODELVIEW );
}
static void event_loop( Display *dpy )
{
while (1) {
XEvent event;
while (XPending(dpy) > 0) {
XNextEvent( dpy, &event );
switch (event.type) {
case Expose:
break;
case ConfigureNotify:
resize( event.xconfigure.width, event.xconfigure.height );
break;
}
}
redraw( dpy, event.xany.window );
}
}
/******************************************************************************/
/* */
/* Function: help */
/* */
/* Description: This function is called when the test is started with */
/* parameter -h. It displays all parameter options specifically */
/* available for this test. */
/* */
/* Input: None. */
/* */
/* Output: None. */
/* */
/* Return: Nothing. */
/* */
/******************************************************************************/
void help()
{
printf(" -b s Use (TGA) file <s> as background (default: %s)\n", conf_filename);
printf(" -l t Limit frame-rate to <t> seconds (default: %f, <=0.0: limit off)\n", conf_limit);
printf(" -s f Set ball size to <f> pixels (default: %f)\n", conf_size);
printf(" -e n Set number of ball segments to <n> (default: %u)\n", conf_ballsegs);
printf(" -f Render in fullscreen-mode\n");
printf(" -v Verbose output on console\n");
}
/******************************************************************************/
/* */
/* Function: main */
/* */
/* Description: Entry point to this test-case. It parses all the command line */
/* inputs, calls the global setup and executes the test. It logs */
/* the test status and results appropriately using the LTP API's */
/* On successful completion or premature failure, cleanup() func */
/* is called and test exits with an appropriate return code. */
/* */
/* Input: Command line parameters (see help()) */
/* */
/* Exit: On failure - Exits by calling cleanup(). */
/* On success - exits with 0 exit value. */
/* */
/******************************************************************************/
int main(int argc, char **argv)
{
int opt_f = 0, opt_b = 0, opt_v = 0, opt_l = 0, opt_s = 0, opt_e = 0;
char *str_b, *str_l, *str_s, *str_e;
option_t options[] = {
{"b:", &opt_b, &str_b},
{"l:", &opt_l, &str_l},
{"s:", &opt_s, &str_s},
{"e:", &opt_e, &str_e},
{"f", &opt_f, NULL},
{"v", &opt_v, NULL},
{NULL, NULL, NULL}
};
/* parse options */
#if LTP_VERSION > 20140115
tst_parse_opts(argc, argv, options, &help);
#else
char *msg;
if((msg = parse_opts(argc, argv, options, &help)) != NULL)
tst_brkm(TBROK, cleanup, "OPTION PARSING ERROR - %s", msg);
#endif
if (opt_l) {
if (sscanf(str_l, "%lf", &conf_limit) != 1)
tst_resm(TWARN,
"double number for frame-rate limit expected. Using default %f now.", conf_limit);
}
if (opt_s) {
if (sscanf(str_s, "%lf", &conf_size) != 1)
tst_resm(TWARN,
"double number for ball size expected. Using default %f now.", conf_size);
}
if (opt_e) {
if (sscanf(str_e, "%u", &conf_ballsegs) != 1)
tst_resm(TWARN,
"unsigned number for ball segments expected. Using default %u now.", conf_ballsegs);
}
if (conf_ballsegs < 4) {
tst_resm(TWARN,
"number of ball-segments %u to small. Using default %u now.", conf_ballsegs, 4);
conf_ballsegs = 4;
}
if (opt_b)
conf_filename = str_b;
if (opt_f)
conf_fullscreen = 1;
if (opt_v){
conf_verbose = 1;
}
tst_resm(TINFO, "Starting bball test.");
setup();
event_loop( dpy );
cleanup();
return 0;
}
/**
* tga_reader.c
*
* Copyright (c) 2014 Kenji Sasaki
* Released under the MIT license.
* https://github.com/npedotnet/TGAReader/blob/master/LICENSE
*
* English document
* https://github.com/npedotnet/TGAReader/blob/master/README.md
*
* Japanese document
* http://3dtech.jp/wiki/index.php?TGAReader
*
*/
#include "tga_reader.h"
static const TGA_ORDER _TGA_READER_ARGB = {16, 8, 0, 24};
const TGA_ORDER *TGA_READER_ARGB = &_TGA_READER_ARGB;
static const TGA_ORDER _TGA_READER_ABGR = {0, 8, 16, 24};
const TGA_ORDER *TGA_READER_ABGR = &_TGA_READER_ABGR;
void *tgaMalloc(size_t size) {
return malloc(size);
}
void tgaFree(void *memory) {
free(memory);
}
int tgaGetWidth(const unsigned char *buffer) {
return (buffer[12] & 0xFF) | (buffer[13] & 0xFF) << 8;
}
int tgaGetHeight(const unsigned char *buffer) {
return (buffer[14] & 0xFF) | (buffer[15] & 0xFF) << 8;
}
#define COLORMAP 1
#define RGB 2
#define GRAYSCALE 3
#define COLORMAP_RLE 9
#define RGB_RLE 10
#define GRAYSCALE_RLE 11
#define RIGHT_ORIGIN 0x10
#define UPPER_ORIGIN 0x20
static unsigned char *decodeRLE(int width, int height, int depth, const unsigned char *buffer, int offset);
static int *createPixelsFromColormap(int width, int height, int depth, const unsigned char *bytes, int offset, const unsigned char *palette, int colormapOrigin, int descriptor, const TGA_ORDER *order);
static int *createPixelsFromRGB(int width, int height, int depth, const unsigned char *bytes, int offset, int descriptor, const TGA_ORDER *order);
static int *createPixelsFromGrayscale(int width, int height, int depth, const unsigned char *bytes, int offset, int descriptor, const TGA_ORDER *order);
int *tgaRead(const unsigned char *buffer, const TGA_ORDER *order) {
// header
// int idFieldLength = buffer[0] & 0xFF;
// int colormapType = buffer[1] & 0xFF;
int type = buffer[2] & 0xFF;
int colormapOrigin = (buffer[3] & 0xFF) | (buffer[4] & 0xFF) << 8;
int colormapLength = (buffer[5] & 0xFF) | (buffer[6] & 0xFF) << 8;
int colormapDepth = buffer[7] & 0xFF;
// int originX = (buffer[8] & 0xFF) | (buffer[9] & 0xFF) << 8; // unsupported
// int originY = (buffer[10] & 0xFF) | (buffer[11] & 0xFF) << 8; // unsupported
int width = tgaGetWidth(buffer);
int height = tgaGetHeight(buffer);
int depth = buffer[16] & 0xFF;
int descriptor = buffer[17] & 0xFF;
int *pixels = NULL;
// data
switch(type) {
case COLORMAP: {
int imageDataOffset = 18 + (colormapDepth / 8) * colormapLength;
pixels = createPixelsFromColormap(width, height, colormapDepth, buffer, imageDataOffset, buffer, colormapOrigin, descriptor, order);
} break;
case RGB:
pixels = createPixelsFromRGB(width, height, depth, buffer, 18, descriptor, order);
break;
case GRAYSCALE:
pixels = createPixelsFromGrayscale(width, height, depth, buffer, 18, descriptor, order);
break;
case COLORMAP_RLE: {
int imageDataOffset = 18 + (colormapDepth / 8) * colormapLength;
unsigned char *decodeBuffer = decodeRLE(width, height, depth, buffer, imageDataOffset);
pixels = createPixelsFromColormap(width, height, colormapDepth, decodeBuffer, 0, buffer, colormapOrigin, descriptor, order);
tgaFree(decodeBuffer);
} break;
case RGB_RLE: {
unsigned char *decodeBuffer = decodeRLE(width, height, depth, buffer, 18);
pixels = createPixelsFromRGB(width, height, depth, decodeBuffer, 0, descriptor, order);
tgaFree(decodeBuffer);
} break;
case GRAYSCALE_RLE: {
unsigned char *decodeBuffer = decodeRLE(width, height, depth, buffer, 18);
pixels = createPixelsFromGrayscale(width, height, depth, decodeBuffer, 0, descriptor, order);
tgaFree(decodeBuffer);
} break;
default:
break;
}
return pixels;
}
static unsigned char *decodeRLE(int width, int height, int depth, const unsigned char *buffer, int offset) {
int elementCount = depth/8;
unsigned char elements[4];
int decodeBufferLength = elementCount * width * height;
unsigned char *decodeBuffer = (unsigned char *)tgaMalloc(decodeBufferLength);
int decoded = 0;
while(decoded < decodeBufferLength) {
int packet = buffer[offset++] & 0xFF;
if((packet & 0x80) != 0) { // RLE
int i, j, count;
for(i=0; i<elementCount; i++) {
elements[i] = buffer[offset++];
}
count = (packet&0x7F)+1;
for(i=0; i<count; i++) {
for(j=0; j<elementCount; j++) {
decodeBuffer[decoded++] = elements[j];
}
}
}
else { // RAW
int count = (packet+1) * elementCount;
int i;
for(i=0; i<count; i++) {
decodeBuffer[decoded++] = buffer[offset++];
}
}
}
return decodeBuffer;
}
static int *createPixelsFromColormap(int width, int height, int depth, const unsigned char *bytes, int offset, const unsigned char *palette, int colormapOrigin, int descriptor, const TGA_ORDER *order) {
int *pixels = NULL;
int rs = order->redShift;
int gs = order->greenShift;
int bs = order->blueShift;
int as = order->alphaShift;
switch(depth) {
case 24:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = (bytes[offset+width*i+j] & 0xFF) - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 3*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*i+(width-j-1)] = color;
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = (bytes[offset+width*i+j] & 0xFF) - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 3*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*(height-i-1)+(width-j-1)] = color;
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = (bytes[offset+width*i+j] & 0xFF) - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 3*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*i+j] = color;
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = (bytes[offset+width*i+j] & 0xFF) - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 3*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*(height-i-1)+j] = color;
}
}
}
}
break;
case 32:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = (bytes[offset+width*i+j] & 0xFF) - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 4*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = palette[index+3] & 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*i+(width-j-1)] = color;
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = (bytes[offset+width*i+j] & 0xFF) - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 4*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = palette[index+3] & 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*(height-i-1)+(width-j-1)] = color;
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = (bytes[offset+width*i+j] & 0xFF) - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 4*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = palette[index+3] & 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*i+j] = color;
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int colormapIndex = (bytes[offset+width*i+j] & 0xFF) - colormapOrigin;
int color = 0xFFFFFFFF;
if(colormapIndex >= 0) {
int index = 4*colormapIndex+18;
int b = palette[index+0] & 0xFF;
int g = palette[index+1] & 0xFF;
int r = palette[index+2] & 0xFF;
int a = palette[index+3] & 0xFF;
color = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
pixels[width*(height-i-1)+j] = color;
}
}
}
}
break;
default:
break;
}
return pixels;
}
static int *createPixelsFromRGB(int width, int height, int depth, const unsigned char *bytes, int offset, int descriptor, const TGA_ORDER *order) {
int *pixels = NULL;
int rs = order->redShift;
int gs = order->greenShift;
int bs = order->blueShift;
int as = order->alphaShift;
switch(depth) {
case 24:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+3*width*i+3*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = 0xFF;
pixels[width*i+(width-j-1)] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+3*width*i+3*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = 0xFF;
pixels[width*(height-i-1)+(width-j-1)] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+3*width*i+3*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = 0xFF;
pixels[width*i+j] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+3*width*i+3*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = 0xFF;
pixels[width*(height-i-1)+j] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
}
break;
case 32:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+4*width*i+4*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = bytes[index+3] & 0xFF;
pixels[width*i+(width-j-1)] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+4*width*i+4*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = bytes[index+3] & 0xFF;
pixels[width*(height-i-1)+(width-j-1)] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+4*width*i+4*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = bytes[index+3] & 0xFF;
pixels[width*i+j] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int index = offset+4*width*i+4*j;
int b = bytes[index+0] & 0xFF;
int g = bytes[index+1] & 0xFF;
int r = bytes[index+2] & 0xFF;
int a = bytes[index+3] & 0xFF;
pixels[width*(height-i-1)+j] = (r<<rs) | (g<<gs) | (b<<bs) | (a<<as);
}
}
}
}
break;
default:
break;
}
return pixels;
}
static int *createPixelsFromGrayscale(int width, int height, int depth, const unsigned char *bytes, int offset, int descriptor, const TGA_ORDER *order) {
int *pixels = NULL;
int rs = order->redShift;
int gs = order->greenShift;
int bs = order->blueShift;
int as = order->alphaShift;
switch(depth) {
case 8:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+width*i+j] & 0xFF;
int a = 0xFF;
pixels[width*i+(width-j-1)] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+width*i+j] & 0xFF;
int a = 0xFF;
pixels[width*(height-i-1)+(width-j-1)] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+width*i+j] & 0xFF;
int a = 0xFF;
pixels[width*i+j] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+width*i+j] & 0xFF;
int a = 0xFF;
pixels[width*(height-i-1)+j] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
}
break;
case 16:
pixels = (int *)tgaMalloc(4*width*height);
if((descriptor & RIGHT_ORIGIN) != 0) {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+2*width*i+2*j+0] & 0xFF;
int a = bytes[offset+2*width*i+2*j+1] & 0xFF;
pixels[width*i+(width-j-1)] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
else {
// LowerRight
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+2*width*i+2*j+0] & 0xFF;
int a = bytes[offset+2*width*i+2*j+1] & 0xFF;
pixels[width*(height-i-1)+(width-j-1)] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
}
else {
if((descriptor & UPPER_ORIGIN) != 0) {
// UpperLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+2*width*i+2*j+0] & 0xFF;
int a = bytes[offset+2*width*i+2*j+1] & 0xFF;
pixels[width*i+j] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
else {
// LowerLeft
int i, j;
for(i=0; i<height; i++) {
for(j=0; j<width; j++) {
int e = bytes[offset+2*width*i+2*j+0] & 0xFF;
int a = bytes[offset+2*width*i+2*j+1] & 0xFF;
pixels[width*(height-i-1)+j] = (e<<rs) | (e<<gs) | (e<<bs) | (a<<as);
}
}
}
}
break;
default:
break;
}
return pixels;
}
/* EOF */
/**
* tga_reader.h
*
* Copyright (c) 2014 Kenji Sasaki
* Released under the MIT license.
* https://github.com/npedotnet/TGAReader/blob/master/LICENSE
*
* English document
* https://github.com/npedotnet/TGAReader/blob/master/README.md
*
* Japanese document
* http://3dtech.jp/wiki/index.php?TGAReader
*
*/
#ifndef __TGA_READER_H__
#define __TGA_READER_H__
#include <stdio.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _TGA_ORDER {
int redShift;
int greenShift;
int blueShift;
int alphaShift;
} TGA_ORDER;
const TGA_ORDER *TGA_READER_ARGB;
const TGA_ORDER *TGA_READER_ABGR;
void *tgaMalloc(size_t size);
void tgaFree(void *memory);
int tgaGetWidth(const unsigned char *buffer);
int tgaGetHeight(const unsigned char *buffer);
int *tgaRead(const unsigned char *buffer, const TGA_ORDER *order);
#ifdef __cplusplus
}
#endif
#endif /* __TGA_READER_H__ */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment