Skip to content

Commit

Permalink
Clarify optimizeimg docs; add warnings
Browse files Browse the repository at this point in the history
The validator will now warn if it detects that a crushed output
is fed into something that is not a crusher.

The is_crusher method of an optimizer shall return True if the
optimisation process is lossless, and does try to find optimal
encoding parameters as opposed to only removing unneeded channels
or reducing palettes.
  • Loading branch information
CounterPillow committed May 8, 2014
1 parent a39c45d commit 6d28942
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 9 deletions.
15 changes: 9 additions & 6 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,9 @@ values. The valid configuration keys are listed below.
The option is a list of Optimizer objects, which are then executed in
the order in which they're specified::
# Import the optimizers we need
from optimizeimages import pngnq, optipng

worlds["world"] = "/path/to/world"

renders["daytime"] = {
Expand All @@ -566,6 +568,10 @@ values. The valid configuration keys are listed below.
"rendermode":smooth_lighting,
"optimizeimg":[pngnq(sampling=1), optipng(olevel=3)],
}

.. note::
Don't forget to import the optimizers you use in your config file, as shown in the
example above.

Here is a list of supported image optimization programs:

Expand Down Expand Up @@ -612,8 +618,9 @@ values. The valid configuration keys are listed below.
**Default:** ``2``

``pngcrush``
pngcrush is very slow and not very good, you should use optipng in probably all cases.
However, Overviewer still allows you to use it because we're nice people like that.
pngcrush, like optipng, is a lossless PNG recompressor. If you are able to do so, it
is recommended to use optipng instead, as it generally yields better results in less
time.
Available settings:

``brute``
Expand All @@ -626,10 +633,6 @@ values. The valid configuration keys are listed below.

**Default:** ``False``

.. note::
Don't forget to import the optimizers you use in your settings file, as shown in the
example above.

**Default:** ``[]``

``bgcolor``
Expand Down
13 changes: 13 additions & 0 deletions overviewer_core/optimizeimages.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ def exists_in_path(prog):

if (not exists_in_path(self.binaryname)) and (not exists_in_path(self.binaryname + ".exe")):
raise Exception("Optimization program '%s' was not found!" % self.binaryname)

def is_crusher(self):
"""Should return True if the optimization is lossless, i.e. none of the actual image data will be changed."""
raise NotImplementedError("I'm so abstract I can't even say whether I'm a crusher.")


class NonAtomicOptimizer(Optimizer):
Expand Down Expand Up @@ -84,6 +88,9 @@ def optimize(self, img):

NonAtomicOptimizer.fire_and_forget(self, args, img)

def is_crusher(self):
return False

class pngcrush(NonAtomicOptimizer, PNGOptimizer):
binaryname = "pngcrush"
# really can't be bothered to add some interface for all
Expand All @@ -98,6 +105,9 @@ def optimize(self, img):

NonAtomicOptimizer.fire_and_forget(self, args, img)

def is_crusher(self):
return True

class optipng(Optimizer, PNGOptimizer):
binaryname = "optipng"

Expand All @@ -106,6 +116,9 @@ def __init__(self, olevel=2):

def optimize(self, img):
Optimizer.fire_and_forget(self, [self.binaryname, "-o" + str(self.olevel), "-quiet", img])

def is_crusher(self):
return True


def optimize_image(imgpath, imgformat, optimizers):
Expand Down
15 changes: 12 additions & 3 deletions overviewer_core/settingsValidators.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,25 @@ def validateBGColor(color):
def validateOptImg(optimizers):
if isinstance(optimizers, (int, long)):
from optimizeimages import pngcrush
logging.warning("You're using a deprecated definition of optimizeimg. We'll do what you say for now, but please fix this as soon as possible.")
logging.warning("You're using a deprecated definition of optimizeimg. "\
"We'll do what you say for now, but please fix this as soon as possible.")
optimizers = [pngcrush()]
if not isinstance(optimizers, list):
raise ValidationException("optimizeimg is not a list. Make sure you specify them like [foo()], with square brackets.")
for opt in optimizers:
raise ValidationException("What you passed to optimizeimg is not a list. "\
"Make sure you specify them like [foo()], with square brackets.")

for opt, next_opt in zip(optimizers, optimizers[1:]) + [(optimizers[-1], None)]:
if not isinstance(opt, Optimizer):
raise ValidationException("Invalid Optimizer!")

opt.check_availability()

# Check whether the chaining is somewhat sane
if next_opt:
if opt.is_crusher() and not next_opt.is_crusher():
logging.warning("You're feeding a crushed output into an optimizer that does not crush. "\
"This is most likely pointless, and wastes time.")

return optimizers

def validateTexturePath(path):
Expand Down

0 comments on commit 6d28942

Please sign in to comment.