blob: ea1c489bec696fabcf1c1b03393488953743a7fb [file] [log] [blame]
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>
#include "imagefile.h"
/* Convert image to a single channel, one byte per pixel */
static
int
notrans(ulong chan)
{
switch(chan){
case GREY1:
case GREY2:
case GREY4:
case CMAP8:
case GREY8:
return 1;
}
return 0;
}
static
int
easycase(ulong chan)
{
switch(chan){
case RGB16:
case RGB24:
case RGBA32:
case ARGB32:
return 1;
}
return 0;
}
/*
* Convert to one byte per pixel, RGBV or grey, depending
*/
static
uchar*
load(Image *image, Memimage *memimage)
{
uchar *data, *p, *q0, *q1, *q2;
uchar *rgbv;
int depth, ndata, dx, dy, i, v;
ulong chan, pixel;
Rectangle r;
Rawimage ri, *nri;
if(memimage == nil){
r = image->r;
depth = image->depth;
chan = image->chan;
}else{
r = memimage->r;
depth = memimage->depth;
chan = memimage->chan;
}
dx = Dx(r);
dy = Dy(r);
/*
* Read image data into memory
* potentially one extra byte on each end of each scan line.
*/
ndata = dy*(2+bytesperline(r, depth));
data = malloc(ndata);
if(data == nil)
return nil;
if(memimage != nil)
ndata = unloadmemimage(memimage, r, data, ndata);
else
ndata = unloadimage(image, r, data, ndata);
if(ndata < 0){
werrstr("onechan: %r");
free(data);
return nil;
}
/*
* Repack
*/
memset(&ri, 0, sizeof(ri));
ri.r = r;
ri.cmap = nil;
ri.cmaplen = 0;
ri.nchans = 3;
ri.chanlen = dx*dy;
ri.chans[0] = malloc(ri.chanlen);
ri.chans[1] = malloc(ri.chanlen);
ri.chans[2] = malloc(ri.chanlen);
if(ri.chans[0]==nil || ri.chans[1]==nil || ri.chans[2]==nil){
Err:
free(ri.chans[0]);
free(ri.chans[1]);
free(ri.chans[2]);
free(data);
return nil;
}
ri.chandesc = CRGB;
p = data;
q0 = ri.chans[0];
q1 = ri.chans[1];
q2 = ri.chans[2];
switch(chan){
default:
werrstr("can't handle image type 0x%lux", chan);
goto Err;
case RGB16:
for(i=0; i<ri.chanlen; i++, p+=2){
pixel = (p[1]<<8)|p[0]; /* rrrrrggg gggbbbbb */
v = (pixel & 0xF800) >> 8;
*q0++ = v | (v>>5);
v = (pixel & 0x07E0) >> 3;
*q1++ = v | (v>>6);
v = (pixel & 0x001F) << 3;
*q2++ = v | (v>>5);
}
break;
case RGB24:
for(i=0; i<ri.chanlen; i++){
*q2++ = *p++;
*q1++ = *p++;
*q0++ = *p++;
}
break;
case RGBA32:
for(i=0; i<ri.chanlen; i++){
*q2++ = *p++;
*q1++ = *p++;
*q0++ = *p++;
p++;
}
break;
case ARGB32:
for(i=0; i<ri.chanlen; i++){
p++;
*q2++ = *p++;
*q1++ = *p++;
*q0++ = *p++;
}
break;
}
rgbv = nil;
nri = torgbv(&ri, 1);
if(nri != nil){
rgbv = nri->chans[0];
free(nri);
}
free(ri.chans[0]);
free(ri.chans[1]);
free(ri.chans[2]);
free(data);
return rgbv;
}
Image*
onechan(Image *i)
{
uchar *data;
Image *ni;
if(notrans(i->chan))
return i;
if(easycase(i->chan))
data = load(i, nil);
else{
ni = allocimage(display, i->r, RGB24, 0, DNofill);
if(ni == nil)
return ni;
draw(ni, ni->r, i, nil, i->r.min);
data = load(ni, nil);
freeimage(ni);
}
if(data == nil)
return nil;
ni = allocimage(display, i->r, CMAP8, 0, DNofill);
if(ni != nil)
if(loadimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
freeimage(ni);
ni = nil;
}
free(data);
return ni;
}
Memimage*
memonechan(Memimage *i)
{
uchar *data;
Memimage *ni;
if(notrans(i->chan))
return i;
if(easycase(i->chan))
data = load(nil, i);
else{
ni = allocmemimage(i->r, RGB24);
if(ni == nil)
return ni;
memimagedraw(ni, ni->r, i, i->r.min, nil, ZP, S);
data = load(nil, ni);
freememimage(ni);
}
if(data == nil)
return nil;
ni = allocmemimage(i->r, CMAP8);
if(ni != nil)
if(loadmemimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
freememimage(ni);
ni = nil;
}
free(data);
return ni;
}