Newer
Older
Import / research / embedded / src / library / image.c
/*
  Copyright (c) 2007-2013, John Ryland
 */
#include <image.h>
#include <oslayer.h>
#include <corelayer.h>


// XXX quick fix to get it working
// fsMap should be a drop in replacement for mmap but seems to not be working now
//#define fsMmap		mmap


Image openImageFile(const char *mapableImage)
{
	Image data = { 0 };
	int fd = fsOpen(mapableImage, O_RDONLY);
	if (fd <= 0)
		return data;
	//strPrintf("opened: %i\n", fd);
	unsigned char *ch = (unsigned char *)fsMmap(0, 16, PROT_READ, MAP_PRIVATE, fd, 0);
	if (ch == 0 || (int)ch == -1)
		return data;
	//strPrintf("mapped: %i\n", ch);
	data.fd = fd;
	data.width = ch[0] | (ch[1] << 8) | (ch[2] << 16) | (ch[3] << 24); ch += 4;
	data.height = ch[0] | (ch[1] << 8) | (ch[2] << 16) | (ch[3] << 24); ch += 4;
	data.bytes_per_pixel = ch[0] | (ch[1] << 8) | (ch[2] << 16) | (ch[3] << 24);
	fsMunmap(ch - 8, 16);
	if (data.bytes_per_pixel == -2)
		data.pixel_data = (unsigned char*)fsMmap(0, data.width*data.height*4 + 16, PROT_READ, MAP_PRIVATE, fd, 0) + 16;
	else
		data.pixel_data = (unsigned char*)fsMmap(0, data.width*data.height*data.bytes_per_pixel + 16, PROT_READ, MAP_PRIVATE, fd, 0) + 16;
	//strPrintf("mapped pixels: %i\n", data.pixel_data);
	return data;
}

void closeImageFile(Image image)
{
	fsClose(image.fd);
}

unsigned blendPixels(unsigned c1, unsigned c2, unsigned char a1)
{
	int a2, r, g, b;
	if (!a1)
		return c1;
	if (a1 == 255)
		return c2;
	a2 = 255 - a1;
	r = a2 * (c1 & (0x1F << 11));
	g = a2 * (c1 & (0x3F << 5));
	b = a2 * (c1 & 0x1F);
	r += a1 * (c2 & (0x1F << 11));
	g += a1 * (c2 & (0x3F << 5));
	b += a1 * (c2 & 0x1F);
	r = (r >> 8) & (0x1F << 11);
	g = (g >> 8) & (0x3F << 5);
	b = (b >> 8) & 0x1F;
	return r | g | b;
}

