diff --git a/docs/config.rst b/docs/config.rst index d41d42a..6325683 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -694,16 +694,29 @@ Image options ``imgformat`` This is which image format to render the tiles into. Its value should be a - string containing "png", "jpg", or "jpeg". + string containing "png", "jpg", "jpeg" or "webp". + + .. note:: + For WebP, your PIL/Pillow needs to be built with WebP support. Do + keep in mind that not all browsers support WebP images. **Default:** ``"png"`` ``imgquality`` - This is the image quality used when saving the tiles into the JPEG image - format. Its value should be an integer between 0 and 100. + This is the image quality used when saving the tiles into the JPEG or WebP + image format. Its value should be an integer between 0 and 100. + + For WebP images in lossless mode, it determines how much effort is spent + on compressing the image. **Default:** ``95`` +``imglossless`` + Determines whether a WebP image is saved in lossless or lossy mode. Has + no effect on other image formats. + + **Default:** ``True`` + ``optimizeimg`` .. warning:: diff --git a/overviewer.py b/overviewer.py index 79a1626..f48be1e 100755 --- a/overviewer.py +++ b/overviewer.py @@ -533,7 +533,11 @@ dir but you forgot to put quotes around the directory, since it contains spaces. # only pass to the TileSet the options it really cares about render['name'] = render_name # perhaps a hack. This is stored here for the asset manager - tileSetOpts = util.dict_subset(render, ["name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "defaultzoom", "imgquality", "optimizeimg", "rendermode", "worldname_orig", "title", "dimension", "changelist", "showspawn", "overlay", "base", "poititle", "maxzoom", "showlocationmarker", "minzoom"]) + tileSetOpts = util.dict_subset(render, [ + "name", "imgformat", "renderchecks", "rerenderprob", "bgcolor", "defaultzoom", + "imgquality", "imglossless", "optimizeimg", "rendermode", "worldname_orig", "title", + "dimension", "changelist", "showspawn", "overlay", "base", "poititle", "maxzoom", + "showlocationmarker", "minzoom"]) tileSetOpts.update({"spawn": w.find_true_spawn()}) # TODO find a better way to do this for rset in rsets: tset = tileset.TileSet(w, rset, assetMrg, tex, tileSetOpts, tileset_dir) diff --git a/overviewer_core/settingsDefinition.py b/overviewer_core/settingsDefinition.py index ef956ae..8e62f98 100644 --- a/overviewer_core/settingsDefinition.py +++ b/overviewer_core/settingsDefinition.py @@ -71,6 +71,8 @@ renders = Setting(required=True, default=util.OrderedDict(), "forcerender": Setting(required=False, validator=validateBool, default=None), "imgformat": Setting(required=True, validator=validateImgFormat, default="png"), "imgquality": Setting(required=False, validator=validateImgQuality, default=95), + "imglossless": Setting(required=False, validator=validateBool, + default=True), "bgcolor": Setting(required=True, validator=validateBGColor, default="1a1a1a"), "defaultzoom": Setting(required=True, validator=validateDefaultZoom, default=1), "optimizeimg": Setting(required=True, validator=validateOptImg, default=[]), diff --git a/overviewer_core/settingsValidators.py b/overviewer_core/settingsValidators.py index bf5ad5e..f674286 100644 --- a/overviewer_core/settingsValidators.py +++ b/overviewer_core/settingsValidators.py @@ -115,9 +115,14 @@ def validateRerenderprob(s): return val def validateImgFormat(fmt): - if fmt not in ("png", "jpg", "jpeg"): + if fmt not in ("png", "jpg", "jpeg", "webp"): raise ValidationException("%r is not a valid image format" % fmt) if fmt == "jpeg": fmt = "jpg" + if fmt == "webp": + try: + from PIL import _webp + except ImportError: + raise ValidationException("WebP is not supported by your PIL/Pillow installation") return fmt def validateImgQuality(qual): diff --git a/overviewer_core/tileset.py b/overviewer_core/tileset.py index 640caf5..5dbac43 100644 --- a/overviewer_core/tileset.py +++ b/overviewer_core/tileset.py @@ -261,12 +261,15 @@ class TileSet(object): rest of this discussion. imgformat - A string indicating the output format. Must be one of 'png' or - 'jpeg' + A string indicating the output format. Must be one of 'png', + 'jpeg' or 'webp' imgquality An integer 1-100 indicating the quality of the jpeg output. Only - relevant in jpeg mode. + relevant in jpeg and webp mode. + + imglossless + A boolean indicating whether to save a webp image in lossless mode. optimizeimg A list of optimizer instances to use. @@ -387,8 +390,10 @@ class TileSet(object): self.imgextension = 'png' elif self.options['imgformat'] in ('jpeg', 'jpg'): self.imgextension = 'jpg' + elif self.options['imgformat'] == 'webp': + self.imgextension = 'webp' else: - raise ValueError("imgformat must be one of: 'png' or 'jpg'") + raise ValueError("imgformat must be one of: 'png', 'jpg' or 'webp'") # This sets self.treedepth, self.xradius, and self.yradius self._set_map_size() @@ -1000,8 +1005,11 @@ class TileSet(object): if imgformat == 'jpg': img.convert('RGB').save(tmppath, "jpeg", quality=self.options['imgquality'], subsampling=0) - else: # PNG + elif imgformat == 'png': # PNG img.save(tmppath, "png") + elif imgformat == 'webp': + img.save(tmppath, "webp", quality=self.options['imgquality'], + lossless=self.options['imglossless']) if self.options['optimizeimg']: optimize_image(tmppath, imgformat, self.options['optimizeimg']) @@ -1101,8 +1109,11 @@ class TileSet(object): if self.imgextension == 'jpg': tileimg.convert('RGB').save(tmppath, "jpeg", quality=self.options['imgquality'], subsampling=0) - else: # PNG + elif self.imgextension == 'png': # PNG tileimg.save(tmppath, "png") + elif self.imgextension == 'webp': + tileimg.save(tmppath, "webp", quality=self.options['imgquality'], + lossless=self.options['imglossless']) if self.options['optimizeimg']: optimize_image(tmppath, self.imgextension, self.options['optimizeimg']) os.utime(tmppath, (max_chunk_mtime, max_chunk_mtime))