/* * fastimg - * Faster reading and writing of image files. * * This code should work on machines with any byte order. * * Could someone make this run real fast using multiple processors * or how about using memory mapped files to speed it up? * * Paul Haeberli - 1991 * * Changed to return sizes. * Sjoerd Mullender - 1993 * Changed to incorporate into Python. * Sjoerd Mullender - 1993 */ #include "Python.h" #if SIZEOF_INT == 4 typedef int Py_Int32; typedef unsigned int Py_UInt32; #else #if SIZEOF_LONG == 4 typedef long Py_Int32; typedef unsigned long Py_UInt32; #else #error "No 4-byte integral type" #endif #endif #include /* * from image.h * */ typedef struct { unsigned short imagic; /* stuff saved on disk . . */ unsigned short type; unsigned short dim; unsigned short xsize; unsigned short ysize; unsigned short zsize; Py_UInt32 min; Py_UInt32 max; Py_UInt32 wastebytes; char name[80]; Py_UInt32 colormap; Py_Int32 file; /* stuff used in core only */ unsigned short flags; short dorev; short x; short y; short z; short cnt; unsigned short *ptr; unsigned short *base; unsigned short *tmpbuf; Py_UInt32 offset; Py_UInt32 rleend; /* for rle images */ Py_UInt32 *rowstart; /* for rle images */ Py_Int32 *rowsize; /* for rle images */ } IMAGE; #define IMAGIC 0732 #define TYPEMASK 0xff00 #define BPPMASK 0x00ff #define ITYPE_VERBATIM 0x0000 #define ITYPE_RLE 0x0100 #define ISRLE(type) (((type) & 0xff00) == ITYPE_RLE) #define ISVERBATIM(type) (((type) & 0xff00) == ITYPE_VERBATIM) #define BPP(type) ((type) & BPPMASK) #define RLE(bpp) (ITYPE_RLE | (bpp)) #define VERBATIM(bpp) (ITYPE_VERBATIM | (bpp)) /* * end of image.h stuff * */ #define RINTLUM (79) #define GINTLUM (156) #define BINTLUM (21) #define ILUM(r,g,b) ((int)(RINTLUM*(r)+GINTLUM*(g)+BINTLUM*(b))>>8) #define OFFSET_R 3 /* this is byte order dependent */ #define OFFSET_G 2 #define OFFSET_B 1 #define OFFSET_A 0 #define CHANOFFSET(z) (3-(z)) /* this is byte order dependent */ static void expandrow(unsigned char *, unsigned char *, int); static void setalpha(unsigned char *, int); static void copybw(Py_Int32 *, int); static void interleaverow(unsigned char*, unsigned char*, int, int); static int compressrow(unsigned char *, unsigned char *, int, int); static void lumrow(unsigned char *, unsigned char *, int); #ifdef ADD_TAGS #define TAGLEN (5) #else #define TAGLEN (0) #endif static PyObject *ImgfileError; static int reverse_order; #ifdef ADD_TAGS /* * addlongimgtag - * this is used to extract image data from core dumps. * */ static void addlongimgtag(Py_UInt32 *dptr, int xsize, int ysize) { dptr = dptr + (xsize * ysize); dptr[0] = 0x12345678; dptr[1] = 0x59493333; dptr[2] = 0x69434222; dptr[3] = xsize; dptr[4] = ysize; } #endif /* * byte order independent read/write of shorts and longs. * */ static unsigned short getshort(FILE *inf) { unsigned char buf[2]; fread(buf, 2, 1, inf); return (buf[0] << 8) + (buf[1] << 0); } static Py_UInt32 getlong(FILE *inf) { unsigned char buf[4]; fread(buf, 4, 1, inf); return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + (buf[3] << 0); } static void putshort(FILE *outf, unsigned short val) { unsigned char buf[2]; buf[0] = (val >> 8); buf[1] = (val >> 0); fwrite(buf, 2, 1, outf); } static int putlong(FILE *outf, Py_UInt32 val) { unsigned char buf[4]; buf[0] = (unsigned char) (val >> 24); buf[1] = (unsigned char) (val >> 16); buf[2] = (unsigned char) (val >> 8); buf[3] = (unsigned char) (val >> 0); return fwrite(buf, 4, 1, outf); } static void readheader(FILE *inf, IMAGE *image) { memset(image ,0, sizeof(IMAGE)); image->imagic = getshort(inf); image->type = getshort(inf); image->dim = getshort(inf); image->xsize = getshort(inf); image->ysize = getshort(inf); image->zsize = getshort(inf); } static int writeheader(FILE *outf, IMAGE *image) { IMAGE t; memset(&t, 0, sizeof(IMAGE)); fwrite(&t, sizeof(IMAGE), 1, outf); fseek(outf, 0, SEEK_SET); putshort(outf, image->imagic); putshort(outf, image->type); putshort(outf, image->dim); putshort(outf, image->xsize); putshort(outf, image->ysize); putshort(outf, image->zsize); putlong(outf, image->min); putlong(outf, image->max); putlong(outf, 0); return fwrite("no name", 8, 1, outf); } static int writetab(FILE *outf, /*unsigned*/ Py_Int32 *tab, int len) { int r = 0; while(len) { r = putlong(outf, *tab++); len--; } return r; } static void readtab(FILE *inf, /*unsigned*/ Py_Int32 *tab, int len) { while(len) { *tab++ = getlong(inf); len--; } } /* * sizeofimage - * return the xsize and ysize of an iris image file. * */ static PyObject * sizeofimage(PyObject *self, PyObject *args) { char *name; IMAGE image; FILE *inf; if (!PyArg_ParseTuple(args, "s:sizeofimage", &name)) return NULL; inf = fopen(name, "rb"); if (!inf) { PyErr_SetString(ImgfileError, "can't open image file"); return NULL; } readheader(inf, &image); fclose(inf); if (image.imagic != IMAGIC) { PyErr_SetString(ImgfileError, "bad magic number in image file"); return NULL; } return Py_BuildValue("(ii)", image.xsize, image.ysize); } /* * longimagedata - * read in a B/W RGB or RGBA iris image file and return a * pointer to an array of longs. * */ static PyObject * longimagedata(PyObject *self, PyObject *args) { char *name; unsigned char *base, *lptr; unsigned char *rledat = NULL, *verdat = NULL; Py_Int32 *starttab = NULL, *lengthtab = NULL; FILE *inf = NULL; IMAGE image; int y, z, tablen; int xsize, ysize, zsize; int bpp, rle, cur, badorder; int rlebuflen; PyObject *rv = NULL; if (!PyArg_ParseTuple(args, "s:longimagedata", &name)) return NULL; inf = fopen(name,"rb"); if (!inf) { PyErr_SetString(ImgfileError, "can't open image file"); return NULL; } readheader(inf,&image); if (image.imagic != IMAGIC) { PyErr_SetString(ImgfileError, "bad magic number in image file"); goto finally; } rle = ISRLE(image.type); bpp = BPP(image.type); if (bpp != 1) { PyErr_SetString(ImgfileError, "image must have 1 byte per pix chan"); goto finally; } xsize = image.xsize; ysize = image.ysize; zsize = image.zsize; if (rle) { tablen = ysize * zsize * sizeof(Py_Int32); starttab = (Py_Int32 *)malloc(tablen); lengthtab = (Py_Int32 *)malloc(tablen); rlebuflen = (int) (1.05 * xsize +10); rledat = (unsigned char *)malloc(rlebuflen); if (!starttab || !lengthtab || !rledat) { PyErr_NoMemory(); goto finally; } fseek(inf, 512, SEEK_SET); readtab(inf, starttab, ysize*zsize); readtab(inf, lengthtab, ysize*zsize); /* check data order */ cur = 0; badorder = 0; for(y = 0; y < ysize; y++) { for(z = 0; z < zsize; z++) { if (starttab[y + z * ysize] < cur) { badorder = 1; break; } cur = starttab[y +z * ysize]; } if (badorder) break; } fseek(inf, 512 + 2 * tablen, SEEK_SET); cur = 512 + 2 * tablen; rv = PyString_FromStringAndSize((char *)NULL, (xsize * ysize + TAGLEN) * sizeof(Py_Int32)); if (rv == NULL) goto finally; base = (unsigned char *) PyString_AsString(rv); #ifdef ADD_TAGS addlongimgtag(base,xsize,ysize); #endif if (badorder) { for (z = 0; z < zsize; z++) { lptr = base; if (reverse_order) lptr += (ysize - 1) * xsize * sizeof(Py_UInt32); for (y = 0; y < ysize; y++) { int idx = y + z * ysize; if (cur != starttab[idx]) { fseek(inf,starttab[idx], SEEK_SET); cur = starttab[idx]; } if (lengthtab[idx] > rlebuflen) { PyErr_SetString(ImgfileError, "rlebuf is too small"); Py_DECREF(rv); rv = NULL; goto finally; } fread(rledat, lengthtab[idx], 1, inf); cur += lengthtab[idx]; expandrow(lptr, rledat, 3-z); if (reverse_order) lptr -= xsize * sizeof(Py_UInt32); else lptr += xsize * sizeof(Py_UInt32); } } } else { lptr = base; if (reverse_order) lptr += (ysize - 1) * xsize * sizeof(Py_UInt32); for (y = 0; y < ysize; y++) { for(z = 0; z < zsize; z++) { int idx = y + z * ysize; if (cur != starttab[idx]) { fseek(inf, starttab[idx], SEEK_SET); cur = starttab[idx]; } fread(rledat, lengthtab[idx], 1, inf); cur += lengthtab[idx]; expandrow(lptr, rledat, 3-z); } if (reverse_order) lptr -= xsize * sizeof(Py_UInt32); else lptr += xsize * sizeof(Py_UInt32); } } if (zsize == 3) setalpha(base, xsize * ysize); else if (zsize < 3) copybw((Py_Int32 *) base, xsize * ysize); } else { rv = PyString_FromStringAndSize((char *) 0, (xsize*ysize+TAGLEN)*sizeof(Py_Int32)); if (rv == NULL) goto finally; base = (unsigned char *) PyString_AsString(rv); #ifdef ADD_TAGS addlongimgtag(base, xsize, ysize); #endif verdat = (unsigned char *)malloc(xsize); if (!verdat) { Py_CLEAR(rv); goto finally; } fseek(inf, 512, SEEK_SET); for (z = 0; z < zsize; z++) { lptr = base; if (reverse_order) lptr += (ysize - 1) * xsize * sizeof(Py_UInt32); for (y = 0; y < ysize; y++) { fread(verdat, xsize, 1, inf); interleaverow(lptr, verdat, 3-z, xsize); if (reverse_order) lptr -= xsize * sizeof(Py_UInt32); else lptr += xsize * sizeof(Py_UInt32); } } if (zsize == 3) setalpha(base, xsize * ysize); else if (zsize < 3) copybw((Py_Int32 *) base, xsize * ysize); } finally: if (starttab) free(starttab); if (lengthtab) free(lengthtab); if (rledat) free(rledat); if (verdat) free(verdat); fclose(inf); return rv; } /* static utility functions for longimagedata */ static void interleaverow(unsigned char *lptr, unsigned char *cptr, int z, int n) { lptr += z; while (n--) { *lptr = *cptr++; lptr += 4; } } static void copybw(Py_Int32 *lptr, int n) { while (n >= 8) { lptr[0] = 0xff000000 + (0x010101 * (lptr[0] & 0xff)); lptr[1] = 0xff000000 + (0x010101 * (lptr[1] & 0xff)); lptr[2] = 0xff000000 + (0x010101 * (lptr[2] & 0xff)); lptr[3] = 0xff000000 + (0x010101 * (lptr[3] & 0xff)); lptr[4] = 0xff000000 + (0x010101 * (lptr[4] & 0xff)); lptr[5] = 0xff000000 + (0x010101 * (lptr[5] & 0xff)); lptr[6] = 0xff000000 + (0x010101 * (lptr[6] & 0xff)); lptr[7] = 0xff000000 + (0x010101 * (lptr[7] & 0xff)); lptr += 8; n -= 8; } while (n--) { *lptr = 0xff000000 + (0x010101 * (*lptr&0xff)); lptr++; } } static void setalpha(unsigned char *lptr, int n) { while (n >= 8) { lptr[0 * 4] = 0xff; lptr[1 * 4] = 0xff; lptr[2 * 4] = 0xff; lptr[3 * 4] = 0xff; lptr[4 * 4] = 0xff; lptr[5 * 4] = 0xff; lptr[6 * 4] = 0xff; lptr[7 * 4] = 0xff; lptr += 4 * 8; n -= 8; } while (n--) { *lptr = 0xff; lptr += 4; } } static void expandrow(unsigned char *optr, unsigned char *iptr, int z) { unsigned char pixel, count; optr += z; while (1) { pixel = *iptr++; if (!(count = (pixel & 0x7f))) return; if (pixel & 0x80) { while (count >= 8) { optr[0 * 4] = iptr[0]; optr[1 * 4] = iptr[1]; optr[2 * 4] = iptr[2]; optr[3 * 4] = iptr[3]; optr[4 * 4] = iptr[4]; optr[5 * 4] = iptr[5]; optr[6 * 4] = iptr[6]; optr[7 * 4] = iptr[7]; optr += 8 * 4; iptr += 8; count -= 8; } while (count--) { *optr = *iptr++; optr += 4; } } else { pixel = *iptr++; while (count >= 8) { optr[0 * 4] = pixel; optr[1 * 4] = pixel; optr[2 * 4] = pixel; optr[3 * 4] = pixel; optr[4 * 4] = pixel; optr[5 * 4] = pixel; optr[6 * 4] = pixel; optr[7 * 4] = pixel; optr += 8 * 4; count -= 8; } while (count--) { *optr = pixel; optr += 4; } } } } /* * longstoimage - * copy an array of longs to an iris image file. Each long * represents one pixel. xsize and ysize specify the dimensions of * the pixel array. zsize specifies what kind of image file to * write out. if zsize is 1, the luminance of the pixels are * calculated, and a single channel black and white image is saved. * If zsize is 3, an RGB image file is saved. If zsize is 4, an * RGBA image file is saved. * */ static PyObject * longstoimage(PyObject *self, PyObject *args) { unsigned char *lptr; char *name; int xsize, ysize, zsize; FILE *outf = NULL; IMAGE image; int tablen, y, z, pos, len; Py_Int32 *starttab = NULL, *lengthtab = NULL; unsigned char *rlebuf = NULL; unsigned char *lumbuf = NULL; int rlebuflen, goodwrite; PyObject *retval = NULL; if (!PyArg_ParseTuple(args, "s#iiis:longstoimage", &lptr, &len, &xsize, &ysize, &zsize, &name)) return NULL; goodwrite = 1; outf = fopen(name, "wb"); if (!outf) { PyErr_SetString(ImgfileError, "can't open output file"); return NULL; } tablen = ysize * zsize * sizeof(Py_Int32); starttab = (Py_Int32 *)malloc(tablen); lengthtab = (Py_Int32 *)malloc(tablen); rlebuflen = (int) (1.05 * xsize + 10); rlebuf = (unsigned char *)malloc(rlebuflen); lumbuf = (unsigned char *)malloc(xsize * sizeof(Py_Int32)); if (!starttab || !lengthtab || !rlebuf || !lumbuf) { PyErr_NoMemory(); goto finally; } memset(&image, 0, sizeof(IMAGE)); image.imagic = IMAGIC; image.type = RLE(1); if (zsize>1) image.dim = 3; else image.dim = 2; image.xsize = xsize; image.ysize = ysize; image.zsize = zsize; image.min = 0; image.max = 255; goodwrite *= writeheader(outf, &image); pos = 512 + 2 * tablen; fseek(outf, pos, SEEK_SET); if (reverse_order) lptr += (ysize - 1) * xsize * sizeof(Py_UInt32); for (y = 0; y < ysize; y++) { for (z = 0; z < zsize; z++) { if (zsize == 1) { lumrow(lptr, lumbuf, xsize); len = compressrow(lumbuf, rlebuf, CHANOFFSET(z), xsize); } else { len = compressrow(lptr, rlebuf, CHANOFFSET(z), xsize); } if(len > rlebuflen) { PyErr_SetString(ImgfileError, "rlebuf is too small"); goto finally; } goodwrite *= fwrite(rlebuf, len, 1, outf); starttab[y + z * ysize] = pos; lengthtab[y + z * ysize] = len; pos += len; } if (reverse_order) lptr -= xsize * sizeof(Py_UInt32); else lptr += xsize * sizeof(Py_UInt32); } fseek(outf, 512, SEEK_SET); goodwrite *= writetab(outf, starttab, ysize*zsize); goodwrite *= writetab(outf, lengthtab, ysize*zsize); if (goodwrite) { Py_INCREF(Py_None); retval = Py_None; } else PyErr_SetString(ImgfileError, "not enough space for image"); finally: fclose(outf); free(starttab); free(lengthtab); free(rlebuf); free(lumbuf); return retval; } /* static utility functions for longstoimage */ static void lumrow(unsigned char *rgbptr, unsigned char *lumptr, int n) { lumptr += CHANOFFSET(0); while (n--) { *lumptr = ILUM(rgbptr[OFFSET_R], rgbptr[OFFSET_G], rgbptr[OFFSET_B]); lumptr += 4; rgbptr += 4; } } static int compressrow(unsigned char *lbuf, unsigned char *rlebuf, int z, int cnt) { unsigned char *iptr, *ibufend, *sptr, *optr; short todo, cc; Py_Int32 count; lbuf += z; iptr = lbuf; ibufend = iptr + cnt * 4; optr = rlebuf; while(iptr < ibufend) { sptr = iptr; iptr += 8; while ((iptr 126 ? 126 : (short)count; count -= todo; *optr++ = 0x80 | todo; while (todo > 8) { optr[0] = sptr[0 * 4]; optr[1] = sptr[1 * 4]; optr[2] = sptr[2 * 4]; optr[3] = sptr[3 * 4]; optr[4] = sptr[4 * 4]; optr[5] = sptr[5 * 4]; optr[6] = sptr[6 * 4]; optr[7] = sptr[7 * 4]; optr += 8; sptr += 8 * 4; todo -= 8; } while (todo--) { *optr++ = *sptr; sptr += 4; } } sptr = iptr; cc = *iptr; iptr += 4; while ((iptr < ibufend) && (*iptr == cc)) iptr += 4; count = (iptr - sptr) / 4; while (count) { todo = count > 126 ? 126 : (short)count; count -= todo; *optr++ = (unsigned char) todo; *optr++ = (unsigned char) cc; } } *optr++ = 0; return optr - (unsigned char *)rlebuf; } static PyObject * ttob(PyObject *self, PyObject *args) { int order, oldorder; if (!PyArg_ParseTuple(args, "i:ttob", &order)) return NULL; oldorder = reverse_order; reverse_order = order; return PyInt_FromLong(oldorder); } static PyMethodDef rgbimg_methods[] = { {"sizeofimage", sizeofimage, METH_VARARGS}, {"longimagedata", longimagedata, METH_VARARGS}, {"longstoimage", longstoimage, METH_VARARGS}, {"ttob", ttob, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; PyMODINIT_FUNC initrgbimg(void) { PyObject *m, *d; m = Py_InitModule("rgbimg", rgbimg_methods); if (m == NULL) return; d = PyModule_GetDict(m); ImgfileError = PyErr_NewException("rgbimg.error", NULL, NULL); if (ImgfileError != NULL) PyDict_SetItemString(d, "error", ImgfileError); }