/* BlinkenLib
 * version 0.5.2 date 2006-05-10
 * Copyright 2004-2006 Stefan Schuermans <1stein@schuermans.info>
 * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html
 * a blinkenarea.org project
 */
#include "globals.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//#include "netinet/in.h"
#include "BlinkenConstants.h"
#include "BlinkenFrame.h"
#include "Tools.h"

Uint16 bswap_16 (Uint16 bsx) {
	return BSWAP_CONSTANT_16(bsx);
}
Uint32 bswap_32 (Uint32 bsx) {
	return BSWAP_CONSTANT_32 (bsx);
}

struct sBlinkenFrame {
	Uint32 height;
	Uint32 width;
	Uint32 channels;
	Uint32 maxval;
	Uint32 duration;
	unsigned char * * ppData;
};

//blinken protocol headers
typedef struct sBlinkenProtoBlpHdr {
	Uint32 magic;
	Uint32 frameNo;
	Uint16 width;
	Uint16 height;
} stBlinkenProtoBlpHdr;
#define BlinkenProtoBlpMagic 0xDEADBEEF

typedef struct sBlinkenProtoEblpHdr {
	Uint32 magic;
	Uint32 frameNo;
	Uint16 width;
	Uint16 height;
} stBlinkenProtoEblpHdr;
#define BlinkenProtoEblpMagic 0xFEEDBEEF

typedef struct sBlinkenProtoMcufHdr {
	Uint32 magic;
	Uint16 height;
	Uint16 width;
	Uint16 channels;
	Uint16 maxval;
} stBlinkenProtoMcufHdr;
#define BlinkenProtoMcufMagic 0x23542666

stBlinkenFrame * BlinkenFrameNew( Uint32 height, Uint32 width, Uint32 channels, Uint32 maxval, Uint32 duration ) {
	stBlinkenFrame * pFrame;

	if( height < BlinkenHeightMin ) height = BlinkenHeightMin;
	if( height > BlinkenHeightMax ) height = BlinkenHeightMax;
	if( width < BlinkenWidthMin ) width = BlinkenWidthMin;
	if( width > BlinkenWidthMax ) width = BlinkenWidthMax;
	if( channels < BlinkenChannelsMin ) channels = BlinkenChannelsMin;
	if( channels > BlinkenChannelsMax ) channels = BlinkenMaxvalMax;
	if( maxval < BlinkenMaxvalMin ) maxval = BlinkenMaxvalMin;
	if( maxval > BlinkenMaxvalMax ) maxval = BlinkenMaxvalMax;
	if( duration < BlinkenDurationMin ) duration = BlinkenDurationMin;
	if( duration > BlinkenDurationMax ) duration = BlinkenDurationMax;

	pFrame = (stBlinkenFrame *)malloc( sizeof( stBlinkenFrame ) );
	if( pFrame == NULL )
		return NULL;

	pFrame->height = height;
	pFrame->width = width;
	pFrame->channels = channels;
	pFrame->maxval = maxval;
	pFrame->duration = duration;

	pFrame->ppData = (unsigned char * *)malloc2D( height, width * channels, sizeof( unsigned char ) );
	if( pFrame->ppData == NULL ) {
		free( pFrame );
		return NULL;
	}

	return pFrame;
}

stBlinkenFrame * BlinkenFrameClone( stBlinkenFrame * pSrcFrame ) {
	Uint32 y, x, c, i;
	stBlinkenFrame * pFrame;

	if( pSrcFrame == NULL )
		return NULL;

	pFrame = BlinkenFrameNew( pSrcFrame->height, pSrcFrame->width,
														pSrcFrame->channels, pSrcFrame->maxval, pSrcFrame->duration );
	if( pFrame == NULL )
		return NULL;

	for( y = 0; y < pFrame->height; y++ )
		for( x = 0, i = 0; x < pFrame->width; x++ )
			for( c = 0; c < pFrame->channels; c++, i++ )
				pFrame->ppData[y][i] = pSrcFrame->ppData[y][i];

	return pFrame;
}

