Ejemplo n.º 1
0
    def test_magicksave(self):
        # save to a file and load again ... we can't use save_load_file since
        # we want to make sure we use magickload/save
        # don't use BMP - GraphicsMagick always adds an alpha
        # don't use TIF - IM7 will save as 16-bit
        filename = temp_filename(self.tempdir, ".jpg")

        self.colour.magicksave(filename)
        x = pyvips.Image.magickload(filename)

        assert self.colour.width == x.width
        assert self.colour.height == x.height
        assert self.colour.bands == x.bands
        max_diff = (self.colour - x).abs().max()
        assert max_diff < 60

        self.save_load_buffer("magicksave_buffer",
                              "magickload_buffer",
                              self.colour,
                              60,
                              format="JPG")

        # try an animation
        if have("gifload"):
            x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
            w1 = x1.magicksave_buffer(format="GIF")
            x2 = pyvips.Image.new_from_buffer(w1, "", n=-1)
            assert x1.get("delay") == x2.get("delay")
            assert x1.get("page-height") == x2.get("page-height")
            # magicks vary in how they handle this ... just pray we are close
            assert abs(x1.get("gif-loop") - x2.get("gif-loop")) < 5
Ejemplo n.º 2
0
    def test_webp(self):
        def webp_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [71, 166, 236])
            assert im.width == 550
            assert im.height == 368
            assert im.bands == 3

        self.file_loader("webpload", WEBP_FILE, webp_valid)
        self.buffer_loader("webpload_buffer", WEBP_FILE, webp_valid)
        self.save_load_buffer("webpsave_buffer", "webpload_buffer",
                              self.colour, 60)
        self.save_load("%s.webp", self.colour)

        # test lossless mode
        im = pyvips.Image.new_from_file(WEBP_FILE)
        buf = im.webpsave_buffer(lossless=True)
        im2 = pyvips.Image.new_from_buffer(buf, "")
        assert im.avg() == im2.avg()

        # higher Q should mean a bigger buffer
        b1 = im.webpsave_buffer(Q=10)
        b2 = im.webpsave_buffer(Q=90)
        assert len(b2) > len(b1)

        # try saving an image with an ICC profile and reading it back ... if we
        # can do it, our webp supports metadata load/save
        buf = self.colour.webpsave_buffer()
        im = pyvips.Image.new_from_buffer(buf, "")
        if im.get_typeof("icc-profile-data") != 0:
            # verify that the profile comes back unharmed
            p1 = self.colour.get("icc-profile-data")
            p2 = im.get("icc-profile-data")
            assert p1 == p2

            # add tests for exif, xmp, ipct
            # the exif test will need us to be able to walk the header,
            # we can't just check exif-data

            # we can test that exif changes change the output of webpsave
            # first make sure we have exif support
            z = pyvips.Image.new_from_file(JPEG_FILE)
            if z.get_typeof("exif-ifd0-Orientation") != 0:
                x = self.colour.copy()
                x.set("orientation", 6)
                buf = x.webpsave_buffer()
                y = pyvips.Image.new_from_buffer(buf, "")
                assert y.get("orientation") == 6

        # try converting an animated gif to webp ... can't do back to gif
        # again without IM support
        if have("gifload"):
            x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
            w1 = x1.webpsave_buffer(Q=10)
            x2 = pyvips.Image.new_from_buffer(w1, "", n=-1)
            assert x1.width == x2.width
            assert x1.height == x2.height
            assert x1.get("gif-delay") == x2.get("gif-delay")
            assert x1.get("page-height") == x2.get("page-height")
            assert x1.get("gif-loop") == x2.get("gif-loop")
Ejemplo n.º 3
0
    def test_webp(self):
        def webp_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [71, 166, 236])
            assert im.width == 550
            assert im.height == 368
            assert im.bands == 3

        self.file_loader("webpload", WEBP_FILE, webp_valid)
        self.buffer_loader("webpload_buffer", WEBP_FILE, webp_valid)
        self.save_load_buffer("webpsave_buffer", "webpload_buffer",
                              self.colour, 60)
        self.save_load("%s.webp", self.colour)

        # test lossless mode
        im = pyvips.Image.new_from_file(WEBP_FILE)
        buf = im.webpsave_buffer(lossless=True)
        im2 = pyvips.Image.new_from_buffer(buf, "")
        assert im.avg() == im2.avg()

        # higher Q should mean a bigger buffer
        b1 = im.webpsave_buffer(Q=10)
        b2 = im.webpsave_buffer(Q=90)
        assert len(b2) > len(b1)

        # try saving an image with an ICC profile and reading it back ... if we
        # can do it, our webp supports metadata load/save
        buf = self.colour.webpsave_buffer()
        im = pyvips.Image.new_from_buffer(buf, "")
        if im.get_typeof("icc-profile-data") != 0:
            # verify that the profile comes back unharmed
            p1 = self.colour.get("icc-profile-data")
            p2 = im.get("icc-profile-data")
            assert p1 == p2

            # add tests for exif, xmp, ipct
            # the exif test will need us to be able to walk the header,
            # we can't just check exif-data

            # we can test that exif changes change the output of webpsave
            # first make sure we have exif support
            z = pyvips.Image.new_from_file(JPEG_FILE)
            if z.get_typeof("exif-ifd0-Orientation") != 0:
                x = self.colour.copy()
                x.set("orientation", 6)
                buf = x.webpsave_buffer()
                y = pyvips.Image.new_from_buffer(buf, "")
                assert y.get("orientation") == 6

        # try converting an animated gif to webp ... can't do back to gif
        # again without IM support
        if have("gifload"):
            x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
            w1 = x1.webpsave_buffer(Q=10)
            x2 = pyvips.Image.new_from_buffer(w1, "", n=-1)
            assert x1.width == x2.width
            assert x1.height == x2.height
            assert x1.get("gif-delay") == x2.get("gif-delay")
            assert x1.get("page-height") == x2.get("page-height")
            assert x1.get("gif-loop") == x2.get("gif-loop")
