diff --git a/MANIFEST.in b/MANIFEST.in index c3620d01e4e..d82b746aa28 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -28,6 +28,7 @@ recursive-include Tests *.dds recursive-include Tests *.doc recursive-include Tests *.eps recursive-include Tests *.fli +recursive-include Tests *.gbr recursive-include Tests *.ggr recursive-include Tests *.gif recursive-include Tests *.gpl diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index 15282ecee6f..8edb8f487b3 100644 --- a/PIL/GbrImagePlugin.py +++ b/PIL/GbrImagePlugin.py @@ -1,17 +1,28 @@ # # The Python Imaging Library -# $Id$ # # load a GIMP brush file # # History: # 96-03-14 fl Created +# 16-01-08 es Version 2 # # Copyright (c) Secret Labs AB 1997. # Copyright (c) Fredrik Lundh 1996. +# Copyright (c) Eric Soroos 2016. # # See the README file for information on usage and redistribution. # +# +# See https://github.com/GNOME/gimp/blob/master/devel-docs/gbr.txt for +# format documentation. +# +# This code Interprets version 1 and 2 .gbr files. +# Version 1 files are obsolete, and should not be used for new +# brushes. +# Version 2 files are saved by GIMP v2.8 (at least) +# Version 3 files have a format specifier of 18 for 16bit floats in +# the color depth field. This is currently unsupported by Pillow. from PIL import Image, ImageFile, _binary @@ -19,7 +30,7 @@ def _accept(prefix): - return len(prefix) >= 8 and i32(prefix) >= 20 and i32(prefix[4:8]) == 1 + return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1,2) ## @@ -31,41 +42,53 @@ class GbrImageFile(ImageFile.ImageFile): format_description = "GIMP brush file" def _open(self): - header_size = i32(self.fp.read(4)) version = i32(self.fp.read(4)) - if header_size < 20 or version != 1: + if header_size < 20: raise SyntaxError("not a GIMP brush") + if version not in (1,2): + raise SyntaxError("Unsupported GIMP brush version: %s" %version) width = i32(self.fp.read(4)) height = i32(self.fp.read(4)) color_depth = i32(self.fp.read(4)) - if width <= 0 or height <= 0 or color_depth != 1: + if width <= 0 or height <= 0: raise SyntaxError("not a GIMP brush") - - comment = self.fp.read(header_size - 20)[:-1] - - self.mode = "L" + if color_depth not in (1,4): + raise SyntaxError("Unsupported GMP brush color depth: %s" %color_depth) + + if version == 1: + comment_length = header_size-20 + else: + comment_length = header_size-28 + magic_number = self.fp.read(4) + if magic_number != b'GIMP': + raise SyntaxError("not a GIMP brush, bad magic number") + self.info['spacing'] = i32(self.fp.read(4)) + + comment = self.fp.read(comment_length)[:-1] + + if color_depth == 1: + self.mode = "L" + else: + self.mode = 'RGBA' + self.size = width, height self.info["comment"] = comment - # Since the brush is so small, we read the data immediately - self.data = self.fp.read(width * height) + # Image might not be small + Image._decompression_bomb_check(self.size) - def load(self): - - if not self.data: - return + # Data is an uncompressed block of w * h * bytes/pixel + self._data_size = width * height * color_depth - # create an image out of the brush data block + def load(self): self.im = Image.core.new(self.mode, self.size) - self.im.frombytes(self.data) - self.data = b"" + self.frombytes(self.fp.read(self._data_size)) # # registry Image.register_open(GbrImageFile.format, GbrImageFile, _accept) - Image.register_extension(GbrImageFile.format, ".gbr") diff --git a/Tests/images/gbr.gbr b/Tests/images/gbr.gbr new file mode 100644 index 00000000000..054b82df5ba Binary files /dev/null and b/Tests/images/gbr.gbr differ diff --git a/Tests/images/gbr.png b/Tests/images/gbr.png new file mode 100644 index 00000000000..d27301df288 Binary files /dev/null and b/Tests/images/gbr.png differ diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py index 57b301ada9c..d38b4a70fc5 100644 --- a/Tests/test_file_gbr.py +++ b/Tests/test_file_gbr.py @@ -1,6 +1,6 @@ from helper import unittest, PillowTestCase -from PIL import GbrImagePlugin +from PIL import Image, GbrImagePlugin class TestFileGbr(PillowTestCase): @@ -12,6 +12,13 @@ def test_invalid_file(self): lambda: GbrImagePlugin.GbrImageFile(invalid_file)) + def test_gbr_file(self): + im = Image.open('Tests/images/gbr.gbr') + + target = Image.open('Tests/images/gbr.png') + + self.assert_image_equal(target, im) + if __name__ == '__main__': unittest.main() diff --git a/docs/handbook/image-file-formats.rst b/docs/handbook/image-file-formats.rst index 24c093db3ba..f36e75a14c6 100644 --- a/docs/handbook/image-file-formats.rst +++ b/docs/handbook/image-file-formats.rst @@ -670,14 +670,17 @@ into account. GBR ^^^ -The GBR decoder reads GIMP brush files. +The GBR decoder reads GIMP brush files, version 1 and 2. The :py:meth:`~PIL.Image.Image.open` method sets the following :py:attr:`~PIL.Image.Image.info` properties: -**description** +**comment** The brush name. +**spacing** + The spacing between the brushes, in pixels. Version 2 only. + GD ^^