void BlinkenFrameFree( stBlinkenFrame * pFrame ) {
	if( pFrame == NULL )
		return;

	free( pFrame->ppData );
	free( pFrame );
}

void BlinkenFrameClear( stBlinkenFrame * pFrame ) {
	Uint32 y, x, c, i;

	if( pFrame == NULL )
		return;

	for( y = 0; y < pFrame->height; y++ )
		for( x = 0, i = 0; x < pFrame->width; x++ )
			for( c = 0; c < pFrame->channels; c++, i++ )
				pFrame->ppData[y][i] = 0;
}

Uint32 BlinkenFrameGetHeight( stBlinkenFrame * pFrame ) {
	if( pFrame == NULL )
		return 0;

	return pFrame->height;
}

Uint32 BlinkenFrameGetWidth( stBlinkenFrame * pFrame ) {
	if( pFrame == NULL )
		return 0;

	return pFrame->width;
}

Uint32 BlinkenFrameGetChannels( stBlinkenFrame * pFrame ) {
	if( pFrame == NULL )
		return 0;

	return pFrame->channels;
}

Uint32 BlinkenFrameGetMaxval( stBlinkenFrame * pFrame ) {
	if( pFrame == NULL )
		return 0;

	return pFrame->maxval;
}

Uint32 BlinkenFrameGetDuration( stBlinkenFrame * pFrame ) {
	if( pFrame == NULL )
		return 0;

	return pFrame->duration;
}

void BlinkenFrameSetDuration( stBlinkenFrame * pFrame, Uint32 duration ) {
	if( pFrame == NULL )
		return;

	if( duration < BlinkenDurationMin ) duration = BlinkenDurationMin;
	if( duration > BlinkenDurationMax ) duration = BlinkenDurationMax;

	pFrame->duration = duration;
}

unsigned char BlinkenFrameGetPixel( stBlinkenFrame * pFrame, Uint32 y, Uint32 x, Uint32 c ) {
	if( pFrame == NULL ||
	    y >= pFrame->height ||
	    x >= pFrame->width ||
	    c >= pFrame->channels )
		return 0;

	return pFrame->ppData[y][x * pFrame->channels + c];
}

void BlinkenFrameSetPixel( stBlinkenFrame * pFrame, Uint32 y, Uint32 x, Uint32 c, unsigned char val ) {
	if( pFrame == NULL ||
			y >= pFrame->height ||
			x >= pFrame->width ||
			c >= pFrame->channels )
		return;

	if( val > pFrame->maxval )
		val = pFrame->maxval;
	pFrame->ppData[y][x * pFrame->channels + c] = val;
}

unsigned long BlinkenFrameGetColor( stBlinkenFrame * pFrame, Uint32 y, Uint32 x ) {
	Uint32 i;

	if( pFrame == NULL ||
			y >= pFrame->height ||
			x >= pFrame->width )
		return 0;

	i = x * pFrame->channels;

	if( pFrame->channels == 1 )
		return (((unsigned long)pFrame->ppData[y][i + 0] * 255 + pFrame->maxval / 2) / pFrame->maxval) << 16 |
				(((unsigned long)pFrame->ppData[y][i + 0] * 255 + pFrame->maxval / 2) / pFrame->maxval) << 8 |
				(((unsigned long)pFrame->ppData[y][i + 0] * 255 + pFrame->maxval / 2) / pFrame->maxval);
	if( pFrame->channels == 2 )
		return (((unsigned long)pFrame->ppData[y][i + 0] * 255 + pFrame->maxval / 2) / pFrame->maxval) << 16 |
				(((unsigned long)pFrame->ppData[y][i + 1] * 255 + pFrame->maxval / 2) / pFrame->maxval) << 8;
	return (((unsigned long)pFrame->ppData[y][i + 0] * 255 + pFrame->maxval / 2) / pFrame->maxval) << 16 |
			(((unsigned long)pFrame->ppData[y][i + 1] * 255 + pFrame->maxval / 2) / pFrame->maxval) << 8 |
			(((unsigned long)pFrame->ppData[y][i + 2] * 255 + pFrame->maxval / 2) / pFrame->maxval);
}

