mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-07-03 11:23:05 +03:00
Merge pull request #5224 from radarhere/mapper
This commit is contained in:
commit
f15f573e51
|
@ -4,10 +4,6 @@ import pytest
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import is_win32
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.skipif(is_win32(), reason="Win32 does not call map_buffer")
|
|
||||||
|
|
||||||
|
|
||||||
def test_overflow():
|
def test_overflow():
|
||||||
# There is the potential to overflow comparisons in map.c
|
# There is the potential to overflow comparisons in map.c
|
||||||
|
@ -27,6 +23,13 @@ def test_overflow():
|
||||||
Image.MAX_IMAGE_PIXELS = max_pixels
|
Image.MAX_IMAGE_PIXELS = max_pixels
|
||||||
|
|
||||||
|
|
||||||
|
def test_tobytes():
|
||||||
|
# Previously raised an access violation on Windows
|
||||||
|
with Image.open("Tests/images/l2rgb_read.bmp") as im:
|
||||||
|
with pytest.raises((ValueError, MemoryError, OSError)):
|
||||||
|
im.tobytes()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="Requires 64-bit system")
|
@pytest.mark.skipif(sys.maxsize <= 2 ** 32, reason="Requires 64-bit system")
|
||||||
def test_ysize():
|
def test_ysize():
|
||||||
numpy = pytest.importorskip("numpy", reason="NumPy not installed")
|
numpy = pytest.importorskip("numpy", reason="NumPy not installed")
|
||||||
|
|
|
@ -192,21 +192,11 @@ class ImageFile(Image.Image):
|
||||||
and args[0] in Image._MAPMODES
|
and args[0] in Image._MAPMODES
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
if hasattr(Image.core, "map"):
|
|
||||||
# use built-in mapper WIN32 only
|
|
||||||
self.map = Image.core.map(self.filename)
|
|
||||||
self.map.seek(offset)
|
|
||||||
self.im = self.map.readimage(
|
|
||||||
self.mode, self.size, args[1], args[2]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# use mmap, if possible
|
# use mmap, if possible
|
||||||
import mmap
|
import mmap
|
||||||
|
|
||||||
with open(self.filename) as fp:
|
with open(self.filename) as fp:
|
||||||
self.map = mmap.mmap(
|
self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
|
||||||
fp.fileno(), 0, access=mmap.ACCESS_READ
|
|
||||||
)
|
|
||||||
self.im = Image.core.map_buffer(
|
self.im = Image.core.map_buffer(
|
||||||
self.map, self.size, decoder_name, offset, args
|
self.map, self.size, decoder_name, offset, args
|
||||||
)
|
)
|
||||||
|
|
|
@ -3973,8 +3973,6 @@ PyPath_Create(ImagingObject *self, PyObject *args);
|
||||||
extern PyObject *
|
extern PyObject *
|
||||||
PyOutline_Create(ImagingObject *self, PyObject *args);
|
PyOutline_Create(ImagingObject *self, PyObject *args);
|
||||||
|
|
||||||
extern PyObject *
|
|
||||||
PyImaging_Mapper(PyObject *self, PyObject *args);
|
|
||||||
extern PyObject *
|
extern PyObject *
|
||||||
PyImaging_MapBuffer(PyObject *self, PyObject *args);
|
PyImaging_MapBuffer(PyObject *self, PyObject *args);
|
||||||
|
|
||||||
|
@ -4030,9 +4028,6 @@ static PyMethodDef functions[] = {
|
||||||
|
|
||||||
/* Memory mapping */
|
/* Memory mapping */
|
||||||
#ifdef WITH_MAPPING
|
#ifdef WITH_MAPPING
|
||||||
#ifdef _WIN32
|
|
||||||
{"map", (PyCFunction)PyImaging_Mapper, 1},
|
|
||||||
#endif
|
|
||||||
{"map_buffer", (PyCFunction)PyImaging_MapBuffer, 1},
|
{"map_buffer", (PyCFunction)PyImaging_MapBuffer, 1},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
260
src/map.c
260
src/map.c
|
@ -28,269 +28,9 @@ PyImaging_CheckBuffer(PyObject *buffer);
|
||||||
extern int
|
extern int
|
||||||
PyImaging_GetBuffer(PyObject *buffer, Py_buffer *view);
|
PyImaging_GetBuffer(PyObject *buffer, Py_buffer *view);
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
|
||||||
/* Standard mapper */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PyObject_HEAD char *base;
|
|
||||||
int size;
|
|
||||||
int offset;
|
|
||||||
#ifdef _WIN32
|
|
||||||
HANDLE hFile;
|
|
||||||
HANDLE hMap;
|
|
||||||
#endif
|
|
||||||
} ImagingMapperObject;
|
|
||||||
|
|
||||||
static PyTypeObject ImagingMapperType;
|
|
||||||
|
|
||||||
ImagingMapperObject *
|
|
||||||
PyImaging_MapperNew(const char *filename, int readonly) {
|
|
||||||
ImagingMapperObject *mapper;
|
|
||||||
|
|
||||||
if (PyType_Ready(&ImagingMapperType) < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapper = PyObject_New(ImagingMapperObject, &ImagingMapperType);
|
|
||||||
if (mapper == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapper->base = NULL;
|
|
||||||
mapper->size = mapper->offset = 0;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
mapper->hFile = (HANDLE)-1;
|
|
||||||
mapper->hMap = (HANDLE)-1;
|
|
||||||
|
|
||||||
/* FIXME: currently supports readonly mappings only */
|
|
||||||
mapper->hFile = CreateFile(
|
|
||||||
filename,
|
|
||||||
GENERIC_READ,
|
|
||||||
FILE_SHARE_READ,
|
|
||||||
NULL,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL);
|
|
||||||
if (mapper->hFile == (HANDLE)-1) {
|
|
||||||
PyErr_SetString(PyExc_OSError, "cannot open file");
|
|
||||||
Py_DECREF(mapper);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapper->hMap = CreateFileMapping(mapper->hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
||||||
if (mapper->hMap == (HANDLE)-1) {
|
|
||||||
CloseHandle(mapper->hFile);
|
|
||||||
PyErr_SetString(PyExc_OSError, "cannot map file");
|
|
||||||
Py_DECREF(mapper);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapper->base = (char *)MapViewOfFile(mapper->hMap, FILE_MAP_READ, 0, 0, 0);
|
|
||||||
|
|
||||||
mapper->size = GetFileSize(mapper->hFile, 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return mapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
mapping_dealloc(ImagingMapperObject *mapper) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (mapper->base != 0) {
|
|
||||||
UnmapViewOfFile(mapper->base);
|
|
||||||
}
|
|
||||||
if (mapper->hMap != (HANDLE)-1) {
|
|
||||||
CloseHandle(mapper->hMap);
|
|
||||||
}
|
|
||||||
if (mapper->hFile != (HANDLE)-1) {
|
|
||||||
CloseHandle(mapper->hFile);
|
|
||||||
}
|
|
||||||
mapper->base = 0;
|
|
||||||
mapper->hMap = mapper->hFile = (HANDLE)-1;
|
|
||||||
#endif
|
|
||||||
PyObject_Del(mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
|
||||||
/* standard file operations */
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
mapping_read(ImagingMapperObject *mapper, PyObject *args) {
|
|
||||||
PyObject *buf;
|
|
||||||
|
|
||||||
int size = -1;
|
|
||||||
if (!PyArg_ParseTuple(args, "|i", &size)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check size */
|
|
||||||
if (size < 0 || mapper->offset + size > mapper->size) {
|
|
||||||
size = mapper->size - mapper->offset;
|
|
||||||
}
|
|
||||||
if (size < 0) {
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = PyBytes_FromStringAndSize(NULL, size);
|
|
||||||
if (!buf) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size > 0) {
|
|
||||||
memcpy(PyBytes_AsString(buf), mapper->base + mapper->offset, size);
|
|
||||||
mapper->offset += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
mapping_seek(ImagingMapperObject *mapper, PyObject *args) {
|
|
||||||
int offset;
|
|
||||||
int whence = 0;
|
|
||||||
if (!PyArg_ParseTuple(args, "i|i", &offset, &whence)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (whence) {
|
|
||||||
case 0: /* SEEK_SET */
|
|
||||||
mapper->offset = offset;
|
|
||||||
break;
|
|
||||||
case 1: /* SEEK_CUR */
|
|
||||||
mapper->offset += offset;
|
|
||||||
break;
|
|
||||||
case 2: /* SEEK_END */
|
|
||||||
mapper->offset = mapper->size + offset;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* FIXME: raise ValueError? */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_INCREF(Py_None);
|
|
||||||
return Py_None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
|
||||||
/* map entire image */
|
|
||||||
|
|
||||||
extern PyObject *
|
extern PyObject *
|
||||||
PyImagingNew(Imaging im);
|
PyImagingNew(Imaging im);
|
||||||
|
|
||||||
static void
|
|
||||||
ImagingDestroyMap(Imaging im) {
|
|
||||||
return; /* nothing to do! */
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
mapping_readimage(ImagingMapperObject *mapper, PyObject *args) {
|
|
||||||
int y, size;
|
|
||||||
Imaging im;
|
|
||||||
|
|
||||||
char *mode;
|
|
||||||
int xsize;
|
|
||||||
int ysize;
|
|
||||||
int stride;
|
|
||||||
int orientation;
|
|
||||||
if (!PyArg_ParseTuple(
|
|
||||||
args, "s(ii)ii", &mode, &xsize, &ysize, &stride, &orientation)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stride <= 0) {
|
|
||||||
/* FIXME: maybe we should call ImagingNewPrologue instead */
|
|
||||||
if (!strcmp(mode, "L") || !strcmp(mode, "P")) {
|
|
||||||
stride = xsize;
|
|
||||||
} else if (!strcmp(mode, "I;16") || !strcmp(mode, "I;16B")) {
|
|
||||||
stride = xsize * 2;
|
|
||||||
} else {
|
|
||||||
stride = xsize * 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size = ysize * stride;
|
|
||||||
|
|
||||||
if (mapper->offset + size > mapper->size) {
|
|
||||||
PyErr_SetString(PyExc_OSError, "image file truncated");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
im = ImagingNewPrologue(mode, xsize, ysize);
|
|
||||||
if (!im) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* setup file pointers */
|
|
||||||
if (orientation > 0) {
|
|
||||||
for (y = 0; y < ysize; y++) {
|
|
||||||
im->image[y] = mapper->base + mapper->offset + y * stride;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (y = 0; y < ysize; y++) {
|
|
||||||
im->image[ysize - y - 1] = mapper->base + mapper->offset + y * stride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
im->destroy = ImagingDestroyMap;
|
|
||||||
|
|
||||||
mapper->offset += size;
|
|
||||||
|
|
||||||
return PyImagingNew(im);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct PyMethodDef methods[] = {
|
|
||||||
/* standard file interface */
|
|
||||||
{"read", (PyCFunction)mapping_read, 1},
|
|
||||||
{"seek", (PyCFunction)mapping_seek, 1},
|
|
||||||
/* extensions */
|
|
||||||
{"readimage", (PyCFunction)mapping_readimage, 1},
|
|
||||||
{NULL, NULL} /* sentinel */
|
|
||||||
};
|
|
||||||
|
|
||||||
static PyTypeObject ImagingMapperType = {
|
|
||||||
PyVarObject_HEAD_INIT(NULL, 0) "ImagingMapper", /*tp_name*/
|
|
||||||
sizeof(ImagingMapperObject), /*tp_size*/
|
|
||||||
0, /*tp_itemsize*/
|
|
||||||
/* methods */
|
|
||||||
(destructor)mapping_dealloc, /*tp_dealloc*/
|
|
||||||
0, /*tp_print*/
|
|
||||||
0, /*tp_getattr*/
|
|
||||||
0, /*tp_setattr*/
|
|
||||||
0, /*tp_compare*/
|
|
||||||
0, /*tp_repr*/
|
|
||||||
0, /*tp_as_number */
|
|
||||||
0, /*tp_as_sequence */
|
|
||||||
0, /*tp_as_mapping */
|
|
||||||
0, /*tp_hash*/
|
|
||||||
0, /*tp_call*/
|
|
||||||
0, /*tp_str*/
|
|
||||||
0, /*tp_getattro*/
|
|
||||||
0, /*tp_setattro*/
|
|
||||||
0, /*tp_as_buffer*/
|
|
||||||
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
||||||
0, /*tp_doc*/
|
|
||||||
0, /*tp_traverse*/
|
|
||||||
0, /*tp_clear*/
|
|
||||||
0, /*tp_richcompare*/
|
|
||||||
0, /*tp_weaklistoffset*/
|
|
||||||
0, /*tp_iter*/
|
|
||||||
0, /*tp_iternext*/
|
|
||||||
methods, /*tp_methods*/
|
|
||||||
0, /*tp_members*/
|
|
||||||
0, /*tp_getset*/
|
|
||||||
};
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
PyImaging_Mapper(PyObject *self, PyObject *args) {
|
|
||||||
char *filename;
|
|
||||||
if (!PyArg_ParseTuple(args, "s", &filename)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (PyObject *)PyImaging_MapperNew(filename, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------- */
|
||||||
/* Buffer mapper */
|
/* Buffer mapper */
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user