The format of the string data used in the imageop module is described

as "This is the same format as used by gl.lrectwrite() and the imgfile
module."  This implies a certain byte order in multi-byte pixel
formats.  However, the code was originally written on an SGI
(big-endian) and *uses* the fact that bytes are stored in a particular
order in ints.  This means that the code uses and produces different
byte order on little-endian systems.

This fix adds a module-level flag "backward_compatible" (default not
set, and if not set, behaves as if set to 1--i.e. backward compatible)
that can be used on a little-endian system to use the same byte order
as the SGI.  Using this flag it is then possible to prepare
SGI-compatible images on a little-endian system.

This patch is the result of a (small) discussion on python-dev and was
submitted to SourceForge as patch #874358.
This commit is contained in:
Sjoerd Mullender 2004-01-10 20:43:43 +00:00
parent 2e5e6445e7
commit 7e6bbe1516
2 changed files with 120 additions and 33 deletions

View File

@ -86,3 +86,15 @@ Convert a 4-bit greyscale image to an 8-bit greyscale image.
\begin{funcdesc}{grey22grey}{image, width, height}
Convert a 2-bit greyscale image to an 8-bit greyscale image.
\end{funcdesc}
\begin{datadesc}{backward_compatible}
If set to 0, the functions in this module use a non-backward
compatible way of representing multi-byte pixels on little-endian
systems. The SGI for which this module was originally written is a
big-endian system, so setting this variable will have no effect.
However, the code wasn't originally intended to run on anything else,
so it made assumptions about byte order which are not universal.
Setting this variable to 0 will cause the byte order to be reversed on
little-endian systems, so that it then is the same as on big-endian
systems.
\end{datadesc}

View File

