/*
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