Ejemplo n.º 4
0
    def test_magickload(self):
        def bmp_valid(im):
            a = im(100, 100)

            assert_almost_equal_objects(a, [227, 216, 201])
            assert im.width == 1419
            assert im.height == 1001

        self.file_loader("magickload", BMP_FILE, bmp_valid)
        self.buffer_loader("magickload_buffer", BMP_FILE, bmp_valid)

        # we should have rgba for svg files
        im = pyvips.Image.magickload(SVG_FILE)
        assert im.bands == 4

        # density should change size of generated svg
        im = pyvips.Image.magickload(SVG_FILE, density='100')
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(SVG_FILE, density='200')
        # This seems to fail on travis, no idea why, some problem in their IM
        # perhaps
        # assert im.width == width * 2
        # assert im.height == height * 2

        # all-frames should load every frame of the animation
        # (though all-frames is deprecated)
        im = pyvips.Image.magickload(GIF_ANIM_FILE)
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(GIF_ANIM_FILE, all_frames=True)
        assert im.width == width
        assert im.height == height * 5

        # page/n let you pick a range of pages
        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            im = pyvips.Image.magickload(GIF_ANIM_FILE)
            width = im.width
            height = im.height
            im = pyvips.Image.magickload(GIF_ANIM_FILE, page=1, n=2)
            assert im.width == width
            assert im.height == height * 2
            page_height = im.get("page-height")
            assert page_height == height

        # should work for dicom
        im = pyvips.Image.magickload(DICOM_FILE)
        assert im.width == 128
        assert im.height == 128
        # some IMs are 3 bands, some are 1, can't really test
        # assert im.bands == 1

        # added in 8.7
        if have("magicksave"):
            self.save_load_file(".bmp", "", self.colour, 0)
            self.save_load_buffer("magicksave_buffer", "magickload_buffer",
                                  self.colour, 0, format="BMP")
            self.save_load("%s.bmp", self.colour)
Ejemplo n.º 5
0
 def test_rotate(self):
     if have("rotate"):
         im = pyvips.Image.new_from_file(JPEG_FILE)
         im2 = im.rotate(90)
         im3 = im.affine([0, -1, 1, 0])
         # rounding in calculating the affine transform from the angle stops
         # this being exactly true
         assert (im2 - im3).abs().max() < 50
Ejemplo n.º 6
0
    def test_thumbnail(self):
        im = pyvips.Image.thumbnail(JPEG_FILE, 100)

        assert im.height == 100
        assert im.bands == 3
        assert im.bands == 3

        # the average shouldn't move too much
        im_orig = pyvips.Image.new_from_file(JPEG_FILE)
        assert abs(im_orig.avg() - im.avg()) < 1

        # make sure we always get the right width
        for height in range(440, 1, -13):
            im = pyvips.Image.thumbnail(JPEG_FILE, height)
            assert im.height == height

        # should fit one of width or height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300)
        assert im.width == 100
        assert im.height != 300
        im = pyvips.Image.thumbnail(JPEG_FILE, 300, height=100)
        assert im.width != 300
        assert im.height == 100

        # with @crop, should fit both width and height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100,
                                    height=300, crop=True)
        assert im.width == 100
        assert im.height == 300

        im1 = pyvips.Image.thumbnail(JPEG_FILE, 100)
        with open(JPEG_FILE, 'rb') as f:
            buf = f.read()
        im2 = pyvips.Image.thumbnail_buffer(buf, 100)
        assert abs(im1.avg() - im2.avg()) < 1

        if have("heifload"):
            # this image is orientation 6 ... thumbnail should flip it
            im = pyvips.Image.new_from_file(HEIC_FILE)
            thumb = pyvips.Image.thumbnail(HEIC_FILE, 100)

            # original is landscape
            assert im.width > im.height

            # thumb should be portrait 
            assert thumb.width < thumb.height
            assert thumb.height == 100
Ejemplo n.º 7
0
    def test_gifsave(self):
        # Animated GIF round trip
        x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
        b1 = x1.gifsave_buffer()
        x2 = pyvips.Image.new_from_buffer(b1, "", n=-1)
        assert x1.width == x2.width
        assert x1.height == x2.height
        assert x1.get("n-pages") == x2.get("n-pages")
        assert x1.get("delay") == x2.get("delay")
        assert x1.get("page-height") == x2.get("page-height")
        assert x1.get("loop") == x2.get("loop")

        # Reducing dither will typically reduce file size (and quality)
        little_dither = self.colour.gifsave_buffer(dither=0.1, effort=1)
        large_dither = self.colour.gifsave_buffer(dither=0.9, effort=1)
        assert len(little_dither) < len(large_dither)

        # Reducing effort will typically increase file size (and reduce quality)
        little_effort = self.colour.gifsave_buffer(effort=1)
        large_effort = self.colour.gifsave_buffer(effort=10)
        assert len(little_effort) > len(large_effort)

        # Reducing bitdepth will typically reduce file size (and reduce quality)
        bitdepth8 = self.colour.gifsave_buffer(bitdepth=8, effort=1)
        bitdepth7 = self.colour.gifsave_buffer(bitdepth=7, effort=1)
        assert len(bitdepth8) > len(bitdepth7)

        if have("webpload"):
            # Animated WebP to GIF
            x1 = pyvips.Image.new_from_file(WEBP_ANIMATED_FILE, n=-1)
            b1 = x1.gifsave_buffer()
            x2 = pyvips.Image.new_from_buffer(b1, "", n=-1)
            assert x1.width == x2.width
            assert x1.height == x2.height
            assert x1.get("n-pages") == x2.get("n-pages")
            assert x1.get("delay") == x2.get("delay")
            assert x1.get("page-height") == x2.get("page-height")
            assert x1.get("loop") == x2.get("loop")
