Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Pr #2577 + tests #2743

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions PIL/ImImagePlugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ def tell(self):
"I;16": ("L 16", "I;16"),
"I;16L": ("L 16L", "I;16L"),
"I;16B": ("L 16B", "I;16B"),
"I;16S": ("L 16S", "I;16S"),
"F": ("L 32F", "F;32F"),
"RGB": ("RGB", "RGB;L"),
"RGBA": ("RGBA", "RGBA;L"),
Expand Down
2 changes: 1 addition & 1 deletion PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ def isImageType(t):
"LAB": ("RGB", "L", ("L", "A", "B")),
"HSV": ("RGB", "L", ("H", "S", "V")),

# Experimental modes include I;16, I;16L, I;16B, RGBa, BGR;15, and
# Experimental modes include I;16, I;16L, I;16B, I;16S, RGBa, BGR;15, and
# BGR;24. Use these modes only if you know exactly what you're
# doing...

Expand Down
1 change: 1 addition & 0 deletions PIL/ImageMode.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def getmode(mode):
modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L")
modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L")
modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L")
modes["I;16S"] = ModeDescriptor("I;16S", "I", "L", "L")
# set global mode cache atomically
_modes = modes
return _modes[mode]
Binary file added Tests/images/16bit.s.tif
Binary file not shown.
5 changes: 5 additions & 0 deletions Tests/test_file_tiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,11 @@ def test_big_endian(self):
self.assertEqual(b[0], b'\x01')
self.assertEqual(b[1], b'\xe0')

def test_16bit_s(self):
im = Image.open('Tests/images/16bit.s.tif')
im.load()
self.assertEqual(im.mode, 'I;16S')

def test_12bit_rawmode(self):
""" Are we generating the same interpretation
of the image as Imagemagick is? """
Expand Down
48 changes: 47 additions & 1 deletion Tests/test_mode_i16.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from helper import unittest, PillowTestCase, hopper

from PIL import Image
import struct

HAS_PYACCESS = False
try:
from PIL import PyAccess
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HAS_PYACCESS = True here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. this.

except: pass

class TestModeI16(PillowTestCase):

Expand Down Expand Up @@ -79,6 +84,7 @@ def basic(mode):
basic("I;16")
basic("I;16B")
basic("I;16L")
basic("I;16S")

basic("I")

Expand All @@ -92,6 +98,7 @@ def tobytes(mode):
self.assertEqual(tobytes("L"), b"\x01")
self.assertEqual(tobytes("I;16"), b"\x01\x00")
self.assertEqual(tobytes("I;16B"), b"\x00\x01")
self.assertEqual(tobytes("I;16S"), b"\x01\x00")
self.assertEqual(tobytes("I"), b"\x01\x00\x00\x00"[::order])

def test_convert(self):
Expand All @@ -106,6 +113,45 @@ def test_convert(self):
self.verify(im.convert("I;16B").convert("L"))
self.verify(im.convert("I;16B").convert("I"))


self.verify(im.convert("I;16S"))
self.verify(im.convert("I;16S").convert("L"))
self.verify(im.convert("I;16S").convert("I"))


def _test_i16s(self, pixels, data, rawmode):
ct = len(pixels)
im = Image.frombytes('I;16S',
(ct,1),
data,
'raw',
rawmode)

self.assertEqual(im.tobytes('raw', rawmode), data)


def _test_access(im, pixels):
access = im.load()
if HAS_PYACCESS:
py_access = PyAccess.new(im, im.readonly)
for ix, val in enumerate(pixels):
self.assertEqual(access[(ix, 0)], val)
if HAS_PYACCESS:
self.assertEqual(py_access[(ix, 0)], val)

_test_access(im, pixels)
_test_access(im.convert('I'), pixels) # lossless
_test_access(im.convert('F'), pixels) # lossless
_test_access(im.convert('L'), (0,0,0,0,1,128,255,255)) #lossy