@ -24,7 +24,54 @@ typedef unsigned long Py_UInt32;
#define LONGP(cp, xmax, x, y) ((Py_Int32 *)(cp+4*(y*xmax+x)))
static PyObject *ImageopError;
static PyObject *ImageopDict;
/* If this function returns true (the default if anything goes wrong), we're
behaving in a backward-compatible way with respect to how multi-byte pixels
are stored in the strings. The code in this module was originally written
for an SGI which is a big-endian system, and so the old code assumed that
4-byte integers hold the R, G, and B values in a particular order.
However, on little-endian systems the order is reversed, and so not
actually compatible with what gl.lrectwrite and imgfile expect.
(gl.lrectwrite and imgfile are also SGI-specific, however, it is
conceivable that the data handled here comes from or goes to an SGI or that
it is otherwise used in the expectation that the byte order in the strings
is as specified.)
The function returns the value of the module variable
"backward_compatible", or 1 if the variable does not exist or is not an
int.
*/
static int
imageop_backward_compatible(void)
{
static PyObject *bcos;
PyObject *bco;
long rc;
if (ImageopDict == NULL) /* "cannot happen" */
return 1;
if (bcos == NULL) {
/* cache string object for future use */
bcos = PyString_FromString("backward_compatible");
if (bcos == NULL)
return 1;
}
bco = PyDict_GetItem(ImageopDict, bcos);
if (bco == NULL)
return 1;
if (!PyInt_Check(bco))
return 1;
rc = PyInt_AsLong(bco);
if (PyErr_Occurred()) {
/* not an integer, or too large, or something */
PyErr_Clear();
rc = 1;
}
return rc != 0; /* convert to values 0, 1 */
}
static PyObject *
imageop_crop(PyObject *self, PyObject *args)
{
@ -497,11 +544,11 @@ static PyObject *
imageop_rgb2rgb8(PyObject *self, PyObject *args)
{
int x, y, len, nlen;
Py_UInt32 *cp;
unsigned char *cp;
unsigned char *ncp;
PyObject *rv;
int i, r, g, b;
Py_UInt32 value, nvalue;
int backward_compatible = imageop_backward_compatible();
if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) )
return 0;
@ -519,18 +566,19 @@ imageop_rgb2rgb8(PyObject *self, PyObject *args)
for ( i=0; i < nlen; i++ ) {
/* Bits in source: aaaaaaaa BBbbbbbb GGGggggg RRRrrrrr */
value = *cp++;
#if 0
r = (value >> 5) & 7;
g = (value >> 13) & 7;
b = (value >> 22) & 3;
#else
r = (int) ((value & 0xff) / 255. * 7. + .5);
g = (int) (((value >> 8) & 0xff) / 255. * 7. + .5);
b = (int) (((value >> 16) & 0xff) / 255. * 3. + .5);
#endif
nvalue = (r<<5) | (b<<3) | g;
*ncp++ = (unsigned char)nvalue;
if (backward_compatible) {
Py_UInt32 value = * (Py_UInt32 *) cp;
cp += 4;
r = (int) ((value & 0xff) / 255. * 7. + .5);
g = (int) (((value >> 8) & 0xff) / 255. * 7. + .5);
b = (int) (((value >> 16) & 0xff) / 255. * 3. + .5);
} else {
cp++; /* skip alpha channel */
b = (int) (*cp++ / 255. * 3. + .5);
g = (int) (*cp++ / 255. * 7. + .5);
r = (int) (*cp++ / 255. * 7. + .5);
}
*ncp++ = (unsigned char)((r<<5) | (b<<3) | g);
}
return rv;
}
@ -540,10 +588,11 @@ imageop_rgb82rgb(PyObject *self, PyObject *args)
{
int x, y, len, nlen;
unsigned char *cp;
Py_UInt32 *ncp;
unsigned char *ncp;
PyObject *rv;
int i, r, g, b;
Py_UInt32 value, nvalue;
unsigned char value;
int backward_compatible = imageop_backward_compatible();
if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) )
return 0;
@ -557,7 +606,7 @@ imageop_rgb82rgb(PyObject *self, PyObject *args)
rv = PyString_FromStringAndSize(NULL, nlen*4);
if ( rv == 0 )
return 0;
ncp = (Py_UInt32 *)PyString_AsString(rv);
ncp = (unsigned char *)PyString_AsString(rv);
for ( i=0; i < nlen; i++ ) {
/* Bits in source: RRRBBGGG
@ -570,8 +619,16 @@ imageop_rgb82rgb(PyObject *self, PyObject *args)
r = (r<<5) | (r<<3) | (r>>1);
g = (g<<5) | (g<<3) | (g>>1);
b = (b<<6) | (b<<4) | (b<<2) | b;
nvalue = r | (g<<8) | (b<<16);
*ncp++ = nvalue;
if (backward_compatible) {
Py_UInt32 nvalue = r | (g<<8) | (b<<16);
* (Py_UInt32 *) ncp = nvalue;
ncp += 4;
} else {
*ncp++ = 0;
*ncp++ = b;
*ncp++ = g;
*ncp++ = r;
}
}
return rv;
}
@ -580,11 +637,12 @@ static PyObject *
imageop_rgb2grey(PyObject *self, PyObject *args)
{
int x, y, len, nlen;
Py_UInt32 *cp;
unsigned char *cp;
unsigned char *ncp;
PyObject *rv;
int i, r, g, b;
Py_UInt32 value, nvalue;
int nvalue;
int backward_compatible = imageop_backward_compatible();
if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) )
return 0;
@ -601,10 +659,18 @@ imageop_rgb2grey(PyObject *self, PyObject *args)
ncp = (unsigned char *)PyString_AsString(rv);
for ( i=0; i < nlen; i++ ) {
value = *cp++;
r = (value ) & 0xff;
g = (value >> 8) & 0xff;
b = (value >> 16) & 0xff;
if (backward_compatible) {
Py_UInt32 value = * (Py_UInt32 *) cp;
cp += 4;
r = (int) ((value & 0xff) / 255. * 7. + .5);
g = (int) (((value >> 8) & 0xff) / 255. * 7. + .5);
b = (int) (((value >> 16) & 0xff) / 255. * 3. + .5);
} else {
cp++; /* skip alpha channel */
b = *cp++;
g = *cp++;
r = *cp++;
}
nvalue = (int)(0.30*r + 0.59*g + 0.11*b);
if ( nvalue > 255 ) nvalue = 255;
*ncp++ = (unsigned char)nvalue;
@ -617,10 +683,11 @@ imageop_grey2rgb(PyObject *self, PyObject *args)
{
int x, y, len, nlen;
unsigned char *cp;
Py_UInt32 *ncp;
unsigned char *ncp;
PyObject *rv;
int i;
Py_UInt32 value;
unsigned char value;
int backward_compatible = imageop_backward_compatible();
if ( !PyArg_ParseTuple(args, "s#ii", &cp, &len, &x, &y) )
return 0;
@ -634,11 +701,19 @@ imageop_grey2rgb(PyObject *self, PyObject *args)
rv = PyString_FromStringAndSize(NULL, nlen*4);
if ( rv == 0 )
return 0;
ncp = (Py_UInt32 *)PyString_AsString(rv);
ncp = (unsigned char *)PyString_AsString(rv);
for ( i=0; i < nlen; i++ ) {
value = *cp++;
*ncp++ = value | (value << 8 ) | (value << 16);
if (backward_compatible) {
* (Py_UInt32 *) ncp = (Py_UInt32) value | ((Py_UInt32) value << 8 ) | ((Py_UInt32) value << 16);
ncp += 4;
} else {
*ncp++ = 0;
*ncp++ = value;
*ncp++ = value;
*ncp++ = value;
}
}
return rv;
}
@ -699,10 +774,10 @@ static PyMethodDef imageop_methods[] = {
PyMODINIT_FUNC
initimageop(void)
{
PyObject *m, *d;
PyObject *m;
m = Py_InitModule("imageop", imageop_methods);
d = PyModule_GetDict(m);
ImageopDict = PyModule_GetDict(m);
ImageopError = PyErr_NewException("imageop.error", NULL, NULL);
if (ImageopError != NULL)
PyDict_SetItemString(d, "error", ImageopError);
PyDict_SetItemString(ImageopDict, "error", ImageopError);
}