Ejemplo n.º 8
0
    def test_thumbnail(self):
        im = pyvips.Image.thumbnail(JPEG_FILE, 100)

        assert im.height == 100
        assert im.bands == 3
        assert im.bands == 3

        # the average shouldn't move too much
        im_orig = pyvips.Image.new_from_file(JPEG_FILE)
        assert abs(im_orig.avg() - im.avg()) < 1

        # make sure we always get the right width
        for height in range(440, 1, -13):
            im = pyvips.Image.thumbnail(JPEG_FILE, height)
            assert im.height == height

        # should fit one of width or height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300)
        assert im.width == 100
        assert im.height != 300
        im = pyvips.Image.thumbnail(JPEG_FILE, 300, height=100)
        assert im.width != 300
        assert im.height == 100

        # with @crop, should fit both width and height
        im = pyvips.Image.thumbnail(JPEG_FILE, 100, height=300, crop=True)
        assert im.width == 100
        assert im.height == 300

        im1 = pyvips.Image.thumbnail(JPEG_FILE, 100)
        with open(JPEG_FILE, 'rb') as f:
            buf = f.read()
        im2 = pyvips.Image.thumbnail_buffer(buf, 100)
        assert abs(im1.avg() - im2.avg()) < 1

        # should be able to thumbnail many-page tiff
        im = pyvips.Image.thumbnail(OME_FILE, 100)
        assert im.width == 100
        assert im.height == 38

        # should be able to thumbnail individual pages from many-page tiff
        im1 = pyvips.Image.thumbnail(OME_FILE + "[page=0]", 100)
        assert im1.width == 100
        assert im1.height == 38
        im2 = pyvips.Image.thumbnail(OME_FILE + "[page=1]", 100)
        assert im2.width == 100
        assert im2.height == 38
        assert (im1 - im2).abs().max() != 0

        # should be able to thumbnail entire many-page tiff as a toilet-roll
        # image
        im = pyvips.Image.thumbnail(OME_FILE + "[n=-1]", 100)
        assert im.width == 100
        assert im.height == 570

        # should be able to thumbnail a single-page tiff in a buffer
        im1 = pyvips.Image.thumbnail(TIF_FILE, 100)
        with open(TIF_FILE, 'rb') as f:
            buf = f.read()
        im2 = pyvips.Image.thumbnail_buffer(buf, 100)
        assert abs(im1.avg() - im2.avg()) < 1

        # linear shrink should work on rgba images
        im1 = pyvips.Image.thumbnail(RGBA_FILE, 64, linear=True)
        im2 = pyvips.Image.new_from_file(RGBA_CORRECT_FILE)
        assert abs(im1.flatten(background=255).avg() - im2.avg()) < 1

        if have("heifload"):
            # this image is orientation 6 ... thumbnail should flip it
            im = pyvips.Image.new_from_file(AVIF_FILE)
            thumb = pyvips.Image.thumbnail(AVIF_FILE, 100)

            # thumb should be portrait
            assert thumb.width < thumb.height
            assert thumb.height == 100
Ejemplo n.º 9
0
    def test_dzsave(self):
        # dzsave is hard to test, there are so many options
        # test each option separately and hope they all function together
        # correctly

        # default deepzoom layout ... we must use png here, since we want to
        # test the overlap for equality
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, suffix=".png")

        # test horizontal overlap ... expect 256 step, overlap 1
        x = pyvips.Image.new_from_file(filename + "_files/10/0_0.png")
        assert x.width == 255
        y = pyvips.Image.new_from_file(filename + "_files/10/1_0.png")
        assert y.width == 256

        # the right two columns of x should equal the left two columns of y
        left = x.crop(x.width - 2, 0, 2, x.height)
        right = y.crop(0, 0, 2, y.height)
        assert (left - right).abs().max() == 0

        # test vertical overlap
        assert x.height == 255
        y = pyvips.Image.new_from_file(filename + "_files/10/0_1.png")
        assert y.height == 256

        # the bottom two rows of x should equal the top two rows of y
        top = x.crop(0, x.height - 2, x.width, 2)
        bottom = y.crop(0, 0, y.width, 2)
        assert (top - bottom).abs().max() == 0

        # there should be a bottom layer
        x = pyvips.Image.new_from_file(filename + "_files/0/0_0.png")
        assert x.width == 1
        assert x.height == 1

        # 10 should be the final layer
        assert not os.path.isdir(filename + "_files/11")

        # default google layout
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, layout="google")

        # test bottom-right tile ... default is 256x256 tiles, overlap 0
        x = pyvips.Image.new_from_file(filename + "/2/2/3.jpg")
        assert x.width == 256
        assert x.height == 256
        assert not os.path.exists(filename + "/2/2/4.jpg")
        assert not os.path.exists(filename + "/3")
        x = pyvips.Image.new_from_file(filename + "/blank.png")
        assert x.width == 256
        assert x.height == 256

        # google layout with overlap ... verify that we clip correctly

        # overlap 1, 510x510 pixels, 256 pixel tiles, should be exactly 2x2
        # tiles, though in fact the bottom and right edges will be white
        filename = temp_filename(self.tempdir, '')
        self.colour.crop(0, 0, 510, 510).dzsave(filename, layout="google",
                                                overlap=1, depth="one")

        x = pyvips.Image.new_from_file(filename + "/0/1/1.jpg")
        assert x.width == 256
        assert x.height == 256
        assert not os.path.exists(filename + "/0/2/2.jpg")

        # with 511x511, it'll fit exactly into 2x2 -- we we actually generate
        # 3x3, since we output the overlaps
        # 8.6 revised the rules on overlaps, so don't test earlier than that
        if pyvips.base.at_least_libvips(8, 6):
            filename = temp_filename(self.tempdir, '')
            self.colour.crop(0, 0, 511, 511).dzsave(filename, layout="google",
                                                    overlap=1, depth="one")

            x = pyvips.Image.new_from_file(filename + "/0/2/2.jpg")
            assert x.width == 256
            assert x.height == 256
            assert not os.path.exists(filename + "/0/3/3.jpg")

        # default zoomify layout
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, layout="zoomify")

        # 256x256 tiles, no overlap
        assert os.path.exists(filename + "/ImageProperties.xml")
        x = pyvips.Image.new_from_file(filename + "/TileGroup0/2-3-2.jpg")
        assert x.width == 256
        assert x.height == 256

        # test zip output
        filename = temp_filename(self.tempdir, '.zip')
        self.colour.dzsave(filename)
        # before 8.5.8, you needed a gc on pypy to flush small zip output to
        # disc
        if not pyvips.base.at_least_libvips(8, 6):
            gc.collect()
        assert os.path.exists(filename)
        assert not os.path.exists(filename + "_files")
        assert not os.path.exists(filename + ".dzi")

        # test compressed zip output
        filename2 = temp_filename(self.tempdir, '.zip')
        self.colour.dzsave(filename2, compression=-1)
        # before 8.5.8, you needed a gc on pypy to flush small zip output to
        # disc
        if not pyvips.base.at_least_libvips(8, 6):
            gc.collect()
        assert os.path.exists(filename2)
        assert os.path.getsize(filename2) < os.path.getsize(filename)

        # test suffix
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, suffix=".png")

        x = pyvips.Image.new_from_file(filename + "_files/10/0_0.png")
        assert x.width == 255

        # test overlap
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, overlap=200)

        y = pyvips.Image.new_from_file(filename + "_files/10/1_1.jpeg")
        assert y.width == 654

        # test tile-size
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, tile_size=512)

        y = pyvips.Image.new_from_file(filename + "_files/10/0_0.jpeg")
        assert y.width == 513
        assert y.height == 513

        # test save to memory buffer
        if have("dzsave_buffer"):
            filename = temp_filename(self.tempdir, '.zip')
            base = os.path.basename(filename)
            root, ext = os.path.splitext(base)

            self.colour.dzsave(filename)
            # before 8.5.8, you needed a gc on pypy to flush small zip
            # output to disc
            if not pyvips.base.at_least_libvips(8, 6):
                gc.collect()
            with open(filename, 'rb') as f:
                buf1 = f.read()
            buf2 = self.colour.dzsave_buffer(basename=root)
            assert len(buf1) == len(buf2)
