From 25428afb7084f9adfaa54178919c1310a90741da Mon Sep 17 00:00:00 2001 From: wiredfool Date: Fri, 8 Jan 2016 07:18:48 -0800 Subject: [PATCH] Working GbrImagePlugin for version 2 GBR (gimp brush) files --- PIL/GbrImagePlugin.py | 57 +++++++++++++++++++++++++++++------------- Tests/test_file_gbr.py | 9 ++++++- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/PIL/GbrImagePlugin.py b/PIL/GbrImagePlugin.py index 15282ecee..8edb8f487 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 @@ i32 = _binary.i32be 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") + 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(header_size - 20)[:-1] + comment = self.fp.read(comment_length)[:-1] - self.mode = "L" + 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) + + # Data is an uncompressed block of w * h * bytes/pixel + self._data_size = width * height * color_depth def load(self): - - if not self.data: - return - - # create an image out of the brush data block 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/test_file_gbr.py b/Tests/test_file_gbr.py index 57b301ada..d38b4a70f 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 @@ class TestFileGbr(PillowTestCase): 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()