mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Functions to optimize and encode webp images
This commit is contained in:
parent
89391da7b7
commit
36203a7497
@ -38,7 +38,7 @@ qt_get_dll_path = partial(get_dll_path, loc=os.path.join(QT_PREFIX, 'lib'))
|
|||||||
|
|
||||||
def binary_includes():
|
def binary_includes():
|
||||||
return [
|
return [
|
||||||
j(PREFIX, 'bin', x) for x in ('pdftohtml', 'pdfinfo', 'pdftoppm', 'pdftotext', 'optipng', 'JxrDecApp')] + [
|
j(PREFIX, 'bin', x) for x in ('pdftohtml', 'pdfinfo', 'pdftoppm', 'pdftotext', 'optipng', 'cwebp', 'JxrDecApp')] + [
|
||||||
|
|
||||||
j(PREFIX, 'private', 'mozjpeg', 'bin', x) for x in ('jpegtran', 'cjpeg')] + [
|
j(PREFIX, 'private', 'mozjpeg', 'bin', x) for x in ('jpegtran', 'cjpeg')] + [
|
||||||
] + list(map(
|
] + list(map(
|
||||||
|
@ -493,7 +493,7 @@ class Freeze:
|
|||||||
print('\nAdding libjpeg, libpng, libwebp, optipng and mozjpeg')
|
print('\nAdding libjpeg, libpng, libwebp, optipng and mozjpeg')
|
||||||
for x in ('jpeg.8', 'png16.16', 'webp.7', 'webpmux.3', 'webpdemux.2'):
|
for x in ('jpeg.8', 'png16.16', 'webp.7', 'webpmux.3', 'webpdemux.2'):
|
||||||
self.install_dylib(join(PREFIX, 'lib', 'lib%s.dylib' % x))
|
self.install_dylib(join(PREFIX, 'lib', 'lib%s.dylib' % x))
|
||||||
for x in 'optipng', 'JxrDecApp':
|
for x in 'optipng', 'JxrDecApp', 'cwebp':
|
||||||
self.install_dylib(join(PREFIX, 'bin', x), set_id=False, dest=self.helpers_dir)
|
self.install_dylib(join(PREFIX, 'bin', x), set_id=False, dest=self.helpers_dir)
|
||||||
for x in ('jpegtran', 'cjpeg'):
|
for x in ('jpegtran', 'cjpeg'):
|
||||||
self.install_dylib(
|
self.install_dylib(
|
||||||
|
@ -134,7 +134,7 @@ def freeze(env, ext_dir, incdir):
|
|||||||
shutil.copy2(x + '.manifest', env.dll_dir)
|
shutil.copy2(x + '.manifest', env.dll_dir)
|
||||||
|
|
||||||
bindir = os.path.join(PREFIX, 'bin')
|
bindir = os.path.join(PREFIX, 'bin')
|
||||||
for x in ('pdftohtml', 'pdfinfo', 'pdftoppm', 'pdftotext', 'jpegtran-calibre', 'cjpeg-calibre', 'optipng-calibre', 'JXRDecApp-calibre'):
|
for x in ('pdftohtml', 'pdfinfo', 'pdftoppm', 'pdftotext', 'jpegtran-calibre', 'cjpeg-calibre', 'optipng-calibre', 'cwebp-calibre', 'JXRDecApp-calibre'):
|
||||||
copybin(os.path.join(bindir, x + '.exe'))
|
copybin(os.path.join(bindir, x + '.exe'))
|
||||||
for f in glob.glob(os.path.join(bindir, '*.dll')):
|
for f in glob.glob(os.path.join(bindir, '*.dll')):
|
||||||
if re.search(r'(easylzma|icutest)', f.lower()) is None:
|
if re.search(r'(easylzma|icutest)', f.lower()) is None:
|
||||||
|
@ -173,7 +173,8 @@ def image_to_data(img, compression_quality=95, fmt='JPEG', png_compression_level
|
|||||||
'''
|
'''
|
||||||
Serialize image to bytestring in the specified format.
|
Serialize image to bytestring in the specified format.
|
||||||
|
|
||||||
:param compression_quality: is for JPEG and goes from 0 to 100. 100 being lowest compression, highest image quality
|
:param compression_quality: is for JPEG and WEBP and goes from 0 to 100.
|
||||||
|
100 being lowest compression, highest image quality. For WEBP 100 means lossless with effort of 70.
|
||||||
:param png_compression_level: is for PNG and goes from 0-9. 9 being highest compression.
|
:param png_compression_level: is for PNG and goes from 0-9. 9 being highest compression.
|
||||||
:param jpeg_optimized: Turns on the 'optimize' option for libjpeg which losslessly reduce file size
|
:param jpeg_optimized: Turns on the 'optimize' option for libjpeg which losslessly reduce file size
|
||||||
:param jpeg_progressive: Turns on the 'progressive scan' option for libjpeg which allows JPEG images to be downloaded in streaming fashion
|
:param jpeg_progressive: Turns on the 'progressive scan' option for libjpeg which allows JPEG images to be downloaded in streaming fashion
|
||||||
@ -202,6 +203,8 @@ def image_to_data(img, compression_quality=95, fmt='JPEG', png_compression_level
|
|||||||
elif fmt == 'PNG':
|
elif fmt == 'PNG':
|
||||||
cl = min(9, max(0, png_compression_level))
|
cl = min(9, max(0, png_compression_level))
|
||||||
w.setQuality(10 * (9-cl))
|
w.setQuality(10 * (9-cl))
|
||||||
|
elif fmt == 'WEBP':
|
||||||
|
w.setQuality(compression_quality)
|
||||||
if not w.write(img):
|
if not w.write(img):
|
||||||
raise ValueError('Failed to export image as ' + fmt + ' with error: ' + w.errorString())
|
raise ValueError('Failed to export image as ' + fmt + ' with error: ' + w.errorString())
|
||||||
return ba.data()
|
return ba.data()
|
||||||
@ -548,6 +551,7 @@ def run_optimizer(file_path, cmd, as_filter=False, input_data=None):
|
|||||||
else:
|
else:
|
||||||
os.close(fd)
|
os.close(fd)
|
||||||
iname, oname = os.path.basename(file_path), os.path.basename(outfile)
|
iname, oname = os.path.basename(file_path), os.path.basename(outfile)
|
||||||
|
input_size = os.path.getsize(file_path)
|
||||||
|
|
||||||
def repl(q, r):
|
def repl(q, r):
|
||||||
cmd[cmd.index(q)] = r
|
cmd[cmd.index(q)] = r
|
||||||
@ -584,6 +588,7 @@ def run_optimizer(file_path, cmd, as_filter=False, input_data=None):
|
|||||||
sz = 0
|
sz = 0
|
||||||
if sz < 1:
|
if sz < 1:
|
||||||
return '%s returned a zero size image' % cmd[0]
|
return '%s returned a zero size image' % cmd[0]
|
||||||
|
if sz < input_size:
|
||||||
shutil.copystat(file_path, outfile)
|
shutil.copystat(file_path, outfile)
|
||||||
atomic_rename(outfile, file_path)
|
atomic_rename(outfile, file_path)
|
||||||
finally:
|
finally:
|
||||||
@ -612,6 +617,21 @@ def optimize_png(file_path, level=7):
|
|||||||
return run_optimizer(file_path, cmd)
|
return run_optimizer(file_path, cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def run_cwebp(file_path, lossless, q, m, metadata):
|
||||||
|
exe = get_exe_path('cwebp')
|
||||||
|
q = max(0, min(q, 100))
|
||||||
|
m = max(0, min(m, 6))
|
||||||
|
cmd = [exe] + f'-mt -metadata {metadata} -q {q} -m {m} -o'.split() + [False, True]
|
||||||
|
if lossless:
|
||||||
|
cmd.insert(1, '-lossless')
|
||||||
|
return run_optimizer(file_path, cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def optimize_webp(file_path, q=100, m=6, metadata='all'):
|
||||||
|
' metadata can be a comma seaprated list of all, none, exif, icc, xmp '
|
||||||
|
return run_cwebp(file_path, True, q, m, metadata)
|
||||||
|
|
||||||
|
|
||||||
def encode_jpeg(file_path, quality=80):
|
def encode_jpeg(file_path, quality=80):
|
||||||
from calibre.utils.speedups import ReadOnlyFileBuffer
|
from calibre.utils.speedups import ReadOnlyFileBuffer
|
||||||
quality = max(0, min(100, int(quality)))
|
quality = max(0, min(100, int(quality)))
|
||||||
@ -626,6 +646,10 @@ def encode_jpeg(file_path, quality=80):
|
|||||||
if not img.save(buf, 'PPM'):
|
if not img.save(buf, 'PPM'):
|
||||||
raise ValueError('Failed to export image to PPM')
|
raise ValueError('Failed to export image to PPM')
|
||||||
return run_optimizer(file_path, cmd, as_filter=True, input_data=ReadOnlyFileBuffer(ba.data()))
|
return run_optimizer(file_path, cmd, as_filter=True, input_data=ReadOnlyFileBuffer(ba.data()))
|
||||||
|
|
||||||
|
|
||||||
|
def encode_webp(file_path, quality=70, m=6, metadata='all'):
|
||||||
|
return run_cwebp(file_path, False, quality, m, metadata)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
@ -649,6 +673,10 @@ def test(): # {{{
|
|||||||
raise SystemExit('optimize_png failed: %s' % ret)
|
raise SystemExit('optimize_png failed: %s' % ret)
|
||||||
if glob('*.bak'):
|
if glob('*.bak'):
|
||||||
raise SystemExit('Spurious .bak files left behind')
|
raise SystemExit('Spurious .bak files left behind')
|
||||||
|
save_image(img, 'test.webp', compression_quality=100)
|
||||||
|
ret = optimize_webp('test.webp')
|
||||||
|
if ret is not None:
|
||||||
|
raise SystemExit('optimize_webp failed: %s' % ret)
|
||||||
quantize_image(img)
|
quantize_image(img)
|
||||||
oil_paint_image(img)
|
oil_paint_image(img)
|
||||||
gaussian_sharpen_image(img)
|
gaussian_sharpen_image(img)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user