Ejemplo n.º 10
0
class TestForeign:
    tempdir = None

    @classmethod
    def setup_class(cls):
        cls.tempdir = tempfile.mkdtemp()

        cls.colour = pyvips.Image.jpegload(JPEG_FILE)
        cls.mono = cls.colour.extract_band(1)
        # we remove the ICC profile: the RGB one will no longer be appropriate
        cls.mono.remove("icc-profile-data")
        cls.rad = cls.colour.float2rad()
        cls.rad.remove("icc-profile-data")
        cls.cmyk = cls.colour.bandjoin(cls.mono)
        cls.cmyk = cls.cmyk.copy(interpretation=pyvips.Interpretation.CMYK)
        cls.cmyk.remove("icc-profile-data")

        im = pyvips.Image.new_from_file(GIF_FILE)
        cls.onebit = im > 128

    @classmethod
    def teardown_class(cls):
        shutil.rmtree(cls.tempdir, ignore_errors=True)

    # we have test files for formats which have a clear standard
    def file_loader(self, loader, test_file, validate):
        im = pyvips.Operation.call(loader, test_file)
        validate(im)
        im = pyvips.Image.new_from_file(test_file)
        validate(im)

    def buffer_loader(self, loader, test_file, validate):
        with open(test_file, 'rb') as f:
            buf = f.read()

        im = pyvips.Operation.call(loader, buf)
        validate(im)
        im = pyvips.Image.new_from_buffer(buf, "")
        validate(im)

    def save_load(self, format, im):
        x = pyvips.Image.new_temp_file(format)
        im.write(x)

        assert im.width == x.width
        assert im.height == x.height
        assert im.bands == x.bands
        max_diff = (im - x).abs().max()
        assert max_diff == 0

    def save_load_file(self, format, options, im, thresh):
        # yuk!
        # but we can't set format parameters for pyvips.Image.new_temp_file()
        filename = temp_filename(self.tempdir, format)

        im.write_to_file(filename + options)
        x = pyvips.Image.new_from_file(filename)

        assert im.width == x.width
        assert im.height == x.height
        assert im.bands == x.bands
        max_diff = (im - x).abs().max()
        assert max_diff <= thresh
        x = None

    def save_load_buffer(self, saver, loader, im, max_diff=0, **kwargs):
        buf = pyvips.Operation.call(saver, im, **kwargs)
        x = pyvips.Operation.call(loader, buf)

        assert im.width == x.width
        assert im.height == x.height
        assert im.bands == x.bands
        assert (im - x).abs().max() <= max_diff

    def save_buffer_tempfile(self, saver, suf, im, max_diff=0):
        filename = temp_filename(self.tempdir, suf)

        buf = pyvips.Operation.call(saver, im)
        f = open(filename, 'wb')
        f.write(buf)
        f.close()

        x = pyvips.Image.new_from_file(filename)

        assert im.width == x.width
        assert im.height == x.height
        assert im.bands == x.bands
        assert (im - x).abs().max() <= max_diff

    def test_vips(self):
        self.save_load_file(".v", "", self.colour, 0)

        # check we can save and restore metadata
        filename = temp_filename(self.tempdir, ".v")
        self.colour.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        before_exif = self.colour.get("exif-data")
        after_exif = x.get("exif-data")

        assert len(before_exif) == len(after_exif)
        for i in range(len(before_exif)):
            assert before_exif[i] == after_exif[i]

        x = None

    @pytest.mark.skipif(not have("jpegload"),
                        reason="no jpeg support in this vips, skipping test")
    def test_jpeg(self):
        def jpeg_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [6, 5, 3])
            profile = im.get("icc-profile-data")
            assert len(profile) == 1352
            assert im.width == 1024
            assert im.height == 768
            assert im.bands == 3

        self.file_loader("jpegload", JPEG_FILE, jpeg_valid)
        self.save_load("%s.jpg", self.mono)
        self.save_load("%s.jpg", self.colour)

        self.buffer_loader("jpegload_buffer", JPEG_FILE, jpeg_valid)
        self.save_load_buffer("jpegsave_buffer", "jpegload_buffer",
                              self.colour, 80)

        # see if we have exif parsing: our test image has this field
        x = pyvips.Image.new_from_file(JPEG_FILE)
        if x.get_typeof("exif-ifd0-Orientation") != 0:
            # we need a copy of the image to set the new metadata on
            # otherwise we get caching problems

            # can set, save and load new orientation
            x = pyvips.Image.new_from_file(JPEG_FILE)
            x = x.copy()
            x.set("orientation", 2)
            filename = temp_filename(self.tempdir, '.jpg')
            x.write_to_file(filename)
            x = pyvips.Image.new_from_file(filename)
            y = x.get("orientation")
            assert y == 2

            # can remove orientation, save, load again, orientation
            # has reset
            x.remove("orientation")
            filename = temp_filename(self.tempdir, '.jpg')
            x.write_to_file(filename)
            x = pyvips.Image.new_from_file(filename)
            y = x.get("orientation")
            assert y == 1

            # autorotate load works
            filename = temp_filename(self.tempdir, '.jpg')
            x = pyvips.Image.new_from_file(JPEG_FILE)
            x = x.copy()
            x.set("orientation", 6)
            x.write_to_file(filename)
            x1 = pyvips.Image.new_from_file(filename)
            x2 = pyvips.Image.new_from_file(filename, autorotate=True)
            assert x1.width == x2.height
            assert x1.height == x2.width

    @pytest.mark.skipif(not have("pngload") or
                        not os.path.isfile(PNG_FILE),
                        reason="no png support, skipping test")
    def test_png(self):
        def png_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [38671.0, 33914.0, 26762.0])
            assert im.width == 290
            assert im.height == 442
            assert im.bands == 3

        self.file_loader("pngload", PNG_FILE, png_valid)
        self.buffer_loader("pngload_buffer", PNG_FILE, png_valid)
        self.save_load_buffer("pngsave_buffer", "pngload_buffer", self.colour)
        self.save_load("%s.png", self.mono)
        self.save_load("%s.png", self.colour)

    @pytest.mark.skipif(not have("tiffload") or
                        not os.path.isfile(TIF_FILE),
                        reason="no tiff support, skipping test")
    def test_tiff(self):
        def tiff_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [38671.0, 33914.0, 26762.0])
            assert im.width == 290
            assert im.height == 442
            assert im.bands == 3

        self.file_loader("tiffload", TIF_FILE, tiff_valid)
        self.buffer_loader("tiffload_buffer", TIF_FILE, tiff_valid)
        if pyvips.at_least_libvips(8, 5):
            self.save_load_buffer("tiffsave_buffer",
                                  "tiffload_buffer",
                                  self.colour)
        self.save_load("%s.tif", self.mono)
        self.save_load("%s.tif", self.colour)
        self.save_load("%s.tif", self.cmyk)

        self.save_load("%s.tif", self.onebit)
        self.save_load_file(".tif", "[squash]", self.onebit, 0)
        self.save_load_file(".tif", "[miniswhite]", self.onebit, 0)
        self.save_load_file(".tif", "[squash,miniswhite]", self.onebit, 0)

        self.save_load_file(".tif",
                            "[profile={0}]".format(SRGB_FILE),
                            self.colour, 0)
        self.save_load_file(".tif", "[tile]", self.colour, 0)
        self.save_load_file(".tif", "[tile,pyramid]", self.colour, 0)
        self.save_load_file(".tif",
                            "[tile,pyramid,compression=jpeg]", self.colour, 80)
        self.save_load_file(".tif", "[bigtiff]", self.colour, 0)
        self.save_load_file(".tif", "[compression=jpeg]", self.colour, 80)
        self.save_load_file(".tif",
                            "[tile,tile-width=256]", self.colour, 10)

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set("orientation", 2)
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get("orientation")
        assert y == 2

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set("orientation", 2)
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get("orientation")
        assert y == 2
        x.remove("orientation")

        filename = temp_filename(self.tempdir, '.tif')
        x.write_to_file(filename)
        x = pyvips.Image.new_from_file(filename)
        y = x.get("orientation")
        assert y == 1

        filename = temp_filename(self.tempdir, '.tif')
        x = pyvips.Image.new_from_file(TIF_FILE)
        x = x.copy()
        x.set("orientation", 6)
        x.write_to_file(filename)
        x1 = pyvips.Image.new_from_file(filename)
        x2 = pyvips.Image.new_from_file(filename, autorotate=True)
        assert x1.width == x2.height
        assert x1.height == x2.width

        # OME support in 8.5
        if pyvips.at_least_libvips(8, 5):
            x = pyvips.Image.new_from_file(OME_FILE)
            assert x.width == 439
            assert x.height == 167
            page_height = x.height

            x = pyvips.Image.new_from_file(OME_FILE, n=-1)
            assert x.width == 439
            assert x.height == page_height * 15

            x = pyvips.Image.new_from_file(OME_FILE, page=1, n=-1)
            assert x.width == 439
            assert x.height == page_height * 14

            x = pyvips.Image.new_from_file(OME_FILE, page=1, n=2)
            assert x.width == 439
            assert x.height == page_height * 2

            x = pyvips.Image.new_from_file(OME_FILE, n=-1)
            assert x(0, 166)[0] == 96
            assert x(0, 167)[0] == 0
            assert x(0, 168)[0] == 1

            filename = temp_filename(self.tempdir, '.tif')
            x.write_to_file(filename)

            x = pyvips.Image.new_from_file(filename, n=-1)
            assert x.width == 439
            assert x.height == page_height * 15
            assert x(0, 166)[0] == 96
            assert x(0, 167)[0] == 0
            assert x(0, 168)[0] == 1

        # pyr save to buffer added in 8.6
        if pyvips.at_least_libvips(8, 6):
            x = pyvips.Image.new_from_file(TIF_FILE)
            buf = x.tiffsave_buffer(tile=True, pyramid=True)
            filename = temp_filename(self.tempdir, '.tif')
            x.tiffsave(filename, tile=True, pyramid=True)
            with open(filename, 'rb') as f:
                buf2 = f.read()
            assert len(buf) == len(buf2)

            a = pyvips.Image.new_from_buffer(buf, "", page=2)
            b = pyvips.Image.new_from_buffer(buf2, "", page=2)
            assert a.width == b.width
            assert a.height == b.height
            assert a.avg() == b.avg()

    @pytest.mark.skipif(not have("magickload") or
                        not os.path.isfile(BMP_FILE),
                        reason="no magick support, skipping test")
    def test_magickload(self):
        def bmp_valid(im):
            a = im(100, 100)

            assert_almost_equal_objects(a, [227, 216, 201])
            assert im.width == 1419
            assert im.height == 1001

        self.file_loader("magickload", BMP_FILE, bmp_valid)
        self.buffer_loader("magickload_buffer", BMP_FILE, bmp_valid)

        # we should have rgba for svg files
        im = pyvips.Image.magickload(SVG_FILE)
        assert im.bands == 4

        # density should change size of generated svg
        im = pyvips.Image.magickload(SVG_FILE, density='100')
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(SVG_FILE, density='200')
        # This seems to fail on travis, no idea why, some problem in their IM
        # perhaps
        # assert im.width == width * 2
        # assert im.height == height * 2

        # all-frames should load every frame of the animation
        # (though all-frames is deprecated)
        im = pyvips.Image.magickload(GIF_ANIM_FILE)
        width = im.width
        height = im.height
        im = pyvips.Image.magickload(GIF_ANIM_FILE, all_frames=True)
        assert im.width == width
        assert im.height == height * 5

        # page/n let you pick a range of pages
        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            im = pyvips.Image.magickload(GIF_ANIM_FILE)
            width = im.width
            height = im.height
            im = pyvips.Image.magickload(GIF_ANIM_FILE, page=1, n=2)
            assert im.width == width
            assert im.height == height * 2
            page_height = im.get("page-height")
            assert page_height == height

        # should work for dicom
        im = pyvips.Image.magickload(DICOM_FILE)
        assert im.width == 128
        assert im.height == 128
        # some IMs are 3 bands, some are 1, can't really test
        # assert im.bands == 1

        # added in 8.7
        if have("magicksave"):
            self.save_load_file(".bmp", "", self.colour, 0)
            self.save_load_buffer("magicksave_buffer", "magickload_buffer",
                                  self.colour, 0, format="BMP")
            self.save_load("%s.bmp", self.colour)

    @pytest.mark.skipif(not have("webpload") or not os.path.isfile(WEBP_FILE),
                        reason="no webp support, skipping test")
    def test_webp(self):
        def webp_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [71, 166, 236])
            assert im.width == 550
            assert im.height == 368
            assert im.bands == 3

        self.file_loader("webpload", WEBP_FILE, webp_valid)
        self.buffer_loader("webpload_buffer", WEBP_FILE, webp_valid)
        self.save_load_buffer("webpsave_buffer", "webpload_buffer",
                              self.colour, 60)
        self.save_load("%s.webp", self.colour)

        # test lossless mode
        im = pyvips.Image.new_from_file(WEBP_FILE)
        buf = im.webpsave_buffer(lossless=True)
        im2 = pyvips.Image.new_from_buffer(buf, "")
        assert im.avg() == im2.avg()

        # higher Q should mean a bigger buffer
        b1 = im.webpsave_buffer(Q=10)
        b2 = im.webpsave_buffer(Q=90)
        assert len(b2) > len(b1)

        # try saving an image with an ICC profile and reading it back ... if we
        # can do it, our webp supports metadata load/save
        buf = self.colour.webpsave_buffer()
        im = pyvips.Image.new_from_buffer(buf, "")
        if im.get_typeof("icc-profile-data") != 0:
            # verify that the profile comes back unharmed
            p1 = self.colour.get("icc-profile-data")
            p2 = im.get("icc-profile-data")
            assert p1 == p2

            # add tests for exif, xmp, ipct
            # the exif test will need us to be able to walk the header,
            # we can't just check exif-data

            # we can test that exif changes change the output of webpsave
            # first make sure we have exif support
            z = pyvips.Image.new_from_file(JPEG_FILE)
            if z.get_typeof("exif-ifd0-Orientation") != 0:
                x = self.colour.copy()
                x.set("orientation", 6)
                buf = x.webpsave_buffer()
                y = pyvips.Image.new_from_buffer(buf, "")
                assert y.get("orientation") == 6

    @pytest.mark.skipif(not have("analyzeload") or
                        not os.path.isfile(ANALYZE_FILE),
                        reason="no analyze support, skipping test")
    def test_analyzeload(self):
        def analyze_valid(im):
            a = im(10, 10)
            assert pytest.approx(a[0]) == 3335
            assert im.width == 128
            assert im.height == 8064
            assert im.bands == 1

        self.file_loader("analyzeload", ANALYZE_FILE, analyze_valid)

    @pytest.mark.skipif(not have("matload") or
                        not os.path.isfile(MATLAB_FILE),
                        reason="no matlab support, skipping test")
    def test_matload(self):
        def matlab_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [38671.0, 33914.0, 26762.0])
            assert im.width == 290
            assert im.height == 442
            assert im.bands == 3

        self.file_loader("matload", MATLAB_FILE, matlab_valid)

    @pytest.mark.skipif(not have("openexrload") or
                        not os.path.isfile(EXR_FILE),
                        reason="no openexr support, skipping test")
    def test_openexrload(self):
        def exr_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [0.124512, 0.159668,
                                            0.040375, 1.0],
                                        threshold=0.00001)
            assert im.width == 610
            assert im.height == 406
            assert im.bands == 4

        self.file_loader("openexrload", EXR_FILE, exr_valid)

    @pytest.mark.skipif(not have("fitsload") or
                        not os.path.isfile(FITS_FILE),
                        reason="no fits support, skipping test")
    def test_fitsload(self):
        def fits_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [-0.165013, -0.148553, 1.09122,
                                            -0.942242], threshold=0.00001)
            assert im.width == 200
            assert im.height == 200
            assert im.bands == 4

        self.file_loader("fitsload", FITS_FILE, fits_valid)
        self.save_load("%s.fits", self.mono)

    @pytest.mark.skipif(not have("openslideload") or  # noqa: E501
                        not os.path.isfile(OPENSLIDE_FILE),
                        reason="no openslide support, skipping test")
    def test_openslideload(self):
        def openslide_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [244, 250, 243, 255])
            assert im.width == 2220
            assert im.height == 2967
            assert im.bands == 4

        self.file_loader("openslideload", OPENSLIDE_FILE, openslide_valid)

    @pytest.mark.skipif(not have("pdfload") or
                        not os.path.isfile(PDF_FILE),
                        reason="no pdf support, skipping test")
    def test_pdfload(self):
        def pdf_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [35, 31, 32, 255])
            assert im.width == 1133
            assert im.height == 680
            assert im.bands == 4

        self.file_loader("pdfload", PDF_FILE, pdf_valid)
        self.buffer_loader("pdfload_buffer", PDF_FILE, pdf_valid)

        im = pyvips.Image.new_from_file(PDF_FILE)
        x = pyvips.Image.new_from_file(PDF_FILE, scale=2)
        assert abs(im.width * 2 - x.width) < 2
        assert abs(im.height * 2 - x.height) < 2

        im = pyvips.Image.new_from_file(PDF_FILE)
        x = pyvips.Image.new_from_file(PDF_FILE, dpi=144)
        assert abs(im.width * 2 - x.width) < 2
        assert abs(im.height * 2 - x.height) < 2

    @pytest.mark.skipif(not have("gifload") or
                        not os.path.isfile(GIF_FILE),
                        reason="no gif support, skipping test")
    def test_gifload(self):
        def gif_valid(im):
            a = im(10, 10)
            assert_almost_equal_objects(a, [33])
            assert im.width == 159
            assert im.height == 203
            assert im.bands == 1

        self.file_loader("gifload", GIF_FILE, gif_valid)
        self.buffer_loader("gifload_buffer", GIF_FILE, gif_valid)

        # 'n' param added in 8.5
        if pyvips.at_least_libvips(8, 5):
            x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE)
            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=2)
            assert x2.height == 2 * x1.height
            page_height = x2.get("page-height")
            assert page_height == x1.height

            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
            assert x2.height == 5 * x1.height

            x2 = pyvips.Image.new_from_file(GIF_ANIM_FILE, page=1, n=-1)
            assert x2.height == 4 * x1.height

    @pytest.mark.skipif(not have("svgload") or
                        not os.path.isfile(SVG_FILE),
                        reason="no svg support, skipping test")
    def test_svgload(self):
        def svg_valid(im):
            a = im(10, 10)

            # some old rsvg versions are way, way off
            assert abs(a[0] - 79) < 2
            assert abs(a[1] - 79) < 2
            assert abs(a[2] - 132) < 2
            assert abs(a[3] - 255) < 2

            assert im.width == 288
            assert im.height == 470
            assert im.bands == 4

        self.file_loader("svgload", SVG_FILE, svg_valid)
        self.buffer_loader("svgload_buffer", SVG_FILE, svg_valid)

        self.file_loader("svgload", SVGZ_FILE, svg_valid)
        self.buffer_loader("svgload_buffer", SVGZ_FILE, svg_valid)

        self.file_loader("svgload", SVG_GZ_FILE, svg_valid)

        im = pyvips.Image.new_from_file(SVG_FILE)
        x = pyvips.Image.new_from_file(SVG_FILE, scale=2)
        assert abs(im.width * 2 - x.width) < 2
        assert abs(im.height * 2 - x.height) < 2

        im = pyvips.Image.new_from_file(SVG_FILE)
        x = pyvips.Image.new_from_file(SVG_FILE, dpi=144)
        assert abs(im.width * 2 - x.width) < 2
        assert abs(im.height * 2 - x.height) < 2

    def test_csv(self):
        self.save_load("%s.csv", self.mono)

    def test_matrix(self):
        self.save_load("%s.mat", self.mono)

    @pytest.mark.skipif(not have("ppmload"),
                        reason="no PPM support, skipping test")
    def test_ppm(self):
        self.save_load("%s.ppm", self.mono)
        self.save_load("%s.ppm", self.colour)

    @pytest.mark.skipif(not have("radload"),
                        reason="no Radiance support, skipping test")
    def test_rad(self):
        self.save_load("%s.hdr", self.colour)
        self.save_buffer_tempfile("radsave_buffer", ".hdr",
                                  self.rad, max_diff=0)

    @pytest.mark.skipif(not have("dzsave"),
                        reason="no dzsave support, skipping test")
    def test_dzsave(self):
        # dzsave is hard to test, there are so many options
        # test each option separately and hope they all function together
        # correctly

        # default deepzoom layout ... we must use png here, since we want to
        # test the overlap for equality
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, suffix=".png")

        # test horizontal overlap ... expect 256 step, overlap 1
        x = pyvips.Image.new_from_file(filename + "_files/10/0_0.png")
        assert x.width == 255
        y = pyvips.Image.new_from_file(filename + "_files/10/1_0.png")
        assert y.width == 256

        # the right two columns of x should equal the left two columns of y
        left = x.crop(x.width - 2, 0, 2, x.height)
        right = y.crop(0, 0, 2, y.height)
        assert (left - right).abs().max() == 0

        # test vertical overlap
        assert x.height == 255
        y = pyvips.Image.new_from_file(filename + "_files/10/0_1.png")
        assert y.height == 256

        # the bottom two rows of x should equal the top two rows of y
        top = x.crop(0, x.height - 2, x.width, 2)
        bottom = y.crop(0, 0, y.width, 2)
        assert (top - bottom).abs().max() == 0

        # there should be a bottom layer
        x = pyvips.Image.new_from_file(filename + "_files/0/0_0.png")
        assert x.width == 1
        assert x.height == 1

        # 10 should be the final layer
        assert not os.path.isdir(filename + "_files/11")

        # default google layout
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, layout="google")

        # test bottom-right tile ... default is 256x256 tiles, overlap 0
        x = pyvips.Image.new_from_file(filename + "/2/2/3.jpg")
        assert x.width == 256
        assert x.height == 256
        assert not os.path.exists(filename + "/2/2/4.jpg")
        assert not os.path.exists(filename + "/3")
        x = pyvips.Image.new_from_file(filename + "/blank.png")
        assert x.width == 256
        assert x.height == 256

        # google layout with overlap ... verify that we clip correctly

        # overlap 1, 510x510 pixels, 256 pixel tiles, should be exactly 2x2
        # tiles, though in fact the bottom and right edges will be white
        filename = temp_filename(self.tempdir, '')
        self.colour.crop(0, 0, 510, 510).dzsave(filename, layout="google",
                                                overlap=1, depth="one")

        x = pyvips.Image.new_from_file(filename + "/0/1/1.jpg")
        assert x.width == 256
        assert x.height == 256
        assert not os.path.exists(filename + "/0/2/2.jpg")

        # with 511x511, it'll fit exactly into 2x2 -- we we actually generate
        # 3x3, since we output the overlaps
        # 8.6 revised the rules on overlaps, so don't test earlier than that
        if pyvips.base.at_least_libvips(8, 6):
            filename = temp_filename(self.tempdir, '')
            self.colour.crop(0, 0, 511, 511).dzsave(filename, layout="google",
                                                    overlap=1, depth="one")

            x = pyvips.Image.new_from_file(filename + "/0/2/2.jpg")
            assert x.width == 256
            assert x.height == 256
            assert not os.path.exists(filename + "/0/3/3.jpg")

        # default zoomify layout
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, layout="zoomify")

        # 256x256 tiles, no overlap
        assert os.path.exists(filename + "/ImageProperties.xml")
        x = pyvips.Image.new_from_file(filename + "/TileGroup0/2-3-2.jpg")
        assert x.width == 256
        assert x.height == 256

        # test zip output
        filename = temp_filename(self.tempdir, '.zip')
        self.colour.dzsave(filename)
        # before 8.5.8, you needed a gc on pypy to flush small zip output to
        # disc
        if not pyvips.base.at_least_libvips(8, 6):
            gc.collect()
        assert os.path.exists(filename)
        assert not os.path.exists(filename + "_files")
        assert not os.path.exists(filename + ".dzi")

        # test compressed zip output
        filename2 = temp_filename(self.tempdir, '.zip')
        self.colour.dzsave(filename2, compression=-1)
        # before 8.5.8, you needed a gc on pypy to flush small zip output to
        # disc
        if not pyvips.base.at_least_libvips(8, 6):
            gc.collect()
        assert os.path.exists(filename2)
        assert os.path.getsize(filename2) < os.path.getsize(filename)

        # test suffix
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, suffix=".png")

        x = pyvips.Image.new_from_file(filename + "_files/10/0_0.png")
        assert x.width == 255

        # test overlap
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, overlap=200)

        y = pyvips.Image.new_from_file(filename + "_files/10/1_1.jpeg")
        assert y.width == 654

        # test tile-size
        filename = temp_filename(self.tempdir, '')
        self.colour.dzsave(filename, tile_size=512)

        y = pyvips.Image.new_from_file(filename + "_files/10/0_0.jpeg")
        assert y.width == 513
        assert y.height == 513

        # test save to memory buffer
        if have("dzsave_buffer"):
            filename = temp_filename(self.tempdir, '.zip')
            base = os.path.basename(filename)
            root, ext = os.path.splitext(base)

            self.colour.dzsave(filename)
            # before 8.5.8, you needed a gc on pypy to flush small zip
            # output to disc
            if not pyvips.base.at_least_libvips(8, 6):
                gc.collect()
            with open(filename, 'rb') as f:
                buf1 = f.read()
            buf2 = self.colour.dzsave_buffer(basename=root)
            assert len(buf1) == len(buf2)