void BlinkenFrameSetColor( stBlinkenFrame * pFrame, Uint32 y, Uint32 x, unsigned long color ) {
	Uint32 i, alpha, alpha_, c;

	if( pFrame == NULL ||
			 y >= pFrame->height ||
			 x >= pFrame->width )
		return;

	i = x * pFrame->channels;

	alpha = (color >> 24) & 0xFF;
	alpha_ = 255 - alpha;
	if( pFrame->channels >= 1 )
		pFrame->ppData[y][i + 0] = (unsigned char)(( ((((color >> 16) & 0xFF) * pFrame->maxval + 127) / 255) * alpha
				+ (unsigned long)pFrame->ppData[y][i + 0] * alpha_
																							 ) / 255);
	if( pFrame->channels >= 2 )
		pFrame->ppData[y][i + 1] = (unsigned char)(( ((((color >> 8) & 0xFF) * pFrame->maxval + 127) / 255) * alpha
				+ (unsigned long)pFrame->ppData[y][i + 1] * alpha_
																							 ) / 255);
	if( pFrame->channels >= 3 )
		pFrame->ppData[y][i + 2] = (unsigned char)(( (((color & 0xFF) * pFrame->maxval + 127) / 255) * alpha
				+ (unsigned long)pFrame->ppData[y][i + 2] * alpha_
																							 ) / 255);
	for( c = 3; c < pFrame->channels; c++ )
		pFrame->ppData[y][i + c] = (unsigned char)(( 0
				+ (unsigned long)pFrame->ppData[y][i + c] * alpha_
																							 ) / 255);
}

void BlinkenFrameResize( stBlinkenFrame * pFrame, Uint32 height, Uint32 width, Uint32 channels, Uint32 maxval ) {
	unsigned char * * ppData;
	Uint32 y, x, c, i, j;
	Uint32 emptyY, emptyX, skipY, skipX, rangeY, rangeX;
	unsigned long val, div;

	if( pFrame == NULL )
		return;

	if( height < BlinkenHeightMin ) height = BlinkenHeightMin;
	if( height > BlinkenHeightMax ) height = BlinkenHeightMax;
	if( width < BlinkenWidthMin ) width = BlinkenWidthMin;
	if( width > BlinkenWidthMax ) width = BlinkenWidthMax;
	if( channels < BlinkenChannelsMin ) channels = BlinkenChannelsMin;
	if( channels > BlinkenChannelsMax ) channels = BlinkenMaxvalMax;
	if( maxval < BlinkenMaxvalMin ) maxval = BlinkenMaxvalMin;
	if( maxval > BlinkenMaxvalMax ) maxval = BlinkenMaxvalMax;

	if( height == pFrame->height &&
			 width == pFrame->width &&
			 channels == pFrame->channels &&
			 maxval == pFrame->maxval )
		return;

  //allocate new data array
	ppData = (unsigned char * *)malloc2D( height, width * channels, sizeof( unsigned char ) );
	if( ppData == NULL )
		return;
	for( y = 0; y < height; y++ )
		for( x = 0, i = 0; x < width; x++ )
			for( c = 0; c < channels; c++, i++ )
				ppData[y][i] = 0;

  //get number of pixels to skip / to leave empty in X and Y direction
	if( height > pFrame->height ) {
		emptyY = (height - pFrame->height) / 2;
		skipY = 0;
		rangeY = pFrame->height;
	}
	else {
		emptyY = 0;
		skipY = (pFrame->height - height) / 2;
		rangeY = height;
	}
	if( width > pFrame->width ) {
		emptyX = (width - pFrame->width) / 2;
		skipX = 0;
		rangeX = pFrame->width;
	}
	else {
		emptyX = 0;
		skipX = (pFrame->width - width) / 2;
		rangeX = width;
	}

  //resize frame with help of calculated parameters
	for( y = 0; y < rangeY; y++ ) {
		for( x = 0; x < rangeX; x++ ) {
			i = (skipX + x) * pFrame->channels;
			j = (emptyX + x) * channels;
			if( channels >= pFrame->channels ) {
				//add channels: copy last channel into new channels
				for( c = 0; c < pFrame->channels; c++, i++, j++ )
					ppData[emptyY + y][j] = (unsigned char)(((unsigned long)pFrame->ppData[skipY + y][i] * maxval + pFrame->maxval / 2) / pFrame->maxval);
				for( ; c < channels; c++, j++ )
					ppData[emptyY + y][j] = ppData[emptyY + y][j - 1];
			}
			else {
				//remove channels: merge leftover channels with last kept channel
				val = 0;
				for( c = 0; c < channels - 1; c++, i++, j++ )
					ppData[emptyY + y][j] = (unsigned char)(((unsigned long)pFrame->ppData[skipY + y][i] * maxval + pFrame->maxval / 2) / pFrame->maxval);
				for( c = channels - 1; c < pFrame->channels; c++, i++ )
					val += (unsigned long)pFrame->ppData[skipY + y][i];
				div = pFrame->maxval * (pFrame->channels - channels + 1);
				ppData[emptyY + y][j++] = (unsigned char)((val * maxval + div / 2) / div);
			}
		}
	}

	pFrame->height = height;
	pFrame->width = width;
	pFrame->channels = channels;
	pFrame->maxval = maxval;
	free( pFrame->ppData );
	pFrame->ppData = ppData;
}