def test_i16s(self):
# LE, base Rawmode:
pixels = (-2**15, -2**7, -1, 0, 1, 128, 255, 2**15-1)
ct = len(pixels)
data = struct.pack("<%dh"%ct, *pixels)
self._test_i16s(pixels, data, 'I;16S')
data = struct.pack(">%dh"%ct, *pixels)
self._test_i16s(pixels, data, 'I;16BS')


if __name__ == '__main__':
unittest.main()
14 changes: 9 additions & 5 deletions _imaging.c
Original file line number Diff line number Diff line change
Expand Up @@ -440,10 +440,11 @@ static inline PyObject*
getpixel(Imaging im, ImagingAccess access, int x, int y)
{
union {
UINT8 b[4];
UINT16 h;
INT32 i;
FLOAT32 f;
UINT8 b[4];
UINT16 H;
INT16 h;
INT32 i;
FLOAT32 f;
} pixel;

if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
Expand Down Expand Up @@ -471,8 +472,11 @@ getpixel(Imaging im, ImagingAccess access, int x, int y)
case IMAGING_TYPE_FLOAT32:
return PyFloat_FromDouble(pixel.f);
case IMAGING_TYPE_SPECIAL:
if (strncmp(im->mode, "I;16", 4) == 0)
if (strncmp(im->mode, "I;16S", 5) == 0) {
return PyInt_FromLong(pixel.h);
} else if (strncmp(im->mode, "I;16", 4) == 0) {
return PyInt_FromLong(pixel.H);
}
break;
}

Expand Down
1 change: 1 addition & 0 deletions libImaging/Access.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ ImagingAccessInit()
ADD("I;16", line_16, get_pixel_16L, put_pixel_16L);
ADD("I;16L", line_16, get_pixel_16L, put_pixel_16L);
ADD("I;16B", line_16, get_pixel_16B, put_pixel_16B);
ADD("I;16S", line_16, get_pixel_16L, put_pixel_16L);
ADD("I;32L", line_32, get_pixel_32L, put_pixel_32L);
ADD("I;32B", line_32, get_pixel_32B, put_pixel_32B);
ADD("F", line_32, get_pixel_32, put_pixel_32);
Expand Down
60 changes: 59 additions & 1 deletion libImaging/Convert.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

#ifndef round
double round(double x) {
return floor(x+0.5);
return floor(x+0.5);
}
#endif

Expand Down Expand Up @@ -694,6 +694,17 @@ I_I16B(UINT8* out, const UINT8* in_, int xsize)
}
}

static void
I_I16S(UINT8* out, const UINT8* in_, int xsize)
{
int x, v;
INT32* in = (INT32*) in_;
for (x = 0; x < xsize; x++, in++) {
v = CLIP16(*in);
*out++ = (UINT8) v;
*out++ = (UINT8) (v >> 8);
}
}

static void
I16L_I(UINT8* out_, const UINT8* in, int xsize)
Expand All @@ -714,6 +725,15 @@ I16B_I(UINT8* out_, const UINT8* in, int xsize)
*out++ = in[1] + ((int) in[0] << 8);
}

static void
I16S_I(UINT8* out_, const UINT8* in, int xsize)
{
int x;
INT32* out = (INT32*) out_;
for (x = 0; x < xsize; x++, in += 2)
*out++ = (INT16)(in[0] + ((int) in[1] << 8));
}

static void
I16L_F(UINT8* out_, const UINT8* in, int xsize)
{
Expand All @@ -733,6 +753,15 @@ I16B_F(UINT8* out_, const UINT8* in, int xsize)
*out++ = (FLOAT32) (in[1] + ((int) in[0] << 8));
}

static void
I16S_F(UINT8* out_, const UINT8* in, int xsize)
{
int x;
FLOAT32* out = (FLOAT32*) out_;
for (x = 0; x < xsize; x++, in += 2)
*out++ = (FLOAT32) (INT16) (in[0] + ((int) in[1] << 8));
}

static void
L_I16L(UINT8* out, const UINT8* in, int xsize)
{
Expand All @@ -753,6 +782,16 @@ L_I16B(UINT8* out, const UINT8* in, int xsize)
}
}

