1# -*- coding: utf-8 -*-
2# This file is part of Xpra.
3# Copyright (C) 2015-2020 Antoine Martin <antoine@xpra.org>
4# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
5# later version. See the file COPYING for details.
6
7#pylint: disable=line-too-long
8
9import binascii
10
11from xpra.util import csv, typedict, roundup
12from xpra.log import Logger
13log = Logger("encoding")
14
15#Warning: many systems will fail above 8k because of memory constraints
16# encoders can allocate many times more memory to hold the frames..
17TEST_LIMIT_W, TEST_LIMIT_H = 8192, 8192
18
19
20#this test data was generated using a 24x16 blank image as input
21TEST_COMPRESSED_DATA = {
22    "h264": {
23        "YUV420P" : binascii.unhexlify("000000016764000aacb317cbc2000003000200000300651e244cd00000000168e970312c8b0000010605ffff56dc45e9bde6d948b7962cd820d923eeef78323634202d20636f726520313432202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303134202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d31207265663d35206465626c6f636b3d313a303a3020616e616c7973653d3078333a3078313133206d653d756d68207375626d653d38207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d31206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d31203878386463743d312063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d2d3220746872656164733d31206c6f6f6b61686561645f746872656164733d3120736c696365645f746872656164733d30206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d32206b6579696e743d393939393939206b6579696e745f6d696e3d353030303030207363656e656375743d343020696e7472615f726566726573683d302072633d637266206d62747265653d30206372663d33382e322071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d342069705f726174696f3d312e34302061713d313a312e3030008000000165888404bffe841fc0a667f891ea1728763fecb5e1"),
24        "YUV422P" : binascii.unhexlify("00000001677a000abcb317cbc2000003000200000300651e244cd00000000168e970312c8b0000010605ffff56dc45e9bde6d948b7962cd820d923eeef78323634202d20636f726520313432202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303134202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d31207265663d35206465626c6f636b3d313a303a3020616e616c7973653d3078333a3078313133206d653d756d68207375626d653d38207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d31206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d31203878386463743d312063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d2d3220746872656164733d31206c6f6f6b61686561645f746872656164733d3120736c696365645f746872656164733d30206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d32206b6579696e743d393939393939206b6579696e745f6d696e3d353030303030207363656e656375743d343020696e7472615f726566726573683d302072633d637266206d62747265653d30206372663d33382e322071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d342069705f726174696f3d312e34302061713d313a312e3030008000000165888404bffe841fc0a667f891ec3d121e72aecb5f"),
25        "YUV444P" : binascii.unhexlify("0000000167f4000a919662f89e1000000300100000030328f12266800000000168e970311121100000010605ffff55dc45e9bde6d948b7962cd820d923eeef78323634202d20636f726520313432202d20482e3236342f4d5045472d342041564320636f646563202d20436f70796c65667420323030332d32303134202d20687474703a2f2f7777772e766964656f6c616e2e6f72672f783236342e68746d6c202d206f7074696f6e733a2063616261633d31207265663d35206465626c6f636b3d313a303a3020616e616c7973653d3078333a3078313133206d653d756d68207375626d653d38207073793d31207073795f72643d312e30303a302e3030206d697865645f7265663d31206d655f72616e67653d3136206368726f6d615f6d653d31207472656c6c69733d31203878386463743d312063716d3d3020646561647a6f6e653d32312c313120666173745f70736b69703d31206368726f6d615f71705f6f66667365743d3420746872656164733d31206c6f6f6b61686561645f746872656164733d3120736c696365645f746872656164733d30206e723d3020646563696d6174653d3120696e7465726c616365643d3020626c757261795f636f6d7061743d3020636f6e73747261696e65645f696e7472613d3020626672616d65733d3020776569676874703d32206b6579696e743d393939393939206b6579696e745f6d696e3d353030303030207363656e656375743d343020696e7472615f726566726573683d302072633d637266206d62747265653d30206372663d33382e322071636f6d703d302e36302071706d696e3d302071706d61783d3639207170737465703d342069705f726174696f3d312e34302061713d313a312e3030008000000165888404bffeeb1fc0a667f75e658f9a9fccb1f341ffff"),
26        },
27    "vp8" : {
28        "YUV420P" : binascii.unhexlify("1003009d012a1800100000070885858899848800281013ad501fc01fd01050122780feffbb029ffffa2546bd18c06f7ffe8951fffe8951af46301bdfffa22a00"),
29        },
30    "vp9" : {
31        "YUV420P" : binascii.unhexlify("8249834200017000f60038241c18000000200000047ffffffba9da00059fffffff753b413bffffffeea7680000"),
32        "YUV444P" : binascii.unhexlify("a249834200002e001ec007048383000000040000223fffffeea76800c7ffffffeea7680677ffffff753b40081000"),
33        },
34}
35
36TEST_PICTURES = {
37    "png" : (
38        "89504e470d0a1a0a0000000d4948445200000020000000200806000000737a7af40000002849444154785eedd08100000000c3a0f9531fe4855061c0800103060c183060c0800103060cbc0f0c102000013337932a0000000049454e44ae426082",
39        "89504e470d0a1a0a0000000d4948445200000020000000200802000000fc18eda30000002549444154785eedd03101000000c2a0f54fed610d884061c0800103060c183060c080810f0c0c20000174754ae90000000049454e44ae426082",
40        ),
41    "png/L" : (
42        "89504e470d0a1a0a0000000d4948445200000020000000200800000000561125280000000274524e5300ff5b9122b50000002049444154785e63fccf801f3011906718550009a1d170180d07e4bc323cd20300a33d013f95f841e70000000049454e44ae426082",
43        "89504e470d0a1a0a0000000d4948445200000020000000200800000000561125280000001549444154785e63601805a321301a02a321803d0400042000017854be5c0000000049454e44ae426082",
44        ),
45    "png/P" : (
46        "89504e470d0a1a0a0000000d494844520000002000000020080300000044a48ac600000300504c5445000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b330f4880000010074524e53ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0053f707250000001c49444154785e63f84f00308c2a0087c068384012c268388ca87000003f68fc2e077ed1070000000049454e44ae426082",
47        "89504e470d0a1a0a0000000d494844520000002000000020080300000044a48ac600000300504c5445000000000000000000000000000000000000000000000000000000000000000000330000660000990000cc0000ff0000003300333300663300993300cc3300ff3300006600336600666600996600cc6600ff6600009900339900669900999900cc9900ff990000cc0033cc0066cc0099cc00cccc00ffcc0000ff0033ff0066ff0099ff00ccff00ffff00000033330033660033990033cc0033ff0033003333333333663333993333cc3333ff3333006633336633666633996633cc6633ff6633009933339933669933999933cc9933ff993300cc3333cc3366cc3399cc33cccc33ffcc3300ff3333ff3366ff3399ff33ccff33ffff33000066330066660066990066cc0066ff0066003366333366663366993366cc3366ff3366006666336666666666996666cc6666ff6666009966339966669966999966cc9966ff996600cc6633cc6666cc6699cc66cccc66ffcc6600ff6633ff6666ff6699ff66ccff66ffff66000099330099660099990099cc0099ff0099003399333399663399993399cc3399ff3399006699336699666699996699cc6699ff6699009999339999669999999999cc9999ff999900cc9933cc9966cc9999cc99cccc99ffcc9900ff9933ff9966ff9999ff99ccff99ffff990000cc3300cc6600cc9900cccc00ccff00cc0033cc3333cc6633cc9933cccc33ccff33cc0066cc3366cc6666cc9966cccc66ccff66cc0099cc3399cc6699cc9999cccc99ccff99cc00cccc33cccc66cccc99ccccccccccffcccc00ffcc33ffcc66ffcc99ffccccffccffffcc0000ff3300ff6600ff9900ffcc00ffff00ff0033ff3333ff6633ff9933ffcc33ffff33ff0066ff3366ff6666ff9966ffcc66ffff66ff0099ff3399ff6699ff9999ffcc99ffff99ff00ccff33ccff66ccff99ccffccccffffccff00ffff33ffff66ffff99ffffccffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000023faca40000001549444154785e63601805a321301a02a321803d0400042000017854be5c0000000049454e44ae426082",
48        ),
49    "jpeg" : (
50        "ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc00011080020002003012200021101031101ffc4001500010100000000000000000000000000000007ffc40014100100000000000000000000000000000000ffc40014010100000000000000000000000000000000ffc40014110100000000000000000000000000000000ffda000c03010002110311003f009f800000000000ffd9",
51        "ffd8ffe000104a46494600010100000100010000ffdb004300100b0c0e0c0a100e0d0e1211101318281a181616183123251d283a333d3c3933383740485c4e404457453738506d51575f626768673e4d71797064785c656763ffdb0043011112121815182f1a1a2f634238426363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363ffc00011080020002003012200021101031101ffc4001500010100000000000000000000000000000007ffc40014100100000000000000000000000000000000ffc40014010100000000000000000000000000000000ffc40014110100000000000000000000000000000000ffda000c03010002110311003f009f800000000000ffd9",
52        ),
53    "webp" : (
54        "524946465c00000057454250565038580a000000100000001f00001f0000414c50480f00000001071011110012c2ffef7a44ff530f005650382026000000d002009d012a200020003ed162aa4fa825a3a2280801001a096900003da3a000fef39d800000",
55        "524946465c00000057454250565038580a000000100000001f00001f0000414c50480f00000001071011110012c2ffef7a44ff530f005650382026000000d002009d012a200020003ed162aa4fa825a3a2280801001a096900003da3a000fef39d800000",
56        ),
57    }
58
59
60def makebuf(size, b=0x20):
61    return (chr(b).encode())*size
62
63
64def make_test_image(pixel_format, w, h):
65    from xpra.codecs.image_wrapper import ImageWrapper
66    from xpra.codecs.codec_constants import get_subsampling_divs
67    #import time
68    #start = monotonic()
69    if pixel_format.startswith("YUV") or pixel_format.startswith("GBRP") or pixel_format=="NV12":
70        divs = get_subsampling_divs(pixel_format)
71        try:
72            depth = int(pixel_format.split("P")[1])   #ie: YUV444P10 -> 10
73        except (IndexError, ValueError):
74            depth = 8
75        Bpp = roundup(depth, 8)//8
76        nplanes = len(divs)
77        ydiv = divs[0]  #always (1, 1)
78        y = makebuf(w//ydiv[0]*h//ydiv[1]*Bpp)
79        udiv = divs[1]
80        u = makebuf(w//udiv[0]*h//udiv[1]*Bpp)
81        planes = [y, u]
82        strides = [w//ydiv[0]*Bpp, w//udiv[0]*Bpp]
83        if nplanes==3:
84            vdiv = divs[2]
85            v = makebuf(w//vdiv[0]*h//vdiv[1]*Bpp)
86            planes.append(v)
87            strides.append(w//vdiv[0]*Bpp)
88        image = ImageWrapper(0, 0, w, h, planes, pixel_format, 32, strides, planes=nplanes, thread_safe=True)
89        #l = len(y)+len(u)+len(v)
90    elif pixel_format in ("RGB", "BGR", "RGBX", "BGRX", "XRGB", "BGRA", "RGBA", "r210", "BGR48"):
91        if pixel_format=="BGR48":
92            stride = w*6
93        else:
94            stride = w*len(pixel_format)
95        rgb_data = makebuf(stride*h)
96        image = ImageWrapper(0, 0, w, h, rgb_data, pixel_format, 32, stride, planes=ImageWrapper.PACKED, thread_safe=True)
97        #l = len(rgb_data)
98    else:
99        raise Exception("don't know how to create a %s image" % pixel_format)
100    #log("make_test_image%30s took %3ims for %6iMBytes",
101    #    (pixel_format, w, h), 1000*(monotonic()-start), l//1024//1024)
102    return image
103
104
105def testdecoder(decoder_module, full):
106    codecs = list(decoder_module.get_encodings())
107    for encoding in tuple(codecs):
108        try:
109            testdecoding(decoder_module, encoding, full)
110        except Exception as e:
111            log("%s: %s decoding failed", decoder_module.get_type(), encoding, exc_info=True)
112            log.warn("%s: %s decoding failed: %s", decoder_module.get_type(), encoding, e)
113            del e
114            codecs.remove(encoding)
115    if not codecs:
116        log.error("%s: all the codecs have failed! (%s)",
117                  decoder_module.get_type(), csv(decoder_module.get_encodings()))
118    return tuple(codecs)
119
120def testdecoding(decoder_module, encoding, full):
121    W = 24
122    H = 16
123    test_data_set = TEST_COMPRESSED_DATA.get(encoding)
124    if not test_data_set:
125        log("%s: no test data for %s", decoder_module.get_type(), encoding)
126        return
127    for cs in decoder_module.get_input_colorspaces(encoding):
128        e = decoder_module.Decoder()
129        try:
130            e.init_context(encoding, W, H, cs)
131            test_data = test_data_set.get(cs)
132            if test_data:
133                log("%s: testing %s / %s with %s bytes of data",
134                    decoder_module.get_type(), encoding, cs, len(test_data))
135                image = e.decompress_image(test_data)
136                assert image is not None, "failed to decode test data for encoding '%s' with colorspace '%s'" % (encoding, cs)
137                assert image.get_width()==W, "expected image of width %s but got %s" % (W, image.get_width())
138                assert image.get_height()==H, "expected image of height %s but got %s" % (H, image.get_height())
139            if full:
140                log("%s: testing %s / %s with junk data", decoder_module.get_type(), encoding, cs)
141                #test failures:
142                try:
143                    image = e.decompress_image(b"junk")
144                except Exception:
145                    image = None
146                if image is not None:
147                    raise Exception("decoding junk with %s should have failed, got %s instead" % (decoder_module.get_type(), image))
148        finally:
149            e.clean()
150
151
152def testencoder(encoder_module, full):
153    codecs = list(encoder_module.get_encodings())
154    for encoding in tuple(codecs):
155        try:
156            testencoding(encoder_module, encoding, full)
157        except Exception as e:
158            log("%s: %s encoding failed", encoder_module.get_type(), encoding, exc_info=True)
159            log.warn("%s: %s encoding failed: %s", encoder_module.get_type(), encoding, e)
160            del e
161            codecs.remove(encoding)
162    if not codecs:
163        log.error("%s: all the codecs have failed! (%s)",
164                  encoder_module.get_type(), csv(encoder_module.get_encodings()))
165    return tuple(codecs)
166
167def testencoding(encoder_module, encoding, full):
168    #test a bit bigger so we exercise more code:
169    W = 64
170    H = 32
171    do_testencoding(encoder_module, encoding, W, H, full)
172
173def get_encoder_max_sizes(encoder_module):
174    w, h = TEST_LIMIT_W, TEST_LIMIT_H
175    for encoding in encoder_module.get_encodings():
176        ew, eh = get_encoder_max_size(encoder_module, encoding)
177        w = min(w, ew)
178        h = min(h, eh)
179    return w, h
180
181def get_encoder_max_size(encoder_module, encoding, limit_w=TEST_LIMIT_W, limit_h=TEST_LIMIT_H):
182    #probe to find the max dimensions:
183    #(it may go higher but we don't care as windows can't)
184    def einfo():
185        return "%s %s %s" % (encoder_module.get_type(), encoding, encoder_module.get_version())
186    log("get_encoder_max_size%s", (encoder_module, encoding, limit_w, limit_h))
187    maxw = w = 512
188    while w<=limit_w:
189        try:
190            do_testencoding(encoder_module, encoding, w, 128)
191            maxw = w
192            w *= 2
193        except Exception as e:
194            log("%s is limited to max width=%i for %s:", einfo(), maxw, encoding)
195            log(" %s", e)
196            del e
197            break
198    log("%s max width=%i", einfo(), maxw)
199    maxh = h = 512
200    while h<=limit_h:
201        try:
202            do_testencoding(encoder_module, encoding, 128, h)
203            maxh = h
204            h *= 2
205        except Exception as e:
206            log("%s is limited to max height=%i for %s:", einfo(), maxh, encoding)
207            log(" %s", e)
208            del e
209            break
210    log("%s max height=%i", einfo(), maxh)
211    #now try combining width and height
212    #as there might be a lower limit based on the total number of pixels:
213    MAX_WIDTH, MAX_HEIGHT = maxw, maxh
214    #start at half:
215    v = max(512, min(maxw, maxh)//2)
216    while v<max(limit_w, limit_h):
217        for tw, th in ((v, v), (v*2, v)):
218            if tw>limit_w or th>limit_h:
219                continue
220            try:
221                w = min(maxw, tw)
222                h = min(maxh, th)
223                do_testencoding(encoder_module, encoding, w, h)
224                log("%s can handle %ix%i for %s", einfo(), w, h, encoding)
225                MAX_WIDTH, MAX_HEIGHT = w, h
226            except Exception as e:
227                log("%s is limited to %ix%i for %s", einfo(), MAX_WIDTH, MAX_HEIGHT, encoding)
228                log(" %s", e)
229                del e
230                break
231        v *= 2
232    log("%s max dimensions for %s: %ix%i", einfo(), encoding, MAX_WIDTH, MAX_HEIGHT)
233    return MAX_WIDTH, MAX_HEIGHT
234
235
236def do_testencoding(encoder_module, encoding, W, H, full=False, limit_w=TEST_LIMIT_W, limit_h=TEST_LIMIT_H):
237    for cs_in in encoder_module.get_input_colorspaces(encoding):
238        for cs_out in encoder_module.get_output_colorspaces(encoding, cs_in):
239            e = encoder_module.Encoder()
240            try:
241                options = typedict({
242                    "b-frames" : True,
243                    "dst-formats" : [cs_out],
244                    "quality" : 50,
245                    "speed" : 50,
246                    })
247                e.init_context(encoding, W, H, cs_in, options)
248                for i in range(2):
249                    image = make_test_image(cs_in, W, H)
250                    v = e.compress_image(image)
251                    if v is None:
252                        raise Exception("%s compression failed" % encoding)
253                    data, meta = v
254                    if not data:
255                        delayed = meta.get("delayed", 0)
256                        assert delayed>0, "data is empty and there are no delayed frames!"
257                        if i>0:
258                            #now we should get one:
259                            data, meta = e.flush(delayed)
260                del image
261                assert data is not None, "None data for %s using %s encoding with %s / %s" % (encoder_module.get_type(), encoding, cs_in, cs_out)
262                assert data, "no compressed data for %s using %s encoding with %s / %s" % (encoder_module.get_type(), encoding, cs_in, cs_out)
263                assert meta is not None, "missing metadata for %s using %s encoding with %s / %s" % (encoder_module.get_type(), encoding, cs_in, cs_out)
264                log("%s: %s / %s / %s passed", encoder_module, encoding, cs_in, cs_out)
265                #print("test_encoder: %s.compress_image(%s)=%s" % (encoder_module.get_type(), image, (data, meta)))
266                #print("compressed data with %s: %s bytes (%s), metadata: %s" % (encoder_module.get_type(), len(data), type(data), meta))
267                #print("compressed data(%s, %s)=%s" % (encoding, cs_in, binascii.hexlify(data)))
268                if full:
269                    wrong_formats = [x for x in ("YUV420P", "YUV444P", "BGRX", "r210") if x!=cs_in]
270                    #log("wrong formats (not %s): %s", cs_in, wrong_formats)
271                    if wrong_formats:
272                        wrong_format = wrong_formats[0]
273                        try:
274                            image = make_test_image(wrong_format, W, H)
275                            out = e.compress_image(None, image, options=options)
276                        except Exception:
277                            out = None
278                        assert out is None, "encoder %s should have failed using %s encoding with %s instead of %s / %s" % (encoder_module.get_type(), encoding, wrong_format, cs_in, cs_out)
279                    for w,h in ((W//2, H//2), (W*2, H//2), (W//2, H**2)):
280                        if w>limit_w or h>limit_h:
281                            continue
282                        try:
283                            image = make_test_image(cs_in, w, h)
284                            out = e.compress_image(None, image, options=options)
285                        except Exception:
286                            out = None
287                        assert out is None, "encoder %s, info=%s should have failed using %s encoding with invalid size %ix%i vs %ix%i" % (encoder_module.get_type(), e.get_info(), encoding, w, h, W, H)
288            finally:
289                e.clean()
290
291
292def testcsc(csc_module, scaling=True, full=False, test_cs_in=None, test_cs_out=None):
293    W = 48
294    H = 32
295    log("test_csc(%s, %s, %s, %s)", csc_module, full, test_cs_in, test_cs_out)
296    do_testcsc(csc_module, W, H, W, H, full, test_cs_in, test_cs_out)
297    if full and scaling:
298        do_testcsc(csc_module, W, H, W*2, H*2, full, test_cs_in, test_cs_out)
299        do_testcsc(csc_module, W, H, W//2, H//2, full, test_cs_in, test_cs_out)
300
301def get_csc_max_size(colorspace_converter, test_cs_in=None, test_cs_out=None, limit_w=TEST_LIMIT_W, limit_h=TEST_LIMIT_H):
302    #probe to find the max dimensions:
303    #(it may go higher but we don't care as windows can't)
304    MAX_WIDTH, MAX_HEIGHT = 512, 512
305    #as there might be a lower limit based on the total number of pixels:
306    v = 512
307    while v<=min(limit_w, limit_h):
308        for tw, th in ((v, v), (v*2, v)):
309            if tw>limit_w or th>limit_h:
310                break
311            try:
312                do_testcsc(colorspace_converter, tw, th, tw, th, False, test_cs_in, test_cs_out, limit_w, limit_h)
313                log("%s can handle %ix%i", colorspace_converter, tw, th)
314                MAX_WIDTH, MAX_HEIGHT = tw, th
315            except Exception:
316                log("%s is limited to %ix%i for %s",
317                    colorspace_converter, MAX_WIDTH, MAX_HEIGHT, (test_cs_in, test_cs_out), exc_info=True)
318                break
319        v *= 2
320    log("%s max dimensions: %ix%i", colorspace_converter, MAX_WIDTH, MAX_HEIGHT)
321    return MAX_WIDTH, MAX_HEIGHT
322
323
324def do_testcsc(csc_module, iw, ih, ow, oh, full=False, test_cs_in=None, test_cs_out=None, limit_w=TEST_LIMIT_W, limit_h=TEST_LIMIT_H):
325    log("do_testcsc%s", (csc_module, iw, ih, ow, oh, full, test_cs_in, test_cs_out, TEST_LIMIT_W, TEST_LIMIT_H))
326    cs_in_list = test_cs_in
327    if cs_in_list is None:
328        cs_in_list = csc_module.get_input_colorspaces()
329    for cs_in in cs_in_list:
330        cs_out_list = test_cs_out
331        if cs_out_list is None:
332            cs_out_list = csc_module.get_output_colorspaces(cs_in)
333        for cs_out in cs_out_list:
334            log("%s: testing %s / %s", csc_module.get_type(), cs_in, cs_out)
335            e = csc_module.ColorspaceConverter()
336            try:
337                e.init_context(iw, ih, cs_in, ow, oh, cs_out)
338                image = make_test_image(cs_in, iw, ih)
339                out = e.convert_image(image)
340                #print("convert_image(%s)=%s" % (image, out))
341                assert out.get_width()==ow, "expected image of width %s but got %s" % (ow, out.get_width())
342                assert out.get_height()==oh, "expected image of height %s but got %s" % (oh, out.get_height())
343                assert out.get_pixel_format()==cs_out, "expected pixel format %s but got %s" % (cs_out, out.get_pixel_format())
344                if full:
345                    for w,h in ((iw*2, ih//2), (iw//2, ih**2)):
346                        if w>limit_w or h>limit_h:
347                            continue
348                        try:
349                            image = make_test_image(cs_in, w, h)
350                            out = e.convert_image(image)
351                        except Exception:
352                            out = None
353                        if out is not None:
354                            raise Exception("converting an image of a smaller size with %s should have failed, got %s instead" % (csc_module.get_type(), out))
355            finally:
356                e.clean()
357