void BlinkenFrameScale( stBlinkenFrame * pFrame, Uint32 height, Uint32 width ) {
	unsigned char * * ppData;
	double scaleHor, scaleVer, ox, oy, ox1, oy1, val;
	Uint32 chans, c, nx, ny, x, y, oxi, oyi, ox1i, oy1i;

	if( pFrame == NULL )
		return;

	if( height < BlinkenHeightMin ) height = BlinkenHeightMin;
	if( height > BlinkenHeightMax ) height = BlinkenHeightMax;
	if( width < BlinkenWidthMin ) width = BlinkenWidthMin;
	if( width > BlinkenWidthMax ) width = BlinkenWidthMax;

	if( height == pFrame->height &&
			 width == pFrame->width )
		return;

	scaleHor = (double)width / (double)pFrame->width;
	scaleVer = (double)height / (double)pFrame->height;

  //allocate new data array
	ppData = (unsigned char * *)malloc2D( height, width * pFrame->channels, sizeof( unsigned char ) );
	if( ppData == NULL )
		return;

  //scale every channel
	chans = pFrame->channels;
	for( c = 0; c < chans; c++ ) {
		for( ny = 0; ny < height; ny++ ) {
			for( nx = 0; nx < width; nx++ ) {
				oy = (double)ny / scaleVer; //sub-pixel exact range in old picture
				ox = (double)nx / scaleHor;
				oy1 = (double)(ny + 1) / scaleVer - 0.000001;
				ox1 = (double)(nx + 1) / scaleHor - 0.000001;
				if( oy < 0 || ox < 0 || oy1 >= pFrame->height || ox1 >= pFrame->width) //out of old picture
					ppData[ny][nx * chans + c] = 0;
				else {
					oyi = (Uint32)oy;
					oxi = (Uint32)ox;
					oy1i = (Uint32)oy1;
					ox1i = (Uint32)ox1;
					if( oyi == oy1i ) {
						if( oxi == ox1i) {
							//one source pixel
							val = (double)pFrame->ppData[oyi][oxi * chans + c];
						}
						else {
							//one line of source pixels
							val = (double)pFrame->ppData[oyi][oxi * chans + c] * (1 - ox + oxi)
									+ (double)pFrame->ppData[oyi][ox1i * chans + c] * (ox1 - ox1i);
							for( x = oxi + 1; x < ox1i; x++ )
								val += (double)pFrame->ppData[oyi][x * chans + c];
							val /= ox1 - ox;
						}
					}
					else {
						//one column of source pixels
						if( oxi == ox1i ) {
							val = (double)pFrame->ppData[oyi][oxi * chans + c] * (1 - oy + oyi)
									+ (double)pFrame->ppData[oy1i][oxi * chans + c] * (oy1 - oy1i);
							for( y = oyi + 1; y < oy1i; y++ )
								val += (double)pFrame->ppData[y][oxi * chans + c];
							val /= oy1 - oy;
						}
						else {
							//rectangle of source pixels
							val = (double)pFrame->ppData[oyi][oxi* chans + c] * (1 - oy + oyi) * (1 - ox + oxi)
									+ (double)pFrame->ppData[oyi][ox1i * chans + c] * (1 - oy + oyi) * (ox1 - ox1i)
									+ (double)pFrame->ppData[oy1i][oxi * chans + c] * (oy1 - oy1i) * (1 - ox + oxi)
									+ (double)pFrame->ppData[oy1i][ox1i * chans + c] * (oy1 - oy1i) * (ox1 - ox1i);
							for( y = oyi + 1; y < oy1i; y++ ) {
								val += (double)pFrame->ppData[y][oxi * chans + c] * (1 - ox + oxi)
										+ (double)pFrame->ppData[y][ox1i * chans + c] * (ox1 - ox1i);
							}
							for( x = oxi + 1; x < ox1i; x++ ) {
								val += (double)pFrame->ppData[oyi][x * chans + c] * (1 - oy + oyi)
										+ (double)pFrame->ppData[oy1i][x * chans + c] * (oy1 - oy1i);
							}
							for( y = oyi + 1; y < oy1i; y++ )
								for( x = oxi + 1; x < ox1i; x++ )
									val += (double)pFrame->ppData[y][x * chans + c];
							val /= (oy1 - oy) * (ox1 - ox);
						}
					}
					ppData[ny][nx * chans + c] = (unsigned char)(val + 0.5);
				}
			} //for( nx ...
		} //for( ny ...
	} //for( c ...

	pFrame->height = height;
	pFrame->width = width;
	free( pFrame->ppData );
	pFrame->ppData = ppData;
}

