image_test.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. # -*- coding: utf-8 -*-
  2. import array
  3. import os
  4. import tempfile
  5. import unittest
  6. import glob
  7. from pygame.tests.test_utils import example_path, png
  8. import pygame, pygame.image, pygame.pkgdata
  9. from pygame.compat import xrange_, ord_, unicode_
  10. def test_magic(f, magic_hex):
  11. """ tests a given file to see if the magic hex matches.
  12. """
  13. data = f.read(len(magic_hex))
  14. if len(data) != len(magic_hex):
  15. return 0
  16. for i in range(len(magic_hex)):
  17. if magic_hex[i] != ord_(data[i]):
  18. return 0
  19. return 1
  20. class ImageModuleTest( unittest.TestCase ):
  21. def testLoadIcon(self):
  22. """ see if we can load the pygame icon.
  23. """
  24. f = pygame.pkgdata.getResource("pygame_icon.bmp")
  25. self.assertEqual(f.mode, "rb")
  26. surf = pygame.image.load_basic(f)
  27. self.assertEqual(surf.get_at((0,0)),(5, 4, 5, 255))
  28. self.assertEqual(surf.get_height(),32)
  29. self.assertEqual(surf.get_width(),32)
  30. def testLoadPNG(self):
  31. """ see if we can load a png with color values in the proper channels.
  32. """
  33. # Create a PNG file with known colors
  34. reddish_pixel = (210, 0, 0, 255)
  35. greenish_pixel = (0, 220, 0, 255)
  36. bluish_pixel = (0, 0, 230, 255)
  37. greyish_pixel = (110, 120, 130, 140)
  38. pixel_array = [reddish_pixel + greenish_pixel,
  39. bluish_pixel + greyish_pixel]
  40. f_descriptor, f_path = tempfile.mkstemp(suffix='.png')
  41. with os.fdopen(f_descriptor, 'wb') as f:
  42. w = png.Writer(2, 2, alpha=True)
  43. w.write(f, pixel_array)
  44. # Read the PNG file and verify that pygame interprets it correctly
  45. surf = pygame.image.load(f_path)
  46. self.assertEqual(surf.get_at((0, 0)), reddish_pixel)
  47. self.assertEqual(surf.get_at((1, 0)), greenish_pixel)
  48. self.assertEqual(surf.get_at((0, 1)), bluish_pixel)
  49. self.assertEqual(surf.get_at((1, 1)), greyish_pixel)
  50. # Read the PNG file obj. and verify that pygame interprets it correctly
  51. with open(f_path, 'rb') as f:
  52. surf = pygame.image.load(f)
  53. self.assertEqual(surf.get_at((0, 0)), reddish_pixel)
  54. self.assertEqual(surf.get_at((1, 0)), greenish_pixel)
  55. self.assertEqual(surf.get_at((0, 1)), bluish_pixel)
  56. self.assertEqual(surf.get_at((1, 1)), greyish_pixel)
  57. os.remove(f_path)
  58. def testLoadJPG(self):
  59. """ see if we can load a jpg.
  60. """
  61. f = example_path('data/alien1.jpg') # normalized
  62. # f = os.path.join("examples", "data", "alien1.jpg")
  63. surf = pygame.image.load(f)
  64. with open(f, "rb") as f:
  65. surf = pygame.image.load(f)
  66. # with open(os.path.join("examples", "data", "alien1.jpg"), "rb") as f:
  67. # surf = pygame.image.load(open(os.path.join("examples", "data",
  68. # "alien1.jpg"), "rb"))
  69. def testSaveJPG(self):
  70. """ JPG equivalent to issue #211 - color channel swapping
  71. Make sure the SDL surface color masks represent the rgb memory format
  72. required by the JPG library. The masks are machine endian dependent
  73. """
  74. from pygame import Color, Rect
  75. # The source image is a 2 by 2 square of four colors. Since JPEG is
  76. # lossy, there can be color bleed. Make each color square 16 by 16,
  77. # to avoid the significantly color value distorts found at color
  78. # boundaries due to the compression value set by Pygame.
  79. square_len = 16
  80. sz = 2 * square_len, 2 * square_len
  81. # +---------------------------------+
  82. # | red | green |
  83. # |----------------+----------------|
  84. # | blue | (255, 128, 64) |
  85. # +---------------------------------+
  86. #
  87. # as (rect, color) pairs.
  88. def as_rect(square_x, square_y):
  89. return Rect(square_x * square_len, square_y * square_len,
  90. square_len, square_len)
  91. squares = [(as_rect(0, 0), Color("red")),
  92. (as_rect(1, 0), Color("green")),
  93. (as_rect(0, 1), Color("blue")),
  94. (as_rect(1, 1), Color(255, 128, 64))]
  95. # A surface format which is not directly usable with libjpeg.
  96. surf = pygame.Surface(sz, 0, 32)
  97. for rect, color in squares:
  98. surf.fill(color, rect)
  99. # Assume pygame.image.Load works correctly as it is handled by the
  100. # third party SDL_image library.
  101. f_path = tempfile.mktemp(suffix='.jpg')
  102. pygame.image.save(surf, f_path)
  103. jpg_surf = pygame.image.load(f_path)
  104. # Allow for small differences in the restored colors.
  105. def approx(c):
  106. mask = 0xFC
  107. return pygame.Color(c.r & mask, c.g & mask, c.b & mask)
  108. offset = square_len // 2
  109. for rect, color in squares:
  110. posn = rect.move((offset, offset)).topleft
  111. self.assertEqual(approx(jpg_surf.get_at(posn)), approx(color))
  112. def testSavePNG32(self):
  113. """ see if we can save a png with color values in the proper channels.
  114. """
  115. # Create a PNG file with known colors
  116. reddish_pixel = (215, 0, 0, 255)
  117. greenish_pixel = (0, 225, 0, 255)
  118. bluish_pixel = (0, 0, 235, 255)
  119. greyish_pixel = (115, 125, 135, 145)
  120. surf = pygame.Surface((1, 4), pygame.SRCALPHA, 32)
  121. surf.set_at((0, 0), reddish_pixel)
  122. surf.set_at((0, 1), greenish_pixel)
  123. surf.set_at((0, 2), bluish_pixel)
  124. surf.set_at((0, 3), greyish_pixel)
  125. f_path = tempfile.mktemp(suffix='.png')
  126. pygame.image.save(surf, f_path)
  127. try:
  128. # Read the PNG file and verify that pygame saved it correctly
  129. reader = png.Reader(filename=f_path)
  130. width, height, pixels, metadata = reader.asRGBA8()
  131. # pixels is a generator
  132. self.assertEqual(tuple(next(pixels)), reddish_pixel)
  133. self.assertEqual(tuple(next(pixels)), greenish_pixel)
  134. self.assertEqual(tuple(next(pixels)), bluish_pixel)
  135. self.assertEqual(tuple(next(pixels)), greyish_pixel)
  136. finally:
  137. # Ensures proper clean up.
  138. if not reader.file.closed:
  139. reader.file.close()
  140. del reader
  141. os.remove(f_path)
  142. def testSavePNG24(self):
  143. """ see if we can save a png with color values in the proper channels.
  144. """
  145. # Create a PNG file with known colors
  146. reddish_pixel = (215, 0, 0)
  147. greenish_pixel = (0, 225, 0)
  148. bluish_pixel = (0, 0, 235)
  149. greyish_pixel = (115, 125, 135)
  150. surf = pygame.Surface((1, 4), 0, 24)
  151. surf.set_at((0, 0), reddish_pixel)
  152. surf.set_at((0, 1), greenish_pixel)
  153. surf.set_at((0, 2), bluish_pixel)
  154. surf.set_at((0, 3), greyish_pixel)
  155. f_path = tempfile.mktemp(suffix='.png')
  156. pygame.image.save(surf, f_path)
  157. try:
  158. # Read the PNG file and verify that pygame saved it correctly
  159. reader = png.Reader(filename=f_path)
  160. width, height, pixels, metadata = reader.asRGB8()
  161. # pixels is a generator
  162. self.assertEqual(tuple(next(pixels)), reddish_pixel)
  163. self.assertEqual(tuple(next(pixels)), greenish_pixel)
  164. self.assertEqual(tuple(next(pixels)), bluish_pixel)
  165. self.assertEqual(tuple(next(pixels)), greyish_pixel)
  166. finally:
  167. # Ensures proper clean up.
  168. if not reader.file.closed:
  169. reader.file.close()
  170. del reader
  171. os.remove(f_path)
  172. def test_save(self):
  173. s = pygame.Surface((10,10))
  174. s.fill((23,23,23))
  175. magic_hex = {}
  176. magic_hex['jpg'] = [0xff, 0xd8, 0xff, 0xe0]
  177. magic_hex['png'] = [0x89 ,0x50 ,0x4e ,0x47]
  178. # magic_hex['tga'] = [0x0, 0x0, 0xa]
  179. magic_hex['bmp'] = [0x42, 0x4d]
  180. formats = ["jpg", "png", "bmp"]
  181. # uppercase too... JPG
  182. formats = formats + [x.upper() for x in formats]
  183. for fmt in formats:
  184. try:
  185. temp_filename = "%s.%s" % ("tmpimg", fmt)
  186. pygame.image.save(s, temp_filename)
  187. # Using 'with' ensures the file is closed even if test fails.
  188. with open(temp_filename, "rb") as handle:
  189. # Test the magic numbers at the start of the file to ensure
  190. # they are saved as the correct file type.
  191. self.assertEqual((1, fmt), (test_magic(handle,
  192. magic_hex[fmt.lower()]), fmt))
  193. # load the file to make sure it was saved correctly.
  194. # Note load can load a jpg saved with a .png file name.
  195. s2 = pygame.image.load(temp_filename)
  196. #compare contents, might only work reliably for png...
  197. # but because it's all one color it seems to work with jpg.
  198. self.assertEqual(s2.get_at((0,0)), s.get_at((0,0)))
  199. finally:
  200. #clean up the temp file, comment out to leave tmp file after run.
  201. os.remove(temp_filename)
  202. def test_save_colorkey(self):
  203. """ make sure the color key is not changed when saving.
  204. """
  205. s = pygame.Surface((10,10), pygame.SRCALPHA, 32)
  206. s.fill((23,23,23))
  207. s.set_colorkey((0,0,0))
  208. colorkey1 = s.get_colorkey()
  209. p1 = s.get_at((0,0))
  210. temp_filename = "tmpimg.png"
  211. try:
  212. pygame.image.save(s, temp_filename)
  213. s2 = pygame.image.load(temp_filename)
  214. finally:
  215. os.remove(temp_filename)
  216. colorkey2 = s.get_colorkey()
  217. # check that the pixel and the colorkey is correct.
  218. self.assertEqual(colorkey1, colorkey2)
  219. self.assertEqual(p1, s2.get_at((0,0)))
  220. def test_load_unicode_path(self):
  221. import shutil
  222. orig = unicode_(example_path("data/asprite.bmp"))
  223. temp = os.path.join(unicode_(example_path('data')), u'你好.bmp')
  224. shutil.copy(orig, temp)
  225. try:
  226. im = pygame.image.load(temp)
  227. finally:
  228. os.remove(temp)
  229. def _unicode_save(self, temp_file):
  230. im = pygame.Surface((10, 10), 0, 32)
  231. try:
  232. with open(temp_file, 'w') as f:
  233. pass
  234. os.remove(temp_file)
  235. except IOError:
  236. raise unittest.SkipTest('the path cannot be opened')
  237. self.assertFalse(os.path.exists(temp_file))
  238. try:
  239. pygame.image.save(im, temp_file)
  240. self.assertGreater(os.path.getsize(temp_file), 10)
  241. finally:
  242. try:
  243. os.remove(temp_file)
  244. except EnvironmentError:
  245. pass
  246. def test_save_unicode_path(self):
  247. """save unicode object with non-ASCII chars"""
  248. self._unicode_save(u"你好.bmp")
  249. def assertPremultipliedAreEqual(self, string1, string2, source_string):
  250. self.assertEqual(len(string1), len(string2))
  251. block_size = 20
  252. if string1 != string2:
  253. for block_start in xrange_(0, len(string1), block_size):
  254. block_end = min(block_start + block_size, len(string1))
  255. block1 = string1[block_start:block_end]
  256. block2 = string2[block_start:block_end]
  257. if block1 != block2:
  258. source_block = source_string[block_start:block_end]
  259. msg = "string difference in %d to %d of %d:\n%s\n%s\nsource:\n%s" % (block_start, block_end, len(string1), block1.encode("hex"), block2.encode("hex"), source_block.encode("hex"))
  260. self.fail(msg)
  261. def test_to_string__premultiplied(self):
  262. """ test to make sure we can export a surface to a premultiplied alpha string
  263. """
  264. def convertRGBAtoPremultiplied(surface_to_modify):
  265. for x in xrange_(surface_to_modify.get_width()):
  266. for y in xrange_(surface_to_modify.get_height()):
  267. color = surface_to_modify.get_at((x, y))
  268. premult_color = (color[0]*color[3]/255,
  269. color[1]*color[3]/255,
  270. color[2]*color[3]/255,
  271. color[3])
  272. surface_to_modify.set_at((x, y), premult_color)
  273. test_surface = pygame.Surface((256, 256), pygame.SRCALPHA, 32)
  274. for x in xrange_(test_surface.get_width()):
  275. for y in xrange_(test_surface.get_height()):
  276. i = x + y*test_surface.get_width()
  277. test_surface.set_at((x,y), ((i*7) % 256, (i*13) % 256, (i*27) % 256, y))
  278. premultiplied_copy = test_surface.copy()
  279. convertRGBAtoPremultiplied(premultiplied_copy)
  280. self.assertPremultipliedAreEqual(pygame.image.tostring(test_surface, "RGBA_PREMULT"),
  281. pygame.image.tostring(premultiplied_copy, "RGBA"),
  282. pygame.image.tostring(test_surface, "RGBA"))
  283. self.assertPremultipliedAreEqual(pygame.image.tostring(test_surface, "ARGB_PREMULT"),
  284. pygame.image.tostring(premultiplied_copy, "ARGB"),
  285. pygame.image.tostring(test_surface, "ARGB"))
  286. no_alpha_surface = pygame.Surface((256, 256), 0, 24)
  287. self.assertRaises(ValueError, pygame.image.tostring, no_alpha_surface, "RGBA_PREMULT")
  288. # Custom assert method to check for identical surfaces.
  289. def _assertSurfaceEqual(self, surf_a, surf_b, msg=None):
  290. a_width, a_height = surf_a.get_width(), surf_a.get_height()
  291. # Check a few things to see if the surfaces are equal.
  292. self.assertEqual(a_width, surf_b.get_width(), msg)
  293. self.assertEqual(a_height, surf_b.get_height(), msg)
  294. self.assertEqual(surf_a.get_size(), surf_b.get_size(), msg)
  295. self.assertEqual(surf_a.get_rect(), surf_b.get_rect(), msg)
  296. self.assertEqual(surf_a.get_colorkey(), surf_b.get_colorkey(), msg)
  297. self.assertEqual(surf_a.get_alpha(), surf_b.get_alpha(), msg)
  298. self.assertEqual(surf_a.get_flags(), surf_b.get_flags(), msg)
  299. self.assertEqual(surf_a.get_bitsize(), surf_b.get_bitsize(), msg)
  300. self.assertEqual(surf_a.get_bytesize(), surf_b.get_bytesize(), msg)
  301. # Anything else?
  302. # Making the method lookups local for a possible speed up.
  303. surf_a_get_at = surf_a.get_at
  304. surf_b_get_at = surf_b.get_at
  305. for y in xrange_(a_height):
  306. for x in xrange_(a_width):
  307. self.assertEqual(surf_a_get_at((x, y)), surf_b_get_at((x, y)),
  308. msg)
  309. def test_fromstring__and_tostring(self):
  310. """Ensure methods tostring() and fromstring() are symmetric."""
  311. ####################################################################
  312. def RotateRGBAtoARGB(str_buf):
  313. byte_buf = array.array("B", str_buf)
  314. num_quads = len(byte_buf)//4
  315. for i in xrange_(num_quads):
  316. alpha = byte_buf[i*4 + 3]
  317. byte_buf[i*4 + 3] = byte_buf[i*4 + 2]
  318. byte_buf[i*4 + 2] = byte_buf[i*4 + 1]
  319. byte_buf[i*4 + 1] = byte_buf[i*4 + 0]
  320. byte_buf[i*4 + 0] = alpha
  321. return byte_buf.tostring()
  322. ####################################################################
  323. def RotateARGBtoRGBA(str_buf):
  324. byte_buf = array.array("B", str_buf)
  325. num_quads = len(byte_buf)//4
  326. for i in xrange_(num_quads):
  327. alpha = byte_buf[i*4 + 0]
  328. byte_buf[i*4 + 0] = byte_buf[i*4 + 1]
  329. byte_buf[i*4 + 1] = byte_buf[i*4 + 2]
  330. byte_buf[i*4 + 2] = byte_buf[i*4 + 3]
  331. byte_buf[i*4 + 3] = alpha
  332. return byte_buf.tostring()
  333. ####################################################################
  334. test_surface = pygame.Surface((64, 256), flags=pygame.SRCALPHA,
  335. depth=32)
  336. for i in xrange_(256):
  337. for j in xrange_(16):
  338. intensity = j*16 + 15
  339. test_surface.set_at((j + 0, i), (intensity, i, i, i))
  340. test_surface.set_at((j + 16, i), (i, intensity, i, i))
  341. test_surface.set_at((j + 32, i), (i, i, intensity, i))
  342. test_surface.set_at((j + 32, i), (i, i, i, intensity))
  343. self._assertSurfaceEqual(test_surface, test_surface,
  344. 'failing with identical surfaces')
  345. rgba_buf = pygame.image.tostring(test_surface, "RGBA")
  346. rgba_buf = RotateARGBtoRGBA(RotateRGBAtoARGB(rgba_buf))
  347. test_rotate_functions = pygame.image.fromstring(
  348. rgba_buf, test_surface.get_size(), "RGBA")
  349. self._assertSurfaceEqual(test_surface, test_rotate_functions,
  350. 'rotate functions are not symmetric')
  351. rgba_buf = pygame.image.tostring(test_surface, "RGBA")
  352. argb_buf = RotateRGBAtoARGB(rgba_buf)
  353. test_from_argb_string = pygame.image.fromstring(
  354. argb_buf, test_surface.get_size(), "ARGB")
  355. self._assertSurfaceEqual(test_surface, test_from_argb_string,
  356. '"RGBA" rotated to "ARGB" failed')
  357. argb_buf = pygame.image.tostring(test_surface, "ARGB")
  358. rgba_buf = RotateARGBtoRGBA(argb_buf)
  359. test_to_argb_string = pygame.image.fromstring(
  360. rgba_buf, test_surface.get_size(), "RGBA")
  361. self._assertSurfaceEqual(test_surface, test_to_argb_string,
  362. '"ARGB" rotated to "RGBA" failed')
  363. for fmt in ('ARGB', 'RGBA'):
  364. fmt_buf = pygame.image.tostring(test_surface, fmt)
  365. test_to_from_fmt_string = pygame.image.fromstring(
  366. fmt_buf, test_surface.get_size(), fmt)
  367. self._assertSurfaceEqual(test_surface, test_to_from_fmt_string,
  368. 'tostring/fromstring functions are not '
  369. 'symmetric with "{}" format'.format(fmt))
  370. def todo_test_frombuffer(self):
  371. # __doc__ (as of 2008-08-02) for pygame.image.frombuffer:
  372. # pygame.image.frombuffer(string, size, format): return Surface
  373. # create a new Surface that shares data inside a string buffer
  374. #
  375. # Create a new Surface that shares pixel data directly from the string
  376. # buffer. This method takes the same arguments as
  377. # pygame.image.fromstring(), but is unable to vertically flip the
  378. # source data.
  379. #
  380. # This will run much faster than pygame.image.fromstring, since no
  381. # pixel data must be allocated and copied.
  382. self.fail()
  383. def todo_test_get_extended(self):
  384. # __doc__ (as of 2008-08-02) for pygame.image.get_extended:
  385. # pygame.image.get_extended(): return bool
  386. # test if extended image formats can be loaded
  387. #
  388. # If pygame is built with extended image formats this function will
  389. # return True. It is still not possible to determine which formats
  390. # will be available, but generally you will be able to load them all.
  391. self.fail()
  392. def todo_test_load_basic(self):
  393. # __doc__ (as of 2008-08-02) for pygame.image.load_basic:
  394. # pygame.image.load(filename): return Surface
  395. # pygame.image.load(fileobj, namehint=): return Surface
  396. # load new image from a file
  397. self.fail()
  398. def todo_test_load_extended(self):
  399. # __doc__ (as of 2008-08-02) for pygame.image.load_extended:
  400. # pygame module for image transfer
  401. self.fail()
  402. def todo_test_save_extended(self):
  403. # __doc__ (as of 2008-08-02) for pygame.image.save_extended:
  404. # pygame module for image transfer
  405. self.fail()
  406. def threads_load(self, images):
  407. import pygame.threads
  408. for i in range(10):
  409. surfs = pygame.threads.tmap(pygame.image.load, images)
  410. for s in surfs:
  411. self.assertIsInstance(s, pygame.Surface)
  412. def test_load_png_threads(self):
  413. self.threads_load(glob.glob(example_path("data/*.png")))
  414. def test_load_jpg_threads(self):
  415. self.threads_load(glob.glob(example_path("data/*.jpg")))
  416. def test_load_bmp_threads(self):
  417. self.threads_load(glob.glob(example_path("data/*.bmp")))
  418. def test_load_gif_threads(self):
  419. self.threads_load(glob.glob(example_path("data/*.gif")))
  420. if __name__ == '__main__':
  421. unittest.main()