|
29 | 29 | rather than in the high-level interface.)
|
30 | 30 | """
|
31 | 31 |
|
| 32 | +from __future__ import division |
32 | 33 | from ctypes import *
|
33 | 34 | from itertools import count
|
34 | 35 | import PIL.Image
|
@@ -63,16 +64,49 @@ def _load_image(buf, size):
|
63 | 64 | return PIL.Image.frombuffer('RGBA', size, buf, 'raw', 'RGBA', 0, 1)
|
64 | 65 | except ImportError:
|
65 | 66 | def _load_image(buf, size):
|
66 |
| - '''buf can be a string, but should be a ctypes buffer to avoid an |
67 |
| - extra copy in the caller.''' |
68 |
| - # First reorder the bytes in a pixel from native-endian aRGB to |
69 |
| - # big-endian RGBa to work around limitations in RGBa loader |
70 |
| - rawmode = (sys.byteorder == 'little') and 'BGRA' or 'ARGB' |
71 |
| - buf = PIL.Image.frombuffer('RGBA', size, buf, 'raw', rawmode, 0, 1) |
72 |
| - # Image.tobytes() is named tostring() in Pillow 1.x and PIL |
73 |
| - buf = (getattr(buf, 'tobytes', None) or buf.tostring)() |
74 |
| - # Now load the image as RGBA, undoing premultiplication |
75 |
| - return PIL.Image.frombuffer('RGBA', size, buf, 'raw', 'RGBa', 0, 1) |
| 67 | + '''buf must be a buffer.''' |
| 68 | + |
| 69 | + # Load entire buffer at once if possible |
| 70 | + MAX_PIXELS_PER_LOAD = (1 << 29) - 1 |
| 71 | + # Otherwise, use chunks smaller than the maximum to reduce memory |
| 72 | + # requirements |
| 73 | + PIXELS_PER_LOAD = 1 << 26 |
| 74 | + |
| 75 | + def do_load(buf, size): |
| 76 | + '''buf can be a string, but should be a ctypes buffer to avoid an |
| 77 | + extra copy in the caller.''' |
| 78 | + # First reorder the bytes in a pixel from native-endian aRGB to |
| 79 | + # big-endian RGBa to work around limitations in RGBa loader |
| 80 | + rawmode = (sys.byteorder == 'little') and 'BGRA' or 'ARGB' |
| 81 | + buf = PIL.Image.frombuffer('RGBA', size, buf, 'raw', rawmode, 0, 1) |
| 82 | + # Image.tobytes() is named tostring() in Pillow 1.x and PIL |
| 83 | + buf = (getattr(buf, 'tobytes', None) or buf.tostring)() |
| 84 | + # Now load the image as RGBA, undoing premultiplication |
| 85 | + return PIL.Image.frombuffer('RGBA', size, buf, 'raw', 'RGBa', 0, 1) |
| 86 | + |
| 87 | + # Fast path for small buffers |
| 88 | + w, h = size |
| 89 | + if w * h <= MAX_PIXELS_PER_LOAD: |
| 90 | + return do_load(buf, size) |
| 91 | + |
| 92 | + # Load in chunks to avoid OverflowError in PIL.Image.frombuffer() |
| 93 | + # https://github.com/python-pillow/Pillow/issues/1475 |
| 94 | + if w > PIXELS_PER_LOAD: |
| 95 | + # We could support this, but it seems like overkill |
| 96 | + raise ValueError('Width %d is too large (maximum %d)' % |
| 97 | + (w, PIXELS_PER_LOAD)) |
| 98 | + rows_per_load = PIXELS_PER_LOAD // w |
| 99 | + img = PIL.Image.new('RGBA', (w, h)) |
| 100 | + for y in range(0, h, rows_per_load): |
| 101 | + rows = min(h - y, rows_per_load) |
| 102 | + if sys.version[0] == '2': |
| 103 | + chunk = buffer(buf, 4 * y * w, 4 * rows * w) |
| 104 | + else: |
| 105 | + # PIL.Image.frombuffer() won't take a memoryview or |
| 106 | + # bytearray, so we can't avoid copying |
| 107 | + chunk = memoryview(buf)[y * w:(y + rows) * w].tobytes() |
| 108 | + img.paste(do_load(chunk, (w, rows)), (0, y)) |
| 109 | + return img |
76 | 110 |
|
77 | 111 | class OpenSlideError(Exception):
|
78 | 112 | """An error produced by the OpenSlide library.
|
|
0 commit comments