char * BlinkenFrameToString( stBlinkenFrame * pFrame ) {
	Uint32 size, y, x, c, i;
	char * str, * ptr;
	unsigned long val;

	if( pFrame == NULL )
		return NULL;

	size = pFrame->height * (pFrame->width + 1) + 32;

	str = (char *)malloc( size );
	if( str == NULL )
		return NULL;

	ptr = str;

	for( y = 0; y < pFrame->height; y++ ) {
		for( x = 0, i = 0; x < pFrame->width; x++ ) {
			val = 0;
			for( val = 0, c = 0; c < pFrame->channels; c++, i++ )
				val += pFrame->ppData[y][i];
			val = val * 7 / pFrame->maxval / pFrame->channels;
			*ptr = " -+*%#&@"[val];
			ptr++;
		}
		*ptr = '\n';
		ptr++;
	}

	sprintf( ptr, "%u ms\n", pFrame->duration );

	return str;
}

Sint32 BlinkenFrameToNetwork( stBlinkenFrame * pFrame, etBlinkenProto proto, char * pData, Uint32 maxLength ) {
	//returns length or -1 on error
	Uint32 y, x, c, i, j, val;

	if( pFrame == NULL )
		return -1;

	switch( proto ) {

		case BlinkenProtoNone:
			return 0;

		case BlinkenProtoBlp:
			if( maxLength < (Uint32)sizeof( stBlinkenProtoBlpHdr ) + pFrame->height * pFrame->width ) //buffer too short
				return -1;
			((stBlinkenProtoBlpHdr *)pData)->magic = htonl( BlinkenProtoBlpMagic ); //build header
			((stBlinkenProtoBlpHdr *)pData)->frameNo = htonl( 0 );
			((stBlinkenProtoBlpHdr *)pData)->width = htons( (Uint16)pFrame->width );
			((stBlinkenProtoBlpHdr *)pData)->height = htons( (Uint16)pFrame->height );
			i = sizeof( stBlinkenProtoBlpHdr ); //put data into packet
			for( y = 0; y < pFrame->height; y++ ) {
				for( x = 0, j = 0; x < pFrame->width; x++, i++ ) {
					val = 0;
					for( c = 0; c < pFrame->channels; c++, j++ )
						val += pFrame->ppData[y][j];
					pData[i] = (val >= pFrame->channels * pFrame->maxval / 2 ? 0x01 : 0x00);
				}
			}
			return i; //return length

		case BlinkenProtoEblp:
			if( maxLength < (Uint32)sizeof( stBlinkenProtoEblpHdr ) + pFrame->height * pFrame->width ) //buffer too short
				return -1;
			((stBlinkenProtoEblpHdr *)pData)->magic = htonl( BlinkenProtoEblpMagic ); //build header
			((stBlinkenProtoEblpHdr *)pData)->frameNo = htonl( 0 );
			((stBlinkenProtoEblpHdr *)pData)->width = htons( (Uint16)pFrame->width );
			((stBlinkenProtoEblpHdr *)pData)->height = htons( (Uint16)pFrame->height );
			i = sizeof( stBlinkenProtoEblpHdr ); //put data into packet
			for( y = 0; y < pFrame->height; y++ ) {
				for( x = 0, j = 0; x < pFrame->width; x++, i++ ) {
					val = 0;
					for( c = 0; c < pFrame->channels; c++, j++ )
						val += pFrame->ppData[y][j];
					val /= pFrame->channels;
					pData[i] = (pFrame->maxval == 255 ? (unsigned char)val
						: (unsigned char)((val * 255 + pFrame->maxval / 2) / pFrame->maxval));
				}
			}
			return i; //return length

		case BlinkenProtoMcuf:
			if( maxLength < (Uint32)sizeof( stBlinkenProtoMcufHdr ) + pFrame->height * pFrame->width * pFrame->channels ) //buffer too short
				return -1;
			((stBlinkenProtoMcufHdr *)pData)->magic = htonl( BlinkenProtoMcufMagic ); //build header
			((stBlinkenProtoMcufHdr *)pData)->height = htons( (Uint16)pFrame->height );
			((stBlinkenProtoMcufHdr *)pData)->width = htons( (Uint16)pFrame->width );
			((stBlinkenProtoMcufHdr *)pData)->channels = htons( (Uint16)pFrame->channels );
			((stBlinkenProtoMcufHdr *)pData)->maxval = htons( (Uint16)pFrame->maxval );
			i = sizeof( stBlinkenProtoMcufHdr ); //put data into packet
			for( y = 0; y < pFrame->height; y++ )
				for( x = 0, j = 0; x < pFrame->width; x++ )
					for( c = 0; c < pFrame->channels; c++, i++, j++ )
						pData[i] = pFrame->ppData[y][j];
			return i; //return length

		default:
			return -1;

	}
}