static void
L_I16S(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in++) {
*out++ = *in;
*out++ = 0;
}
}

static void
I16L_L(UINT8* out, const UINT8* in, int xsize)
{
Expand All @@ -775,6 +814,19 @@ I16B_L(UINT8* out, const UINT8* in, int xsize)
*out++ = in[1];
}

static void
I16S_L(UINT8* out, const UINT8* in, int xsize)
{
int x;
for (x = 0; x < xsize; x++, in += 2)
if (in[1] & 0x80)
*out++ = 0; /* Negative -> 0 */
else if (in[1] != 0)
*out++ = 255; /* Greater than 255 -> 255 */
else
*out++ = in[0];
}

static struct {
const char* from;
const char* to;
Expand Down Expand Up @@ -873,9 +925,15 @@ static struct {
{ "L", "I;16B", L_I16B },
{ "I;16B", "L", I16B_L },

{ "I", "I;16S", I_I16S },
{ "I;16S", "I", I16S_I },
{ "L", "I;16S", L_I16S },
{ "I;16S", "L", I16S_L },

{ "I;16", "F", I16L_F },
{ "I;16L", "F", I16L_F },
{ "I;16B", "F", I16B_F },
{ "I;16S", "F", I16S_F },

{ NULL }
};
Expand Down
12 changes: 12 additions & 0 deletions libImaging/Pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,16 @@ packI16N_I16(UINT8* out, const UINT8* in, int pixels){
}
}

static void
packI16S_I16BS(UINT8* out, const UINT8* in, int pixels){
int i;
UINT8* tmp = (UINT8*) in;
for (i = 0; i < pixels; i++) {
C16S;
out += 2; tmp += 2;
}
}


static void
packI32S(UINT8* out, const UINT8* in, int pixels)
Expand Down Expand Up @@ -614,6 +624,8 @@ static struct {
{"I;16", "I;16", 16, copy2},
{"I;16B", "I;16B", 16, copy2},
{"I;16L", "I;16L", 16, copy2},
{"I;16S", "I;16S", 16, copy2},
{"I;16S", "I;16BS", 16, packI16S_I16BS},
{"I;16", "I;16N", 16, packI16N_I16}, // LibTiff native->image endian.
{"I;16L", "I;16N", 16, packI16N_I16},
{"I;16B", "I;16N", 16, packI16N_I16B},
Expand Down
3 changes: 2 additions & 1 deletion libImaging/Storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size)
im->type = IMAGING_TYPE_INT32;

} else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 \
|| strcmp(mode, "I;16B") == 0 || strcmp(mode, "I;16N") == 0) {
|| strcmp(mode, "I;16B") == 0 || strcmp(mode, "I;16N") == 0 \
|| strcmp(mode, "I;16S") == 0) {
/* EXPERIMENTAL */
/* 16-bit raw integer images */
im->bands = 1;
Expand Down
12 changes: 12 additions & 0 deletions libImaging/Unpack.c
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,16 @@ unpackI16N_I16(UINT8* out, const UINT8* in, int pixels){
}
}

static void
unpackI16BS_I16S(UINT8* out, const UINT8* in, int pixels){
int i;
UINT8* tmp = (UINT8*) out;
for (i = 0; i < pixels; i++) {
C16S;
in += 2; tmp += 2;
}
}

static void
unpackI12_I16(UINT8* out, const UINT8* in, int pixels){
/* Fillorder 1/MSB -> LittleEndian, for 12bit integer greyscale tiffs.
Expand Down Expand Up @@ -1406,6 +1416,8 @@ static struct {
{"I;16", "I;16", 16, copy2},
{"I;16B", "I;16B", 16, copy2},
{"I;16L", "I;16L", 16, copy2},
{"I;16S", "I;16S", 16, copy2},
{"I;16S", "I;16BS", 16, unpackI16BS_I16S},

{"I;16", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian.
{"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian.
Expand Down