void blitImage(Image *dst, int x, int y, Image *image, int srcX, int srcY, int srcW, int srcH)
{
	int i, j;
	const unsigned char *data = image->pixel_data;
	int width, height, srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2;
	int realBpp = (image->bytes_per_pixel == -2) ? 4 : image->bytes_per_pixel;

	if (srcX >= image->width || srcY >= image->height || x >= dst->width || y >= dst->height)
		return;

	width = (srcW < 0) ? image->width : srcW;
	height = (srcH < 0) ? image->width : srcH;
	srcX1 = (srcX < 0) ? 0 : srcX;
	srcY1 = (srcY < 0) ? 0 : srcY;
	srcX2 = srcX + width;
	srcY2 = srcY + height;
	srcX2 = (srcX2 > image->width) ? image->width : srcX2;
	srcY2 = (srcY2 > image->height) ? image->height : srcY2;
	width = srcX2 - srcX1;
	height = srcY2 - srcY1;
	dstX1 = (x < 0) ? 0 : x; // won't scroll off screen to left as expected when x moves from > 0 to < 0 and srcW is constant
	dstY1 = (y < 0) ? 0 : y; // should do this clamp after
	dstX2 = dstX1 + width;
	dstY2 = dstY1 + height;
	dstX2 = (dstX2 > dst->width) ? dst->width : dstX2;
	dstY2 = (dstY2 > dst->height) ? dst->height : dstY2;
	width = dstX2 - dstX1;
	height = dstY2 - dstY1;

	if ( dst->bytes_per_pixel != 2 ) {
		strPrint("oh no, dst not 16 bit, can't support yet\n");
		return;
	}

	if (image->bytes_per_pixel == -2) {
		//strPrint("Unsupported depth\n");
		//strPrint("depth -2\n");

		for (j = 0; j < height; j++) {
			const unsigned char *row = &data[((srcY1+j)*image->width+srcX1)*4];
			short *dstRow = (short*)&dst->pixel_data[((dstY1+j)*dst->width+dstX1)*2];
			for (i = 0; i < (width>>1); i++) {
				unsigned int col1, col2;
				unsigned int dstCol = ((unsigned int *)dstRow)[i];
				int a1 = row[3];
				if (a1 == 255)
					col1 = dstCol & 0xFFFF;
				else {
					col1 = ((((a1 * (dstCol & (0x1F<<11))) + (row[2]<<16)) & (0x1F<<19)) |
							(((a1 * (dstCol & (0x3F<< 5))) + (row[1]<<11)) & (0x3F<<13)) |
							(((a1 * (dstCol & (0x1F	))) + (row[0]<< 5)) & (0x1F<< 8))) >> 8;
				}
				a1 = row[7];
				dstCol >>= 16;
				if (a1 == 255)
					col2 = dstCol;
				else {
					col2 = ((((a1 * (dstCol & (0x1F<<11))) + (row[6]<<16)) & (0x1F<<19)) |
							(((a1 * (dstCol & (0x3F<< 5))) + (row[5]<<11)) & (0x3F<<13)) |
							(((a1 * (dstCol & (0x1F	))) + (row[4]<< 5)) & (0x1F<< 8))) >> 8;
				}
				((unsigned int *)dstRow)[i] = (col2 << 16) | col1;
				row += 8;
			}
			if (width % 1) {
				dstRow[i] = blendPixels(dstRow[i], (row[0]<<8) | row[1], row[3]);
				row += 4;
			}
		}
	} else if (image->bytes_per_pixel == 2) {
		//strPrint("depth 2\n");
		for (j = 0; j < height; j++) {
			unsigned int *row = (unsigned int *)&data[((srcY1+j)*image->width+srcX1)*2];
			unsigned *dstRow = (unsigned *)&dst->pixel_data[((dstY1+j)*dst->width+dstX1)*2];
			for (i = 0; i < (width>>1); i++) {
				dstRow[i] = row[i];
			}
			if (width % 1)
				((short*)&dstRow[i])[1] = ((short*)&row[i])[1];
		}
	} else if (image->bytes_per_pixel == 4) {
		for (j = 0; j < height; j++) {
			const unsigned char *row = &data[((srcY1+j)*image->width+srcX1)*4];
			short *dstRow = (short *)&dst->pixel_data[((dstY1+j)*dst->width+dstX1)*2];
			for (i = 0; i < (width>>1); i++) {
				unsigned int col1 = ((row[0]>>3)<<11) | ((row[1]>>2)<<5) | (row[2]>>3);
				unsigned int col2 = ((row[4]>>3)<<11) | ((row[5]>>2)<<5) | (row[6]>>3);
				unsigned int dstCol = ((unsigned int *)dstRow)[i];
				col1 = blendPixels(dstCol & 0xFFFF, col1, row[3]);
				col2 = blendPixels(dstCol >> 16, col2, row[7]);
				((unsigned int *)dstRow)[i] = (col2 << 16) | col1;
				row += 8;
			}
			if (width % 1) {
				dstRow[i] = blendPixels(dstRow[i], ((row[0]>>3)<<11) | ((row[1]>>2)<<5) | (row[2]>>3), row[3]);
				row += 4;
			}
		}
	} else if (image->bytes_per_pixel == 3) {
		for (j = 0; j < height; j++) {
			const unsigned char *row = &data[((srcY1+j)*image->width+srcX1)*image->bytes_per_pixel];
			short *dstRow = (short *)&dst->pixel_data[((dstY1+j)*dst->width+dstX1)*2];
			for (i = 0; i < (width>>1); i++) {
				unsigned int col1 = ((row[0]>>3)<<11) | ((row[1]>>2)<<5) | (row[2]>>3);
				unsigned int col2 = ((row[3]>>3)<<11) | ((row[4]>>2)<<5) | (row[5]>>3);
				((unsigned int *)dstRow)[i] = (col2 << 16) | col1;
				row += (image->bytes_per_pixel << 1);
			}
			if (width % 1) {
				dstRow[i*2] = ((row[0]>>3)<<11) | ((row[1]>>2)<<5) | (row[2]>>3);
				row += image->bytes_per_pixel;
			}
		}
	} else {
		strPrint("Unsupported depth\n");
	}
}


#ifdef BUILD_TEST_CASES
#include <screen.h>
void imageTestCases()
{
	Image screen = openScreen();
	Image wallpaper = openImageFile("pics/wallpaper.map");
	//Image wallpaper = openImageFile("pics/opening.map");
	Image overlay = openImageFile("pics/listFocus.map");
	short offscreenBufferPixels[320*240];
	Image offscreenBuffer;
	offscreenBuffer.width = 320;
	offscreenBuffer.height = 240;
	offscreenBuffer.bytes_per_pixel = 2;
	offscreenBuffer.pixel_data = (unsigned char *)offscreenBufferPixels;
	blitImage(&offscreenBuffer, 0, 0, &wallpaper, 0, 0, -1, -1);
	blitImage(&offscreenBuffer, 0, 0, &overlay, 0, 0, -1, -1);
	blitScreen(screen.pixel_data, offscreenBufferPixels);
	closeScreen(screen);
}
#endif