stBlinkenFrame * BlinkenFrameFromNetwork( char * pData, Uint32 length, etBlinkenProto * pProto ) {
	//returns protocol in *pProto if pProto not NULL
	stBlinkenFrame * pFrame;
	Uint32 height, width, channels, maxval, y, x, c, i, j;

	if( length >= (Uint32)sizeof( stBlinkenProtoBlpHdr ) &&
			 ((stBlinkenProtoBlpHdr *)pData)->magic == htonl( BlinkenProtoBlpMagic ) ) {
		if( pProto != NULL ) //return protocol
			*pProto = BlinkenProtoBlp;
		height = ntohs( ((stBlinkenProtoBlpHdr *)pData)->height ); //get header data
		width = ntohs( ((stBlinkenProtoBlpHdr *)pData)->width );
		if( length < (Uint32)sizeof( stBlinkenProtoBlpHdr ) + height * width ) //check length of packet
			return NULL;
		if( height < BlinkenHeightMin || height > BlinkenHeightMax || //check header data
					width < BlinkenWidthMin || width > BlinkenWidthMax )
			return NULL;
		pFrame = BlinkenFrameNew( height, width, 1, 1, 0 ); //create frame according to header data
		if( pFrame == NULL )
			return NULL;
		i = sizeof( stBlinkenProtoBlpHdr ); //put data into frame
		for( y = 0; y < pFrame->height; y++ )
			for( x = 0; x < pFrame->width; x++, i++ )
				pFrame->ppData[y][x * 1 + 0] = pData[i];
		return pFrame;
	}

	if( length >= (Uint32)sizeof( stBlinkenProtoEblpHdr ) &&
			 ((stBlinkenProtoEblpHdr *)pData)->magic == htonl( BlinkenProtoEblpMagic ) ) {
		if( pProto != NULL ) //return protocol
			*pProto = BlinkenProtoEblp;
		height = ntohs( ((stBlinkenProtoEblpHdr *)pData)->height ); //get header data
		width = ntohs( ((stBlinkenProtoEblpHdr *)pData)->width );
		if( length < (Uint32)sizeof( stBlinkenProtoEblpHdr ) + width * height ) //check length of packet
			return NULL;
		if( height < BlinkenHeightMin || height > BlinkenHeightMax || //check header data
					width < BlinkenWidthMin || width > BlinkenWidthMax )
			return NULL;
		pFrame = BlinkenFrameNew( height, width, 1, 255, 0 ); //create frame according to header data
		if( pFrame == NULL )
			return NULL;
		i = sizeof( stBlinkenProtoEblpHdr ); //put data into frame
		for( y = 0; y < pFrame->height; y++ )
			for( x = 0; x < pFrame->width; x++, i++ )
				pFrame->ppData[y][x * 1 + 0] = pData[i];
		return pFrame;
	}

	if( length >= (Uint32)sizeof( stBlinkenProtoMcufHdr ) &&
			 ((stBlinkenProtoMcufHdr *)pData)->magic == htonl( BlinkenProtoMcufMagic ) ) {
		if( pProto != NULL ) //return protocol
			*pProto = BlinkenProtoMcuf;
		height = ntohs( ((stBlinkenProtoMcufHdr *)pData)->height ); //get header data
		width = ntohs( ((stBlinkenProtoMcufHdr *)pData)->width );
		channels = ntohs( ((stBlinkenProtoMcufHdr *)pData)->channels );
		maxval = ntohs( ((stBlinkenProtoMcufHdr *)pData)->maxval );
		if( length < (Uint32)sizeof( stBlinkenProtoMcufHdr ) + height * width * channels ) //check length of packet
			return NULL;
		if( height < BlinkenHeightMin || height > BlinkenHeightMax || //check header data
					width < BlinkenWidthMin || width > BlinkenWidthMax ||
					channels < BlinkenChannelsMin || channels > BlinkenChannelsMax ||
					maxval < BlinkenMaxvalMin || maxval > BlinkenMaxvalMax )
			return NULL;
		pFrame = BlinkenFrameNew( height, width, channels, maxval, 0 ); //create frame according to header data
		if( pFrame == NULL )
			return NULL;
		i = sizeof( stBlinkenProtoMcufHdr ); //put data into frame
		for( y = 0; y < pFrame->height; y++ )
			for( x = 0, j = 0; x < pFrame->width; x++ )
				for( c = 0; c < pFrame->channels; c++, i++, j++ )
					pFrame->ppData[y][j] = pData[i];
		return pFrame;
	}

	if( pProto != NULL ) //return protocol
		*pProto = BlinkenProtoNone;
	return NULL;
}