Ejemplo n.º 11
0
    def test_webp(self):
        def webp_valid(im):
            a = im(10, 10)
            # different webp versions use different rounding systems leading
            # to small variations
            assert_almost_equal_objects(a, [71, 166, 236], threshold=2)
            assert im.width == 550
            assert im.height == 368
            assert im.bands == 3

        self.file_loader("webpload", WEBP_FILE, webp_valid)
        self.buffer_loader("webpload_buffer", WEBP_FILE, webp_valid)
        self.save_load_buffer("webpsave_buffer", "webpload_buffer",
                              self.colour, 60)
        self.save_load("%s.webp", self.colour)

        # test lossless mode
        im = pyvips.Image.new_from_file(WEBP_FILE)
        buf = im.webpsave_buffer(lossless=True)
        im2 = pyvips.Image.new_from_buffer(buf, "")
        assert abs(im.avg() - im2.avg()) < 1

        # higher Q should mean a bigger buffer
        b1 = im.webpsave_buffer(Q=10)
        b2 = im.webpsave_buffer(Q=90)
        assert len(b2) > len(b1)

        # try saving an image with an ICC profile and reading it back ... if we
        # can do it, our webp supports metadata load/save
        buf = self.colour.webpsave_buffer()
        im = pyvips.Image.new_from_buffer(buf, "")
        if im.get_typeof("icc-profile-data") != 0:
            # verify that the profile comes back unharmed
            p1 = self.colour.get("icc-profile-data")
            p2 = im.get("icc-profile-data")
            assert p1 == p2

            # add tests for exif, xmp, ipct
            # the exif test will need us to be able to walk the header,
            # we can't just check exif-data

            # we can test that exif changes change the output of webpsave
            # first make sure we have exif support
            z = pyvips.Image.new_from_file(JPEG_FILE)
            if z.get_typeof("exif-ifd0-Orientation") != 0:
                x = self.colour.copy()
                x.set("orientation", 6)
                buf = x.webpsave_buffer()
                y = pyvips.Image.new_from_buffer(buf, "")
                assert y.get("orientation") == 6

        # try converting an animated gif to webp ... can't do back to gif
        # again without IM support
        if have("gifload"):
            x1 = pyvips.Image.new_from_file(GIF_ANIM_FILE, n=-1)
            w1 = x1.webpsave_buffer(Q=10)

            # our test gif has delay 0 for the first frame set in error,
            # when converting to WebP this should result in a 100ms delay.
            expected_delay = [100 if d <= 10 else d for d in x1.get("delay")]

            x2 = pyvips.Image.new_from_buffer(w1, "", n=-1)
            assert x1.width == x2.width
            assert x1.height == x2.height
            assert expected_delay == x2.get("delay")
            assert x1.get("page-height") == x2.get("page-height")
            assert x1.get("gif-loop") == x2.get("gif-loop")

        # WebP image that happens to contain the string "<svg"
        if have("svgload"):
            x = pyvips.Image.new_from_file(WEBP_LOOKS_LIKE_SVG_FILE)
            assert x.get("vips-loader") == "webpload"

        # Animated WebP roundtrip
        x = pyvips.Image.new_from_file(WEBP_ANIMATED_FILE, n=-1)
        assert x.width == 13
        assert x.height == 16393
        buf = x.webpsave_buffer()