mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-04-20 09:12:00 +03:00
introduce multi-band format (TIFF only)
This commit is contained in:
parent
03df35777c
commit
936439b481
|
@ -877,14 +877,14 @@ class TestFileTiff:
|
|||
def test_open_tiff_uint16_multiband(self):
|
||||
"""Test opening multiband TIFFs and reading all channels."""
|
||||
base_value = 4660
|
||||
for i in range(2, 6):
|
||||
for i in range(1, 6):
|
||||
infile = f"Tests/images/uint16_{i}_{base_value}.tif"
|
||||
im = Image.open(infile)
|
||||
im.load()
|
||||
pixel = [base_value + j for j in range(0, i)]
|
||||
pixel = tuple([base_value + j for j in range(0, i)])
|
||||
actual_pixel = im.getpixel((0, 0))
|
||||
if isinstance(actual_pixel, int):
|
||||
actual_pixel = [actual_pixel]
|
||||
actual_pixel = (actual_pixel,)
|
||||
assert actual_pixel == pixel
|
||||
|
||||
|
||||
|
|
|
@ -117,6 +117,8 @@ class ImageFile(Image.Image):
|
|||
|
||||
self.readonly = 1 # until we know better
|
||||
|
||||
self.newconfig = ()
|
||||
|
||||
self.decoderconfig = ()
|
||||
self.decodermaxblock = MAXBLOCK
|
||||
|
||||
|
@ -317,7 +319,7 @@ class ImageFile(Image.Image):
|
|||
def load_prepare(self) -> None:
|
||||
# create image memory if necessary
|
||||
if not self.im or self.im.mode != self.mode or self.im.size != self.size:
|
||||
self.im = Image.core.new(self.mode, self.size)
|
||||
self.im = Image.core.new(self.mode, self.size, *self.newconfig)
|
||||
# create palette (optional)
|
||||
if self.mode == "P":
|
||||
Image.Image.load(self)
|
||||
|
|
|
@ -60,7 +60,7 @@ from ._deprecate import deprecate
|
|||
from .TiffTags import TYPES
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG) # XXX hack202406
|
||||
logger.setLevel(logging.DEBUG) # XXX hack202406
|
||||
|
||||
# Set these to true to force use of libtiff for reading or writing.
|
||||
READ_LIBTIFF = False
|
||||
|
@ -183,7 +183,7 @@ OPEN_INFO = {
|
|||
(II, 1, (1,), 1, (12,), ()): ("I;16", "I;12"),
|
||||
(II, 0, (1,), 1, (16,), ()): ("I;16", "I;16"),
|
||||
(II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"),
|
||||
(II, 1, (1, 1), 1, (16, 16), (0,)): ("I;16", "I;16"),
|
||||
(II, 1, (1, 1), 1, (16, 16), (0,)): ("MB", "MB"),
|
||||
(
|
||||
II,
|
||||
1,
|
||||
|
@ -194,7 +194,7 @@ OPEN_INFO = {
|
|||
0,
|
||||
0,
|
||||
),
|
||||
): ("I;16", "I;16"),
|
||||
): ("MB", "MB"),
|
||||
(
|
||||
II,
|
||||
1,
|
||||
|
@ -206,7 +206,7 @@ OPEN_INFO = {
|
|||
0,
|
||||
0,
|
||||
),
|
||||
): ("I;16", "I;16"),
|
||||
): ("MB", "MB"),
|
||||
(
|
||||
II,
|
||||
1,
|
||||
|
@ -219,7 +219,7 @@ OPEN_INFO = {
|
|||
0,
|
||||
0,
|
||||
),
|
||||
): ("I;16", "I;16"),
|
||||
): ("MB", "MB"),
|
||||
(MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"),
|
||||
(II, 1, (1,), 2, (16,), ()): ("I;16", "I;16R"),
|
||||
(II, 1, (2,), 1, (16,), ()): ("I", "I;16S"),
|
||||
|
@ -1474,6 +1474,9 @@ class TiffImageFile(ImageFile.ImageFile):
|
|||
|
||||
logger.debug("- raw mode: %s", rawmode)
|
||||
logger.debug("- pil mode: %s", self.mode)
|
||||
if self.mode == "MB":
|
||||
assert max(bps_tuple) == min(bps_tuple)
|
||||
self.newconfig = (max(bps_tuple), samples_per_pixel)
|
||||
|
||||
self.info["compression"] = self._compression
|
||||
|
||||
|
|
|
@ -310,7 +310,7 @@ getbands(const char *mode) {
|
|||
int bands;
|
||||
|
||||
/* FIXME: add primitive to libImaging to avoid extra allocation */
|
||||
im = ImagingNew(mode, 0, 0);
|
||||
im = ImagingNew(mode, 0, 0, -1, -1);
|
||||
if (!im) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -434,6 +434,35 @@ float16tofloat32(const FLOAT16 in) {
|
|||
return out[0];
|
||||
}
|
||||
|
||||
static inline PyObject *
|
||||
getpixel_mb(Imaging im, ImagingAccess access, int x, int y) {
|
||||
UINT8 pixel[im->pixelsize];
|
||||
access->get_pixel(im, x, y, &pixel);
|
||||
|
||||
PyObject *tuple = PyTuple_New(im->bands);
|
||||
if (tuple == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UINT8 *pos = pixel;
|
||||
for (int i = 0; i < im->bands; ++i) {
|
||||
switch (im->depth) {
|
||||
case CHAR_BIT:
|
||||
PyTuple_SET_ITEM(tuple, i, PyLong_FromLong(*pos));
|
||||
break;
|
||||
case 2 * CHAR_BIT:
|
||||
PyTuple_SET_ITEM(tuple, i, PyLong_FromLong(*(UINT16 *)pos));
|
||||
break;
|
||||
case 4 * CHAR_BIT:
|
||||
PyTuple_SET_ITEM(tuple, i, PyLong_FromLong(*(INT32 *)pos));
|
||||
break;
|
||||
}
|
||||
pos += im->depth / CHAR_BIT;
|
||||
}
|
||||
|
||||
return tuple;
|
||||
}
|
||||
|
||||
static inline PyObject *
|
||||
getpixel(Imaging im, ImagingAccess access, int x, int y) {
|
||||
union {
|
||||
|
@ -455,6 +484,10 @@ getpixel(Imaging im, ImagingAccess access, int x, int y) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (im->type == IMAGING_TYPE_MB) {
|
||||
return getpixel_mb(im, access, x, y);
|
||||
}
|
||||
|
||||
access->get_pixel(im, x, y, &pixel);
|
||||
|
||||
switch (im->type) {
|
||||
|
@ -685,13 +718,13 @@ _fill(PyObject *self, PyObject *args) {
|
|||
static PyObject *
|
||||
_new(PyObject *self, PyObject *args) {
|
||||
char *mode;
|
||||
int xsize, ysize;
|
||||
int xsize, ysize, depth = -1, bands = -1;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s(ii)", &mode, &xsize, &ysize)) {
|
||||
if (!PyArg_ParseTuple(args, "s(ii)|ii", &mode, &xsize, &ysize, &depth, &bands)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return PyImagingNew(ImagingNew(mode, xsize, ysize));
|
||||
return PyImagingNew(ImagingNew(mode, xsize, ysize, depth, bands));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
@ -1714,7 +1747,8 @@ _quantize(ImagingObject *self, PyObject *args) {
|
|||
|
||||
if (!self->image->xsize || !self->image->ysize) {
|
||||
/* no content; return an empty image */
|
||||
return PyImagingNew(ImagingNew("P", self->image->xsize, self->image->ysize));
|
||||
return PyImagingNew(
|
||||
ImagingNew("P", self->image->xsize, self->image->ysize, -1, -1));
|
||||
}
|
||||
|
||||
return PyImagingNew(ImagingQuantize(self->image, colours, method, kmeans));
|
||||
|
@ -2782,7 +2816,7 @@ _font_getmask(ImagingFontObject *self, PyObject *args) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
im = ImagingNew(self->bitmap->mode, textwidth(self, text), self->ysize);
|
||||
im = ImagingNew(self->bitmap->mode, textwidth(self, text), self->ysize, -1, -1);
|
||||
if (!im) {
|
||||
free(text);
|
||||
return ImagingError_MemoryError();
|
||||
|
|
25
src/decode.c
25
src/decode.c
|
@ -291,11 +291,33 @@ static PyTypeObject ImagingDecoderType = {
|
|||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
int
|
||||
static void
|
||||
mb_shuffle_passthru(UINT8 *dst, const UINT8 *src, Imaging im, ImagingCodecState state) {
|
||||
state->shuffle(dst, src, state->xsize);
|
||||
}
|
||||
|
||||
static void
|
||||
shuffle_mb_unavail(UINT8 *dst, const UINT8 *src, int pixels) {
|
||||
abort();
|
||||
}
|
||||
|
||||
static void
|
||||
mb_shuffle(UINT8 *dst, const UINT8 *src, Imaging im, ImagingCodecState state) {
|
||||
memcpy(dst, src, state->xsize * im->pixelsize);
|
||||
}
|
||||
|
||||
static int
|
||||
get_unpacker(ImagingDecoderObject *decoder, const char *mode, const char *rawmode) {
|
||||
int bits;
|
||||
ImagingShuffler unpack;
|
||||
|
||||
if (strcmp(mode, IMAGING_MODE_MB) == 0) {
|
||||
decoder->state.shuffle = shuffle_mb_unavail;
|
||||
decoder->state.mb_shuffle = mb_shuffle;
|
||||
decoder->state.bits = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unpack = ImagingFindUnpacker(mode, rawmode, &bits);
|
||||
if (!unpack) {
|
||||
Py_DECREF(decoder);
|
||||
|
@ -304,6 +326,7 @@ get_unpacker(ImagingDecoderObject *decoder, const char *mode, const char *rawmod
|
|||
}
|
||||
|
||||
decoder->state.shuffle = unpack;
|
||||
decoder->state.mb_shuffle = mb_shuffle_passthru;
|
||||
decoder->state.bits = bits;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -133,6 +133,11 @@ get_pixel_32B(Imaging im, int x, int y, void *color) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
get_pixel_mb(Imaging im, int x, int y, void *color) {
|
||||
memcpy(color, &im->image[y][x * im->pixelsize], im->pixelsize);
|
||||
}
|
||||
|
||||
/* store individual pixel */
|
||||
|
||||
static void
|
||||
|
@ -183,6 +188,11 @@ put_pixel_32(Imaging im, int x, int y, const void *color) {
|
|||
memcpy(&im->image32[y][x], color, sizeof(INT32));
|
||||
}
|
||||
|
||||
static void
|
||||
put_pixel_mb(Imaging im, int x, int y, void *color) {
|
||||
memcpy(&im->image[y][x * im->pixelsize], color, im->pixelsize);
|
||||
}
|
||||
|
||||
void
|
||||
ImagingAccessInit() {
|
||||
#define ADD(mode_, get_pixel_, put_pixel_) \
|
||||
|
@ -222,6 +232,7 @@ ImagingAccessInit() {
|
|||
ADD("YCbCr", get_pixel_32, put_pixel_32);
|
||||
ADD("LAB", get_pixel_32, put_pixel_32);
|
||||
ADD("HSV", get_pixel_32, put_pixel_32);
|
||||
ADD("MB", get_pixel_mb, put_pixel_mb);
|
||||
}
|
||||
|
||||
ImagingAccess
|
||||
|
|
|
@ -68,10 +68,13 @@ typedef struct ImagingPaletteInstance *ImagingPalette;
|
|||
#define IMAGING_TYPE_INT32 1
|
||||
#define IMAGING_TYPE_FLOAT32 2
|
||||
#define IMAGING_TYPE_SPECIAL 3 /* check mode for details */
|
||||
#define IMAGING_TYPE_MB 4 /* multi-band format */
|
||||
|
||||
#define IMAGING_MODE_LENGTH \
|
||||
6 + 1 /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK", "YCbCr", "BGR;xy") */
|
||||
|
||||
#define IMAGING_MODE_MB "MB" /* multi-band format */
|
||||
|
||||
typedef struct {
|
||||
char *ptr;
|
||||
int size;
|
||||
|
@ -80,9 +83,9 @@ typedef struct {
|
|||
struct ImagingMemoryInstance {
|
||||
/* Format */
|
||||
char mode[IMAGING_MODE_LENGTH]; /* Band names ("1", "L", "P", "RGB", "RGBA", "CMYK",
|
||||
"YCbCr", "BGR;xy") */
|
||||
"YCbCr", "BGR;xy", "MB") */
|
||||
int type; /* Data type (IMAGING_TYPE_*) */
|
||||
int depth; /* Depth (ignored in this version) */
|
||||
int depth; /* Sample size (1, 2, or 4) in multi-band format */
|
||||
int bands; /* Number of bands (1, 2, 3, or 4) */
|
||||
int xsize; /* Image dimension. */
|
||||
int ysize;
|
||||
|
@ -173,7 +176,7 @@ extern void
|
|||
ImagingMemoryClearCache(ImagingMemoryArena arena, int new_size);
|
||||
|
||||
extern Imaging
|
||||
ImagingNew(const char *mode, int xsize, int ysize);
|
||||
ImagingNew(const char *mode, int xsize, int ysize, int depth, int bands);
|
||||
extern Imaging
|
||||
ImagingNewDirty(const char *mode, int xsize, int ysize);
|
||||
extern Imaging
|
||||
|
@ -185,9 +188,10 @@ extern Imaging
|
|||
ImagingNewBlock(const char *mode, int xsize, int ysize);
|
||||
|
||||
extern Imaging
|
||||
ImagingNewPrologue(const char *mode, int xsize, int ysize);
|
||||
ImagingNewPrologue(const char *mode, int xsize, int ysize, int depth, int bands);
|
||||
extern Imaging
|
||||
ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int structure_size);
|
||||
ImagingNewPrologueSubtype(
|
||||
const char *mode, int xsize, int ysize, int depth, int bands, int structure_size);
|
||||
|
||||
extern void
|
||||
ImagingCopyPalette(Imaging destination, Imaging source);
|
||||
|
@ -663,6 +667,8 @@ struct ImagingCodecStateInstance {
|
|||
int ystep;
|
||||
int xsize, ysize, xoff, yoff;
|
||||
ImagingShuffler shuffle;
|
||||
void (*mb_shuffle)(
|
||||
UINT8 *dst, const UINT8 *src, Imaging im, ImagingCodecState state);
|
||||
int bits, bytes;
|
||||
UINT8 *buffer;
|
||||
void *context;
|
||||
|
|
|
@ -152,7 +152,7 @@ ImagingPoint(Imaging imIn, const char *mode, const void *table) {
|
|||
goto mode_mismatch;
|
||||
}
|
||||
|
||||
imOut = ImagingNew(mode, imIn->xsize, imIn->ysize);
|
||||
imOut = ImagingNew(mode, imIn->xsize, imIn->ysize, -1, -1);
|
||||
if (!imOut) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -214,7 +214,7 @@ ImagingPointTransform(Imaging imIn, double scale, double offset) {
|
|||
return (Imaging)ImagingError_ModeError();
|
||||
}
|
||||
|
||||
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize);
|
||||
imOut = ImagingNew(imIn->mode, imIn->xsize, imIn->ysize, -1, -1);
|
||||
if (!imOut) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -84,7 +84,8 @@ MakeRankFunction(UINT8) MakeRankFunction(INT32) MakeRankFunction(FLOAT32)
|
|||
return (Imaging)ImagingError_ValueError("bad rank value");
|
||||
}
|
||||
|
||||
imOut = ImagingNew(im->mode, im->xsize - 2 * margin, im->ysize - 2 * margin);
|
||||
imOut =
|
||||
ImagingNew(im->mode, im->xsize - 2 * margin, im->ysize - 2 * margin, -1, -1);
|
||||
if (!imOut) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -71,10 +71,11 @@ ImagingRawDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t byt
|
|||
}
|
||||
|
||||
/* Unpack data */
|
||||
state->shuffle(
|
||||
state->mb_shuffle(
|
||||
(UINT8 *)im->image[state->y + state->yoff] + state->xoff * im->pixelsize,
|
||||
ptr,
|
||||
state->xsize);
|
||||
im,
|
||||
state);
|
||||
|
||||
ptr += state->bytes;
|
||||
bytes -= state->bytes;
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
*/
|
||||
|
||||
Imaging
|
||||
ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
|
||||
ImagingNewPrologueSubtype(
|
||||
const char *mode, int xsize, int ysize, int depth, int bands, int size) {
|
||||
Imaging im;
|
||||
|
||||
/* linesize overflow check, roughly the current largest space req'd */
|
||||
|
@ -190,6 +191,17 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
|
|||
im->pixelsize = 4;
|
||||
im->linesize = xsize * 4;
|
||||
|
||||
} else if (strcmp(mode, IMAGING_MODE_MB) == 0) {
|
||||
if (bands <= 0 || depth <= 0) {
|
||||
return (Imaging)ImagingError_ValueError(
|
||||
"multi-band missing bands and depth");
|
||||
}
|
||||
im->bands = bands;
|
||||
im->depth = depth;
|
||||
im->pixelsize = depth * bands;
|
||||
im->linesize = xsize * im->pixelsize;
|
||||
im->type = IMAGING_TYPE_MB;
|
||||
|
||||
} else {
|
||||
free(im);
|
||||
return (Imaging)ImagingError_ValueError("unrecognized image mode");
|
||||
|
@ -225,9 +237,9 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
|
|||
}
|
||||
|
||||
Imaging
|
||||
ImagingNewPrologue(const char *mode, int xsize, int ysize) {
|
||||
ImagingNewPrologue(const char *mode, int xsize, int ysize, int depth, int bands) {
|
||||
return ImagingNewPrologueSubtype(
|
||||
mode, xsize, ysize, sizeof(struct ImagingMemoryInstance));
|
||||
mode, xsize, ysize, depth, bands, sizeof(struct ImagingMemoryInstance));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -485,15 +497,16 @@ ImagingAllocateBlock(Imaging im) {
|
|||
* Create a new, internally allocated, image.
|
||||
*/
|
||||
|
||||
Imaging
|
||||
ImagingNewInternal(const char *mode, int xsize, int ysize, int dirty) {
|
||||
static Imaging
|
||||
ImagingNewInternal(
|
||||
const char *mode, int xsize, int ysize, int depth, int bands, int dirty) {
|
||||
Imaging im;
|
||||
|
||||
if (xsize < 0 || ysize < 0) {
|
||||
return (Imaging)ImagingError_ValueError("bad image size");
|
||||
}
|
||||
|
||||
im = ImagingNewPrologue(mode, xsize, ysize);
|
||||
im = ImagingNewPrologue(mode, xsize, ysize, depth, bands);
|
||||
if (!im) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -514,13 +527,13 @@ ImagingNewInternal(const char *mode, int xsize, int ysize, int dirty) {
|
|||
}
|
||||
|
||||
Imaging
|
||||
ImagingNew(const char *mode, int xsize, int ysize) {
|
||||
return ImagingNewInternal(mode, xsize, ysize, 0);
|
||||
ImagingNew(const char *mode, int xsize, int ysize, int depth, int bands) {
|
||||
return ImagingNewInternal(mode, xsize, ysize, depth, bands, 0);
|
||||
}
|
||||
|
||||
Imaging
|
||||
ImagingNewDirty(const char *mode, int xsize, int ysize) {
|
||||
return ImagingNewInternal(mode, xsize, ysize, 1);
|
||||
return ImagingNewInternal(mode, xsize, ysize, -1, -1, 1);
|
||||
}
|
||||
|
||||
Imaging
|
||||
|
@ -531,7 +544,7 @@ ImagingNewBlock(const char *mode, int xsize, int ysize) {
|
|||
return (Imaging)ImagingError_ValueError("bad image size");
|
||||
}
|
||||
|
||||
im = ImagingNewPrologue(mode, xsize, ysize);
|
||||
im = ImagingNewPrologue(mode, xsize, ysize, -1, -1);
|
||||
if (!im) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -119,7 +119,8 @@ PyImaging_MapBuffer(PyObject *self, PyObject *args) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
im = ImagingNewPrologueSubtype(mode, xsize, ysize, sizeof(ImagingBufferInstance));
|
||||
im = ImagingNewPrologueSubtype(
|
||||
mode, xsize, ysize, -1, -1, sizeof(ImagingBufferInstance));
|
||||
if (!im) {
|
||||
PyBuffer_Release(&view);
|
||||
return NULL;
|
||||
|
|
Loading…
Reference in New Issue
Block a user