mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-11 04:07:21 +03:00
Merge branch 'master' of https://github.com/etienned/Pillow into etienned-master
This commit is contained in:
commit
b03abf2e58
|
@ -36,6 +36,7 @@ __version__ = "0.6"
|
||||||
|
|
||||||
import array, struct
|
import array, struct
|
||||||
from PIL import Image, ImageFile, _binary
|
from PIL import Image, ImageFile, _binary
|
||||||
|
from JpegPresets import presets
|
||||||
|
|
||||||
i8 = _binary.i8
|
i8 = _binary.i8
|
||||||
o8 = _binary.o8
|
o8 = _binary.o8
|
||||||
|
@ -416,6 +417,31 @@ RAWMODE = {
|
||||||
"YCbCr": "YCbCr",
|
"YCbCr": "YCbCr",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zigzag_index = ( 0, 1, 5, 6, 14, 15, 27, 28,
|
||||||
|
2, 4, 7, 13, 16, 26, 29, 42,
|
||||||
|
3, 8, 12, 17, 25, 30, 41, 43,
|
||||||
|
9, 11, 18, 24, 31, 40, 44, 53,
|
||||||
|
10, 19, 23, 32, 39, 45, 52, 54,
|
||||||
|
20, 22, 33, 38, 46, 51, 55, 60,
|
||||||
|
21, 34, 37, 47, 50, 56, 59, 61,
|
||||||
|
35, 36, 48, 49, 57, 58, 62, 63)
|
||||||
|
|
||||||
|
samplings = {
|
||||||
|
(1, 1, 1, 1, 1, 1): 0,
|
||||||
|
(2, 1, 1, 1, 1, 1): 1,
|
||||||
|
(2, 2, 1, 1, 1, 1): 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
def convert_dict_qtables(qtables):
|
||||||
|
qtables = [qtables[key] for key in xrange(len(qtables)) if qtables.has_key(key)]
|
||||||
|
for idx, table in enumerate(qtables):
|
||||||
|
qtables[idx] = [table[i] for i in zigzag_index]
|
||||||
|
return qtables
|
||||||
|
|
||||||
|
def get_sampling(im):
|
||||||
|
sampling = im.layer[0][1:3] + im.layer[1][1:3] + im.layer[2][1:3]
|
||||||
|
return samplings.get(sampling, -1)
|
||||||
|
|
||||||
def _save(im, fp, filename):
|
def _save(im, fp, filename):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -427,13 +453,72 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
dpi = info.get("dpi", (0, 0))
|
dpi = info.get("dpi", (0, 0))
|
||||||
|
|
||||||
|
quality = info.get("quality", 0)
|
||||||
subsampling = info.get("subsampling", -1)
|
subsampling = info.get("subsampling", -1)
|
||||||
|
qtables = info.get("qtables")
|
||||||
|
|
||||||
|
if quality == "keep":
|
||||||
|
quality = 0
|
||||||
|
subsampling = "keep"
|
||||||
|
qtables = "keep"
|
||||||
|
elif quality in presets:
|
||||||
|
preset = presets[quality]
|
||||||
|
quality = 0
|
||||||
|
subsampling = preset.get('subsampling', -1)
|
||||||
|
qtables = preset.get('quantization')
|
||||||
|
elif not isinstance(quality, int):
|
||||||
|
raise ValueError("Invalid quality setting")
|
||||||
|
else:
|
||||||
|
if subsampling in presets:
|
||||||
|
subsampling = presets[subsampling].get('subsampling', -1)
|
||||||
|
if qtables in presets:
|
||||||
|
qtables = presets[qtables].get('quantization')
|
||||||
|
|
||||||
if subsampling == "4:4:4":
|
if subsampling == "4:4:4":
|
||||||
subsampling = 0
|
subsampling = 0
|
||||||
elif subsampling == "4:2:2":
|
elif subsampling == "4:2:2":
|
||||||
subsampling = 1
|
subsampling = 1
|
||||||
elif subsampling == "4:1:1":
|
elif subsampling == "4:1:1":
|
||||||
subsampling = 2
|
subsampling = 2
|
||||||
|
elif subsampling == "keep":
|
||||||
|
if im.format != "JPEG":
|
||||||
|
raise ValueError("Cannot use 'keep' when original image is not a JPEG")
|
||||||
|
subsampling = get_sampling(im)
|
||||||
|
|
||||||
|
def validate_qtables(qtables):
|
||||||
|
if qtables is None:
|
||||||
|
return qtables
|
||||||
|
if isinstance(qtables, basestring):
|
||||||
|
try:
|
||||||
|
lines = [int(num) for line in qtables.splitlines()
|
||||||
|
for num in line.split('#', 1)[0].split()]
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("Invalid quantization table")
|
||||||
|
else:
|
||||||
|
qtables = [lines[s:s+64] for s in xrange(0, len(lines), 64)]
|
||||||
|
if isinstance(qtables, (tuple, list, dict)):
|
||||||
|
if isinstance(qtables, dict):
|
||||||
|
qtables = convert_dict_qtables(qtables)
|
||||||
|
elif isinstance(qtables, tuple):
|
||||||
|
qtables = list(qtables)
|
||||||
|
if not (0 < len(qtables) < 5):
|
||||||
|
raise ValueError("None or too many quantization tables")
|
||||||
|
for idx, table in enumerate(qtables):
|
||||||
|
try:
|
||||||
|
if len(table) != 64:
|
||||||
|
raise
|
||||||
|
table = array.array('b', table)
|
||||||
|
except TypeError:
|
||||||
|
raise ValueError("Invalid quantization table")
|
||||||
|
else:
|
||||||
|
qtables[idx] = list(table)
|
||||||
|
return qtables
|
||||||
|
|
||||||
|
if qtables == "keep":
|
||||||
|
if im.format != "JPEG":
|
||||||
|
raise ValueError("Cannot use 'keep' when original image is not a JPEG")
|
||||||
|
qtables = getattr(im, "quantization", None)
|
||||||
|
qtables = validate_qtables(qtables)
|
||||||
|
|
||||||
extra = b""
|
extra = b""
|
||||||
|
|
||||||
|
@ -454,7 +539,7 @@ def _save(im, fp, filename):
|
||||||
|
|
||||||
# get keyword arguments
|
# get keyword arguments
|
||||||
im.encoderconfig = (
|
im.encoderconfig = (
|
||||||
info.get("quality", 0),
|
quality,
|
||||||
# "progressive" is the official name, but older documentation
|
# "progressive" is the official name, but older documentation
|
||||||
# says "progression"
|
# says "progression"
|
||||||
# FIXME: issue a warning if the wrong form is used (post-1.1.7)
|
# FIXME: issue a warning if the wrong form is used (post-1.1.7)
|
||||||
|
@ -464,6 +549,7 @@ def _save(im, fp, filename):
|
||||||
info.get("streamtype", 0),
|
info.get("streamtype", 0),
|
||||||
dpi[0], dpi[1],
|
dpi[0], dpi[1],
|
||||||
subsampling,
|
subsampling,
|
||||||
|
qtables,
|
||||||
extra,
|
extra,
|
||||||
info.get("exif", b"")
|
info.get("exif", b"")
|
||||||
)
|
)
|
||||||
|
|
250
PIL/JpegPresets.py
Normal file
250
PIL/JpegPresets.py
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
"""
|
||||||
|
JPEG quality settings equivalent to the Photoshop settings.
|
||||||
|
|
||||||
|
More presets can be added to the presets dict if needed.
|
||||||
|
|
||||||
|
Can be use when saving JPEG file.
|
||||||
|
|
||||||
|
To apply the preset, specify:
|
||||||
|
|
||||||
|
- quality=preset name
|
||||||
|
|
||||||
|
To apply only the quantization table:
|
||||||
|
|
||||||
|
- qtables=preset name
|
||||||
|
|
||||||
|
To apply only the subsampling setting:
|
||||||
|
|
||||||
|
- subsampling=preset name
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
im.save("image_name.jpg", quality="web_high")
|
||||||
|
|
||||||
|
|
||||||
|
Subsampling
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Subsampling is the practice of encoding images by implementing less resolution
|
||||||
|
for chroma information than for luma information.
|
||||||
|
(ref.: http://en.wikipedia.org/wiki/Chroma_subsampling)
|
||||||
|
|
||||||
|
Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and
|
||||||
|
4:1:1 (or 4:2:0?).
|
||||||
|
|
||||||
|
You can get the subsampling of a JPEG with the
|
||||||
|
`JpegImagePlugin.get_subsampling(im)` function.
|
||||||
|
|
||||||
|
|
||||||
|
Quantization tables
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
They are values use by the DCT (Discrete cosine transform) to remove
|
||||||
|
*unnecessary* information from the image (the lossy part of the compression).
|
||||||
|
(ref.: http://en.wikipedia.org/wiki/Quantization_matrix#Quantization_matrices,
|
||||||
|
http://en.wikipedia.org/wiki/JPEG#Quantization)
|
||||||
|
|
||||||
|
You can get the quantization tables of a JPEG with:
|
||||||
|
|
||||||
|
im.quantization
|
||||||
|
|
||||||
|
This will return a dict with a number of arrays. You can pass this dict directly
|
||||||
|
as the qtables argument when saving a JPEG.
|
||||||
|
|
||||||
|
The tables format between im.quantization and quantization in presets differ in
|
||||||
|
3 ways:
|
||||||
|
|
||||||
|
1. The base container of the preset is a list with sublists instead of dict.
|
||||||
|
dict[0] -> list[0], dict[1] -> list[1], ...
|
||||||
|
|
||||||
|
2. Each table in a preset is a list instead of an array.
|
||||||
|
|
||||||
|
3. The zigzag order is remove in the preset (needed by libjpeg >= 6a).
|
||||||
|
|
||||||
|
You can convert the dict format to the preset format with the
|
||||||
|
`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function.
|
||||||
|
|
||||||
|
Libjpeg ref.: http://www.jpegcameras.com/libjpeg/libjpeg-3.html
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
presets = {
|
||||||
|
'web_low': {'subsampling': 2, # "4:1:1"
|
||||||
|
'quantization': [
|
||||||
|
[20, 16, 25, 39, 50, 46, 62, 68,
|
||||||
|
16, 18, 23, 38, 38, 53, 65, 68,
|
||||||
|
25, 23, 31, 38, 53, 65, 68, 68,
|
||||||
|
39, 38, 38, 53, 65, 68, 68, 68,
|
||||||
|
50, 38, 53, 65, 68, 68, 68, 68,
|
||||||
|
46, 53, 65, 68, 68, 68, 68, 68,
|
||||||
|
62, 65, 68, 68, 68, 68, 68, 68,
|
||||||
|
68, 68, 68, 68, 68, 68, 68, 68],
|
||||||
|
[21, 25, 32, 38, 54, 68, 68, 68,
|
||||||
|
25, 28, 24, 38, 54, 68, 68, 68,
|
||||||
|
32, 24, 32, 43, 66, 68, 68, 68,
|
||||||
|
38, 38, 43, 53, 68, 68, 68, 68,
|
||||||
|
54, 54, 66, 68, 68, 68, 68, 68,
|
||||||
|
68, 68, 68, 68, 68, 68, 68, 68,
|
||||||
|
68, 68, 68, 68, 68, 68, 68, 68,
|
||||||
|
68, 68, 68, 68, 68, 68, 68, 68]
|
||||||
|
]},
|
||||||
|
|
||||||
|
'web_medium': {'subsampling': 2, # "4:1:1"
|
||||||
|
'quantization': [
|
||||||
|
[16, 11, 11, 16, 23, 27, 31, 30,
|
||||||
|
11, 12, 12, 15, 20, 23, 23, 30,
|
||||||
|
11, 12, 13, 16, 23, 26, 35, 47,
|
||||||
|
16, 15, 16, 23, 26, 37, 47, 64,
|
||||||
|
23, 20, 23, 26, 39, 51, 64, 64,
|
||||||
|
27, 23, 26, 37, 51, 64, 64, 64,
|
||||||
|
31, 23, 35, 47, 64, 64, 64, 64,
|
||||||
|
30, 30, 47, 64, 64, 64, 64, 64],
|
||||||
|
[17, 15, 17, 21, 20, 26, 38, 48,
|
||||||
|
15, 19, 18, 17, 20, 26, 35, 43,
|
||||||
|
17, 18, 20, 22, 26, 30, 46, 53,
|
||||||
|
21, 17, 22, 28, 30, 39, 53, 64,
|
||||||
|
20, 20, 26, 30, 39, 48, 64, 64,
|
||||||
|
26, 26, 30, 39, 48, 63, 64, 64,
|
||||||
|
38, 35, 46, 53, 64, 64, 64, 64,
|
||||||
|
48, 43, 53, 64, 64, 64, 64, 64]
|
||||||
|
]},
|
||||||
|
|
||||||
|
'web_high': {'subsampling': 0, # "4:4:4"
|
||||||
|
'quantization': [
|
||||||
|
[ 6, 4, 4, 6, 9, 11, 12, 16,
|
||||||
|
4, 5, 5, 6, 8, 10, 12, 12,
|
||||||
|
4, 5, 5, 6, 10, 12, 14, 19,
|
||||||
|
6, 6, 6, 11, 12, 15, 19, 28,
|
||||||
|
9, 8, 10, 12, 16, 20, 27, 31,
|
||||||
|
11, 10, 12, 15, 20, 27, 31, 31,
|
||||||
|
12, 12, 14, 19, 27, 31, 31, 31,
|
||||||
|
16, 12, 19, 28, 31, 31, 31, 31],
|
||||||
|
[ 7, 7, 13, 24, 26, 31, 31, 31,
|
||||||
|
7, 12, 16, 21, 31, 31, 31, 31,
|
||||||
|
13, 16, 17, 31, 31, 31, 31, 31,
|
||||||
|
24, 21, 31, 31, 31, 31, 31, 31,
|
||||||
|
26, 31, 31, 31, 31, 31, 31, 31,
|
||||||
|
31, 31, 31, 31, 31, 31, 31, 31,
|
||||||
|
31, 31, 31, 31, 31, 31, 31, 31,
|
||||||
|
31, 31, 31, 31, 31, 31, 31, 31]
|
||||||
|
]},
|
||||||
|
|
||||||
|
'web_very_high': {'subsampling': 0, # "4:4:4"
|
||||||
|
'quantization': [
|
||||||
|
[ 2, 2, 2, 2, 3, 4, 5, 6,
|
||||||
|
2, 2, 2, 2, 3, 4, 5, 6,
|
||||||
|
2, 2, 2, 2, 4, 5, 7, 9,
|
||||||
|
2, 2, 2, 4, 5, 7, 9, 12,
|
||||||
|
3, 3, 4, 5, 8, 10, 12, 12,
|
||||||
|
4, 4, 5, 7, 10, 12, 12, 12,
|
||||||
|
5, 5, 7, 9, 12, 12, 12, 12,
|
||||||
|
6, 6, 9, 12, 12, 12, 12, 12],
|
||||||
|
[ 3, 3, 5, 9, 13, 15, 15, 15,
|
||||||
|
3, 4, 6, 11, 14, 12, 12, 12,
|
||||||
|
5, 6, 9, 14, 12, 12, 12, 12,
|
||||||
|
9, 11, 14, 12, 12, 12, 12, 12,
|
||||||
|
13, 14, 12, 12, 12, 12, 12, 12,
|
||||||
|
15, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
15, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
15, 12, 12, 12, 12, 12, 12, 12]
|
||||||
|
]},
|
||||||
|
|
||||||
|
'web_maximum': {'subsampling': 0, # "4:4:4"
|
||||||
|
'quantization': [
|
||||||
|
[ 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 2,
|
||||||
|
1, 1, 1, 1, 1, 1, 2, 2,
|
||||||
|
1, 1, 1, 1, 1, 2, 2, 3,
|
||||||
|
1, 1, 1, 1, 2, 2, 3, 3,
|
||||||
|
1, 1, 1, 2, 2, 3, 3, 3,
|
||||||
|
1, 1, 2, 2, 3, 3, 3, 3],
|
||||||
|
[ 1, 1, 1, 2, 2, 3, 3, 3,
|
||||||
|
1, 1, 1, 2, 3, 3, 3, 3,
|
||||||
|
1, 1, 1, 3, 3, 3, 3, 3,
|
||||||
|
2, 2, 3, 3, 3, 3, 3, 3,
|
||||||
|
2, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3]
|
||||||
|
]},
|
||||||
|
|
||||||
|
'low': {'subsampling': 2, # "4:1:1"
|
||||||
|
'quantization': [
|
||||||
|
[18, 14, 14, 21, 30, 35, 34, 17,
|
||||||
|
14, 16, 16, 19, 26, 23, 12, 12,
|
||||||
|
14, 16, 17, 21, 23, 12, 12, 12,
|
||||||
|
21, 19, 21, 23, 12, 12, 12, 12,
|
||||||
|
30, 26, 23, 12, 12, 12, 12, 12,
|
||||||
|
35, 23, 12, 12, 12, 12, 12, 12,
|
||||||
|
34, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
17, 12, 12, 12, 12, 12, 12, 12],
|
||||||
|
[20, 19, 22, 27, 20, 20, 17, 17,
|
||||||
|
19, 25, 23, 14, 14, 12, 12, 12,
|
||||||
|
22, 23, 14, 14, 12, 12, 12, 12,
|
||||||
|
27, 14, 14, 12, 12, 12, 12, 12,
|
||||||
|
20, 14, 12, 12, 12, 12, 12, 12,
|
||||||
|
20, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
17, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
17, 12, 12, 12, 12, 12, 12, 12]
|
||||||
|
]},
|
||||||
|
'medium': {'subsampling': 2, # "4:1:1"
|
||||||
|
'quantization': [
|
||||||
|
[12, 8, 8, 12, 17, 21, 24, 17,
|
||||||
|
8, 9, 9, 11, 15, 19, 12, 12,
|
||||||
|
8, 9, 10, 12, 19, 12, 12, 12,
|
||||||
|
12, 11, 12, 21, 12, 12, 12, 12,
|
||||||
|
17, 15, 19, 12, 12, 12, 12, 12,
|
||||||
|
21, 19, 12, 12, 12, 12, 12, 12,
|
||||||
|
24, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
17, 12, 12, 12, 12, 12, 12, 12],
|
||||||
|
[13, 11, 13, 16, 20, 20, 17, 17,
|
||||||
|
11, 14, 14, 14, 14, 12, 12, 12,
|
||||||
|
13, 14, 14, 14, 12, 12, 12, 12,
|
||||||
|
16, 14, 14, 12, 12, 12, 12, 12,
|
||||||
|
20, 14, 12, 12, 12, 12, 12, 12,
|
||||||
|
20, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
17, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
17, 12, 12, 12, 12, 12, 12, 12]
|
||||||
|
]},
|
||||||
|
|
||||||
|
'high': {'subsampling': 0, # "4:4:4"
|
||||||
|
'quantization': [
|
||||||
|
[ 6, 4, 4, 6, 9, 11, 12, 16,
|
||||||
|
4, 5, 5, 6, 8, 10, 12, 12,
|
||||||
|
4, 5, 5, 6, 10, 12, 12, 12,
|
||||||
|
6, 6, 6, 11, 12, 12, 12, 12,
|
||||||
|
9, 8, 10, 12, 12, 12, 12, 12,
|
||||||
|
11, 10, 12, 12, 12, 12, 12, 12,
|
||||||
|
12, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
16, 12, 12, 12, 12, 12, 12, 12],
|
||||||
|
[ 7, 7, 13, 24, 20, 20, 17, 17,
|
||||||
|
7, 12, 16, 14, 14, 12, 12, 12,
|
||||||
|
13, 16, 14, 14, 12, 12, 12, 12,
|
||||||
|
24, 14, 14, 12, 12, 12, 12, 12,
|
||||||
|
20, 14, 12, 12, 12, 12, 12, 12,
|
||||||
|
20, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
17, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
17, 12, 12, 12, 12, 12, 12, 12]
|
||||||
|
]},
|
||||||
|
|
||||||
|
'maximum': {'subsampling': 0, # "4:4:4"
|
||||||
|
'quantization': [
|
||||||
|
[ 2, 2, 2, 2, 3, 4, 5, 6,
|
||||||
|
2, 2, 2, 2, 3, 4, 5, 6,
|
||||||
|
2, 2, 2, 2, 4, 5, 7, 9,
|
||||||
|
2, 2, 2, 4, 5, 7, 9, 12,
|
||||||
|
3, 3, 4, 5, 8, 10, 12, 12,
|
||||||
|
4, 4, 5, 7, 10, 12, 12, 12,
|
||||||
|
5, 5, 7, 9, 12, 12, 12, 12,
|
||||||
|
6, 6, 9, 12, 12, 12, 12, 12],
|
||||||
|
[ 3, 3, 5, 9, 13, 15, 15, 15,
|
||||||
|
3, 4, 6, 10, 14, 12, 12, 12,
|
||||||
|
5, 6, 9, 14, 12, 12, 12, 12,
|
||||||
|
9, 10, 14, 12, 12, 12, 12, 12,
|
||||||
|
13, 14, 12, 12, 12, 12, 12, 12,
|
||||||
|
15, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
15, 12, 12, 12, 12, 12, 12, 12,
|
||||||
|
15, 12, 12, 12, 12, 12, 12, 12]
|
||||||
|
]},
|
||||||
|
}
|
72
encode.c
72
encode.c
|
@ -506,6 +506,69 @@ PyImaging_ZipEncoderNew(PyObject* self, PyObject* args)
|
||||||
|
|
||||||
#include "Jpeg.h"
|
#include "Jpeg.h"
|
||||||
|
|
||||||
|
static unsigned int** get_qtables_arrays(PyObject* qtables) {
|
||||||
|
PyObject* tables;
|
||||||
|
PyObject* table;
|
||||||
|
PyObject* table_data;
|
||||||
|
int i, j, num_tables;
|
||||||
|
unsigned int **qarrays;
|
||||||
|
|
||||||
|
if (qtables == Py_None) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PySequence_Check(qtables)) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tables = PySequence_Fast(qtables, "expected a sequence");
|
||||||
|
num_tables = PySequence_Size(qtables);
|
||||||
|
if (num_tables < 2 || num_tables > NUM_QUANT_TBLS) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Not a valid numbers of quantization tables. Should be between 2 and 4.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
qarrays = (unsigned int**) PyMem_Malloc(num_tables * sizeof(unsigned int));
|
||||||
|
if (!qarrays) {
|
||||||
|
Py_DECREF(tables);
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (i = 0; i < num_tables; i++) {
|
||||||
|
table = PySequence_Fast_GET_ITEM(tables, i);
|
||||||
|
if (!PySequence_Check(table)) {
|
||||||
|
Py_DECREF(tables);
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (PySequence_Size(table) != DCTSIZE2) {
|
||||||
|
Py_DECREF(tables);
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Invalid quantization tables");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
table_data = PySequence_Fast(table, "expected a sequence");
|
||||||
|
qarrays[i] = (unsigned int*) PyMem_Malloc(DCTSIZE2 * sizeof(unsigned int));
|
||||||
|
if (!qarrays[i]) {
|
||||||
|
Py_DECREF(tables);
|
||||||
|
PyErr_NoMemory();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (j = 0; j < DCTSIZE2; j++) {
|
||||||
|
qarrays[i][j] = PyInt_AS_LONG(PySequence_Fast_GET_ITEM(table_data, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(tables);
|
||||||
|
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
PyMem_Free(qarrays);
|
||||||
|
qarrays = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return qarrays;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PyObject*
|
PyObject*
|
||||||
PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
|
@ -520,15 +583,17 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
||||||
int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
|
int streamtype = 0; /* 0=interchange, 1=tables only, 2=image only */
|
||||||
int xdpi = 0, ydpi = 0;
|
int xdpi = 0, ydpi = 0;
|
||||||
int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
|
int subsampling = -1; /* -1=default, 0=none, 1=medium, 2=high */
|
||||||
|
PyObject* qtables;
|
||||||
|
unsigned int **qarrays = NULL;
|
||||||
char* extra = NULL;
|
char* extra = NULL;
|
||||||
int extra_size;
|
int extra_size;
|
||||||
char* rawExif = NULL;
|
char* rawExif = NULL;
|
||||||
int rawExifLen = 0;
|
int rawExifLen = 0;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "ss|iiiiiiii"PY_ARG_BYTES_LENGTH""PY_ARG_BYTES_LENGTH,
|
if (!PyArg_ParseTuple(args, "ss|iiiiiiiiO"PY_ARG_BYTES_LENGTH""PY_ARG_BYTES_LENGTH,
|
||||||
&mode, &rawmode, &quality,
|
&mode, &rawmode, &quality,
|
||||||
&progressive, &smooth, &optimize, &streamtype,
|
&progressive, &smooth, &optimize, &streamtype,
|
||||||
&xdpi, &ydpi, &subsampling, &extra, &extra_size,
|
&xdpi, &ydpi, &subsampling, &qtables, &extra, &extra_size,
|
||||||
&rawExif, &rawExifLen))
|
&rawExif, &rawExifLen))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -539,6 +604,8 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
||||||
if (get_packer(encoder, mode, rawmode) < 0)
|
if (get_packer(encoder, mode, rawmode) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
qarrays = get_qtables_arrays(qtables);
|
||||||
|
|
||||||
if (extra && extra_size > 0) {
|
if (extra && extra_size > 0) {
|
||||||
char* p = malloc(extra_size);
|
char* p = malloc(extra_size);
|
||||||
if (!p)
|
if (!p)
|
||||||
|
@ -560,6 +627,7 @@ PyImaging_JpegEncoderNew(PyObject* self, PyObject* args)
|
||||||
encoder->encode = ImagingJpegEncode;
|
encoder->encode = ImagingJpegEncode;
|
||||||
|
|
||||||
((JPEGENCODERSTATE*)encoder->state.context)->quality = quality;
|
((JPEGENCODERSTATE*)encoder->state.context)->quality = quality;
|
||||||
|
((JPEGENCODERSTATE*)encoder->state.context)->qtables = qarrays;
|
||||||
((JPEGENCODERSTATE*)encoder->state.context)->subsampling = subsampling;
|
((JPEGENCODERSTATE*)encoder->state.context)->subsampling = subsampling;
|
||||||
((JPEGENCODERSTATE*)encoder->state.context)->progressive = progressive;
|
((JPEGENCODERSTATE*)encoder->state.context)->progressive = progressive;
|
||||||
((JPEGENCODERSTATE*)encoder->state.context)->smooth = smooth;
|
((JPEGENCODERSTATE*)encoder->state.context)->smooth = smooth;
|
||||||
|
|
|
@ -88,6 +88,9 @@ typedef struct {
|
||||||
/* Chroma Subsampling (-1=default, 0=none, 1=medium, 2=high) */
|
/* Chroma Subsampling (-1=default, 0=none, 1=medium, 2=high) */
|
||||||
int subsampling;
|
int subsampling;
|
||||||
|
|
||||||
|
/* Custom quantization tables () */
|
||||||
|
unsigned int **qtables;
|
||||||
|
|
||||||
/* Extra data (to be injected after header) */
|
/* Extra data (to be injected after header) */
|
||||||
char* extra; int extra_size;
|
char* extra; int extra_size;
|
||||||
|
|
||||||
|
|
|
@ -143,8 +143,22 @@ ImagingJpegEncode(Imaging im, ImagingCodecState state, UINT8* buf, int bytes)
|
||||||
|
|
||||||
/* Compressor configuration */
|
/* Compressor configuration */
|
||||||
jpeg_set_defaults(&context->cinfo);
|
jpeg_set_defaults(&context->cinfo);
|
||||||
if (context->quality > 0)
|
|
||||||
|
/* Use custom quantization tables */
|
||||||
|
if (context->qtables) {
|
||||||
|
int i;
|
||||||
|
int quality = 100;
|
||||||
|
if (context->quality > 0) {
|
||||||
|
quality = context->quality;
|
||||||
|
}
|
||||||
|
for (i = 0; i < sizeof(context->qtables)/sizeof(unsigned int); i++) {
|
||||||
|
// TODO: Should add support for none baseline
|
||||||
|
jpeg_add_quant_table(&context->cinfo, i, context->qtables[i],
|
||||||
|
quality, TRUE);
|
||||||
|
}
|
||||||
|
} else if (context->quality > 0) {
|
||||||
jpeg_set_quality(&context->cinfo, context->quality, 1);
|
jpeg_set_quality(&context->cinfo, context->quality, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Set subsampling options */
|
/* Set subsampling options */
|
||||||
switch (context->subsampling)
|
switch (context->subsampling)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user