import os
import unittest
from pygame.tests import test_utils
from pygame.tests.test_utils import (
    example_path,
    SurfaceSubclass,
)

try:
    from pygame.tests.test_utils.arrinter import *
except (ImportError, NameError):
    pass
import pygame
from pygame.locals import *
from pygame.bufferproxy import BufferProxy

import platform
import gc
import weakref
import ctypes
import itertools

IS_PYPY = "PyPy" == platform.python_implementation()


class SurfaceTypeTest(unittest.TestCase):
    def test_surface__pixel_format_as_surface_subclass(self):
        """Ensure a subclassed surface can be used for pixel format
        when creating a new surface."""
        expected_depth = 16
        expected_flags = SRCALPHA
        expected_size = (13, 37)
        depth_surface = SurfaceSubclass((11, 21), expected_flags, expected_depth)

        surface = pygame.Surface(expected_size, expected_flags, depth_surface)

        self.assertIsNot(surface, depth_surface)
        self.assertIsInstance(surface, pygame.Surface)
        self.assertNotIsInstance(surface, SurfaceSubclass)
        self.assertEqual(surface.get_size(), expected_size)
        self.assertEqual(surface.get_flags(), expected_flags)
        self.assertEqual(surface.get_bitsize(), expected_depth)

    def test_surface_created_opaque_black(self):
        surf = pygame.Surface((20, 20))
        self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 255))

        # See https://github.com/pygame-community/pygame-ce/issues/796
        pygame.display.set_mode((500, 500))
        surf = pygame.Surface((20, 20))
        self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 255))

    def test_set_clip(self):
        """see if surface.set_clip(None) works correctly."""
        s = pygame.Surface((800, 600))
        r = pygame.Rect(10, 10, 10, 10)
        s.set_clip(r)
        r.move_ip(10, 0)
        s.set_clip(None)
        res = s.get_clip()
        # this was garbled before.
        self.assertEqual(res[0], 0)
        self.assertEqual(res[2], 800)

    def test_print(self):
        surf = pygame.Surface((70, 70), 0, 32)
        self.assertEqual(repr(surf), "<Surface(70x70x32)>")

        surf_global = pygame.Surface((70, 70), pygame.SRCALPHA, 32)
        self.assertEqual(repr(surf_global), "<Surface(70x70x32, global_alpha=255)>")

        surf_colorkey = pygame.Surface((70, 70), 0, 32)
        surf_colorkey.set_colorkey("purple")
        self.assertEqual(
            repr(surf_colorkey), "<Surface(70x70x32, colorkey=(160, 32, 240, 255))>"
        )

        surf_colorkey_global = pygame.Surface((70, 70), 32)
        surf_colorkey_global.set_colorkey("orange")
        surf_colorkey_global.set_alpha(200)
        self.assertEqual(
            repr(surf_colorkey_global),
            "<Surface(70x70x32, colorkey=(255, 165, 0, 255), global_alpha=200)>",
        )

        surf_16_global = pygame.Surface((70, 70), 0, 16)
        surf_16_global.set_alpha(200)
        self.assertEqual(repr(surf_16_global), "<Surface(70x70x16, global_alpha=200)>")

        surf_8 = pygame.Surface((70, 70), 0, 8)
        self.assertEqual(repr(surf_8), "<Surface(70x70x8)>")

    def test_keyword_arguments(self):
        surf = pygame.Surface((70, 70), flags=SRCALPHA, depth=32)
        self.assertEqual(surf.get_flags() & SRCALPHA, SRCALPHA)
        self.assertEqual(surf.get_bitsize(), 32)

        # sanity check to make sure the check below is valid
        surf_16 = pygame.Surface((70, 70), 0, 16)
        self.assertEqual(surf_16.get_bytesize(), 2)

        # try again with an argument list
        surf_16 = pygame.Surface((70, 70), depth=16)
        self.assertEqual(surf_16.get_bytesize(), 2)

    def test_set_at(self):
        # 24bit surfaces
        s = pygame.Surface((100, 100), 0, 24)
        s.fill((0, 0, 0))

        # set it with a tuple.
        s.set_at((0, 0), (10, 10, 10, 255))
        r = s.get_at((0, 0))
        self.assertIsInstance(r, pygame.Color)
        self.assertEqual(r, (10, 10, 10, 255))

        s.set_at(pygame.Vector2(0, 0), (10, 10, 10, 255))
        r = s.get_at(pygame.Vector2(0, 0))
        self.assertIsInstance(r, pygame.Color)
        self.assertEqual(r, (10, 10, 10, 255))

        # try setting a color with a single integer.
        s.fill((0, 0, 0, 255))
        s.set_at((10, 1), 0x0000FF)
        r = s.get_at((10, 1))
        self.assertEqual(r, (0, 0, 255, 255))

    def test_set_at__big_endian(self):
        """png files are loaded in big endian format (BGR rather than RGB)"""
        pygame.display.init()
        try:
            image = pygame.image.load(example_path(os.path.join("data", "BGR.png")))
            # Check they start red, green and blue
            self.assertEqual(image.get_at((10, 10)), pygame.Color(255, 0, 0))
            self.assertEqual(image.get_at((10, 20)), pygame.Color(0, 255, 0))
            self.assertEqual(image.get_at((10, 40)), pygame.Color(0, 0, 255))
            # Set three pixels that are already red, green, blue
            # to red, green and, blue with set_at:
            image.set_at((10, 10), pygame.Color(255, 0, 0))
            image.set_at((10, 20), pygame.Color(0, 255, 0))
            image.set_at((10, 40), pygame.Color(0, 0, 255))

            # Check they still are
            self.assertEqual(image.get_at((10, 10)), pygame.Color(255, 0, 0))
            self.assertEqual(image.get_at((10, 20)), pygame.Color(0, 255, 0))
            self.assertEqual(image.get_at((10, 40)), pygame.Color(0, 0, 255))

        finally:
            pygame.display.quit()

    def test_SRCALPHA(self):
        # has the flag been passed in ok?
        surf = pygame.Surface((70, 70), SRCALPHA, 32)
        self.assertEqual(surf.get_flags() & SRCALPHA, SRCALPHA)

        # 24bit surfaces can not have SRCALPHA.
        self.assertRaises(ValueError, pygame.Surface, (100, 100), pygame.SRCALPHA, 24)

        # if we have a 32 bit surface, the SRCALPHA should have worked too.
        surf2 = pygame.Surface((70, 70), SRCALPHA)
        if surf2.get_bitsize() == 32:
            self.assertEqual(surf2.get_flags() & SRCALPHA, SRCALPHA)

    def test_flags_default0_nodisplay(self):
        """is set to zero, and SRCALPHA is not set by default with no display initialized."""
        pygame.display.quit()
        surf = pygame.Surface((70, 70))
        self.assertEqual(surf.get_flags() & SRCALPHA, 0)

    def test_flags_default0_display(self):
        """is set to zero, and SRCALPH is not set by default even when the display is initialized."""
        pygame.display.set_mode((320, 200))
        try:
            surf = pygame.Surface((70, 70))
            self.assertEqual(surf.get_flags() & SRCALPHA, 0)
        finally:
            pygame.display.quit()

    def test_masks(self):
        def make_surf(bpp, flags, masks):
            pygame.Surface((10, 10), flags, bpp, masks)

        # With some masks SDL_CreateRGBSurface does not work properly.
        masks = (0xFF000000, 0xFF0000, 0xFF00, 0)
        self.assertEqual(make_surf(32, 0, masks), None)
        # For 24 and 32 bit surfaces Pygame assumes no losses.
        masks = (0x7F0000, 0xFF00, 0xFF, 0)
        self.assertRaises(ValueError, make_surf, 24, 0, masks)
        self.assertRaises(ValueError, make_surf, 32, 0, masks)
        # What contiguous bits in a mask.
        masks = (0x6F0000, 0xFF00, 0xFF, 0)
        self.assertRaises(ValueError, make_surf, 32, 0, masks)

    def test_get_bounding_rect(self):
        surf = pygame.Surface((70, 70), SRCALPHA, 32)
        surf.fill((0, 0, 0, 0))
        bound_rect = surf.get_bounding_rect()
        self.assertEqual(bound_rect.width, 0)
        self.assertEqual(bound_rect.height, 0)
        surf.set_at((30, 30), (255, 255, 255, 1))
        bound_rect = surf.get_bounding_rect()
        self.assertEqual(bound_rect.left, 30)
        self.assertEqual(bound_rect.top, 30)
        self.assertEqual(bound_rect.width, 1)
        self.assertEqual(bound_rect.height, 1)
        surf.set_at((29, 29), (255, 255, 255, 1))
        bound_rect = surf.get_bounding_rect()
        self.assertEqual(bound_rect.left, 29)
        self.assertEqual(bound_rect.top, 29)
        self.assertEqual(bound_rect.width, 2)
        self.assertEqual(bound_rect.height, 2)

        surf = pygame.Surface((70, 70), 0, 24)
        surf.fill((0, 0, 0))
        bound_rect = surf.get_bounding_rect()
        self.assertEqual(bound_rect.width, surf.get_width())
        self.assertEqual(bound_rect.height, surf.get_height())

        surf.set_colorkey((0, 0, 0))
        bound_rect = surf.get_bounding_rect()
        self.assertEqual(bound_rect.width, 0)
        self.assertEqual(bound_rect.height, 0)
        surf.set_at((30, 30), (255, 255, 255))
        bound_rect = surf.get_bounding_rect()
        self.assertEqual(bound_rect.left, 30)
        self.assertEqual(bound_rect.top, 30)
        self.assertEqual(bound_rect.width, 1)
        self.assertEqual(bound_rect.height, 1)
        surf.set_at((60, 60), (255, 255, 255))
        bound_rect = surf.get_bounding_rect()
        self.assertEqual(bound_rect.left, 30)
        self.assertEqual(bound_rect.top, 30)
        self.assertEqual(bound_rect.width, 31)
        self.assertEqual(bound_rect.height, 31)

        # pygame-ce issue #195
        pygame.display.init()
        try:
            surf = pygame.Surface((4, 1), 0, 8)
            surf.fill((255, 255, 255))
            surf.get_bounding_rect()  # Segfault.
        finally:
            pygame.display.quit()

    def test_copy(self):
        """Ensure a surface can be copied."""
        color = (25, 25, 25, 25)
        s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32)
        s1.fill(color)

        s2 = s1.copy()

        s1rect = s1.get_rect()
        s2rect = s2.get_rect()

        self.assertEqual(s1rect.size, s2rect.size)
        self.assertEqual(s2.get_at((10, 10)), color)

    def test_deepcopy(self):
        """Ensure a surface can be copied."""
        color = (25, 25, 25, 25)
        s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32)
        s1.fill(color)

        import copy

        s2 = copy.deepcopy(s1)

        s1rect = s1.get_rect()
        s2rect = s2.get_rect()

        self.assertEqual(s1rect.size, s2rect.size)
        self.assertEqual(s2.get_at((10, 10)), color)

    def test_fill(self):
        """Ensure a surface can be filled."""
        color = (25, 25, 25, 25)
        fill_rect = pygame.Rect(0, 0, 16, 16)
        s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32)
        s1.fill(color, fill_rect)

        for pt in test_utils.rect_area_pts(fill_rect):
            self.assertEqual(s1.get_at(pt), color)

        for pt in test_utils.rect_outer_bounds(fill_rect):
            self.assertNotEqual(s1.get_at(pt), color)

    def test_fill_rle(self):
        """Test RLEACCEL flag with fill()"""
        color = (250, 25, 25, 255)

        surf = pygame.Surface((32, 32))
        blit_surf = pygame.Surface((32, 32))

        blit_surf.set_colorkey((255, 0, 255), pygame.RLEACCEL)
        self.assertTrue(blit_surf.get_flags() & pygame.RLEACCELOK)
        surf.blit(blit_surf, (0, 0))
        blit_surf.fill(color)
        self.assertEqual(
            blit_surf.mustlock(), (blit_surf.get_flags() & pygame.RLEACCEL) != 0
        )
        self.assertTrue(blit_surf.get_flags() & pygame.RLEACCEL)

    def test_fill_raise_exceptions(self):
        surf = pygame.Surface((5, 5))
        invalid_rect_style_obj = 0
        self.assertRaises(ValueError, surf.fill, "black", rect=invalid_rect_style_obj)
        self.assertRaises(
            pygame.error, surf.fill, "black", special_flags=pygame.BLEND_PREMULTIPLIED
        )
        self.assertRaises(
            pygame.error, surf.fill, "black", special_flags=pygame.BLEND_ALPHA_SDL2
        )

    def test_mustlock_rle(self):
        """Test RLEACCEL flag with mustlock()"""
        surf = pygame.Surface((100, 100))
        blit_surf = pygame.Surface((100, 100))
        blit_surf.set_colorkey((0, 0, 255), pygame.RLEACCEL)
        self.assertTrue(blit_surf.get_flags() & pygame.RLEACCELOK)
        surf.blit(blit_surf, (0, 0))
        self.assertTrue(blit_surf.get_flags() & pygame.RLEACCEL)
        self.assertTrue(blit_surf.mustlock())

    def test_mustlock_surf_alpha_rle(self):
        """Test RLEACCEL flag with mustlock() on a surface
        with per pixel alpha - new feature in SDL2"""
        surf = pygame.Surface((100, 100))
        blit_surf = pygame.Surface((100, 100), depth=32, flags=pygame.SRCALPHA)
        blit_surf.set_colorkey((192, 191, 192, 255), pygame.RLEACCEL)
        self.assertTrue(blit_surf.get_flags() & pygame.RLEACCELOK)
        surf.blit(blit_surf, (0, 0))
        self.assertTrue(blit_surf.get_flags() & pygame.RLEACCEL)
        self.assertTrue(blit_surf.get_flags() & pygame.SRCALPHA)
        self.assertTrue(blit_surf.mustlock())

    def test_copy_rle(self):
        """Test copying a surface set to use run length encoding"""
        s1 = pygame.Surface((32, 32), 24)
        s1.set_colorkey((255, 0, 255), pygame.RLEACCEL)
        self.assertTrue(s1.get_flags() & pygame.RLEACCELOK)

        newsurf = s1.copy()
        self.assertTrue(s1.get_flags() & pygame.RLEACCELOK)
        self.assertTrue(newsurf.get_flags() & pygame.RLEACCELOK)

    def test_subsurface_rle(self):
        """Ensure an RLE sub-surface works independently of its parent."""
        color = (250, 25, 25, 255)
        color2 = (200, 200, 250, 255)
        sub_rect = pygame.Rect(16, 16, 16, 16)
        s0 = pygame.Surface((32, 32), 24)
        s1 = pygame.Surface((32, 32), 24)
        s1.set_colorkey((255, 0, 255), pygame.RLEACCEL)
        s1.fill(color)
        s2 = s1.subsurface(sub_rect)
        s2.fill(color2)
        s0.blit(s1, (0, 0))
        self.assertTrue(s1.get_flags() & pygame.RLEACCEL)
        self.assertTrue(not s2.get_flags() & pygame.RLEACCEL)

    def test_subsurface_rle2(self):
        """Ensure an RLE sub-surface works independently of its parent."""
        color = (250, 25, 25, 255)
        color2 = (200, 200, 250, 255)
        sub_rect = pygame.Rect(16, 16, 16, 16)

        s0 = pygame.Surface((32, 32), 24)
        s1 = pygame.Surface((32, 32), 24)
        s1.set_colorkey((255, 0, 255), pygame.RLEACCEL)
        s1.fill(color)
        s2 = s1.subsurface(sub_rect)
        s2.fill(color2)
        s0.blit(s2, (0, 0))
        self.assertTrue(s1.get_flags() & pygame.RLEACCELOK)
        self.assertTrue(not s2.get_flags() & pygame.RLEACCELOK)

    def test_solarwolf_rle_usage(self):
        """Test for error/crash when calling set_colorkey() followed
        by convert twice in succession. Code originally taken
        from solarwolf."""

        def optimize(img):
            clear = img.get_colorkey()
            img.set_colorkey(clear, RLEACCEL)
            self.assertEqual(img.get_colorkey(), clear)
            return img.convert()

        pygame.display.init()
        try:
            pygame.display.set_mode((640, 480))

            image = pygame.image.load(example_path(os.path.join("data", "alien1.png")))
            image = image.convert()
            orig_colorkey = image.get_colorkey()

            image = optimize(image)
            image = optimize(image)
            self.assertTrue(image.get_flags() & pygame.RLEACCELOK)
            self.assertTrue(not image.get_flags() & pygame.RLEACCEL)
            self.assertEqual(image.get_colorkey(), orig_colorkey)
            self.assertTrue(isinstance(image, pygame.Surface))
        finally:
            pygame.display.quit()

    def test_solarwolf_rle_usage_2(self):
        """Test for RLE status after setting alpha"""

        pygame.display.init()
        try:
            pygame.display.set_mode((640, 480), depth=32)
            blit_to_surf = pygame.Surface((100, 100))

            image = pygame.image.load(example_path(os.path.join("data", "alien1.png")))
            image = image.convert()
            orig_colorkey = image.get_colorkey()

            # set the colorkey with RLEACCEL, should add the RLEACCELOK flag
            image.set_colorkey(orig_colorkey, RLEACCEL)
            self.assertTrue(image.get_flags() & pygame.RLEACCELOK)
            self.assertTrue(not image.get_flags() & pygame.RLEACCEL)

            # now blit the surface - should add the RLEACCEL flag
            blit_to_surf.blit(image, (0, 0))
            self.assertTrue(image.get_flags() & pygame.RLEACCELOK)
            self.assertTrue(image.get_flags() & pygame.RLEACCEL)

            # Now set the alpha, without RLE acceleration - should strip all
            # RLE flags
            image.set_alpha(90)
            self.assertTrue(not image.get_flags() & pygame.RLEACCELOK)
            self.assertTrue(not image.get_flags() & pygame.RLEACCEL)

        finally:
            pygame.display.quit()

    def test_set_alpha__set_colorkey_rle(self):
        pygame.display.init()
        try:
            pygame.display.set_mode((640, 480))
            blit_to_surf = pygame.Surface((80, 71))
            blit_to_surf.fill((255, 255, 255))

            image = pygame.image.load(example_path(os.path.join("data", "alien1.png")))
            image = image.convert()
            orig_colorkey = image.get_colorkey()

            # Add the RLE flag while setting alpha for the whole surface
            image.set_alpha(90, RLEACCEL)
            blit_to_surf.blit(image, (0, 0))
            sample_pixel_rle = blit_to_surf.get_at((50, 50))

            # Now reset the colorkey to the original value with RLE
            self.assertEqual(image.get_colorkey(), orig_colorkey)
            image.set_colorkey(orig_colorkey, RLEACCEL)
            blit_to_surf.fill((255, 255, 255))
            blit_to_surf.blit(image, (0, 0))
            sample_pixel_no_rle = blit_to_surf.get_at((50, 50))

            self.assertAlmostEqual(sample_pixel_rle.r, sample_pixel_no_rle.r, delta=2)
            self.assertAlmostEqual(sample_pixel_rle.g, sample_pixel_no_rle.g, delta=2)
            self.assertAlmostEqual(sample_pixel_rle.b, sample_pixel_no_rle.b, delta=2)

        finally:
            pygame.display.quit()

    def test_fill_negative_coordinates(self):
        # negative coordinates should be clipped by fill, and not draw outside the surface.
        color = (25, 25, 25, 25)
        color2 = (20, 20, 20, 25)
        fill_rect = pygame.Rect(-10, -10, 16, 16)

        s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32)
        r1 = s1.fill(color, fill_rect)
        c = s1.get_at((0, 0))
        self.assertEqual(c, color)

        # make subsurface in the middle to test it doesn't overwrite.
        s2 = s1.subsurface((5, 5, 5, 5))
        r2 = s2.fill(color2, (-3, -3, 5, 5))
        c2 = s1.get_at((4, 4))
        self.assertEqual(c, color)

        # rect returns the area we actually fill.
        r3 = s2.fill(color2, (-30, -30, 5, 5))
        # since we are using negative coords, it should be a zero sized rect.
        self.assertEqual(tuple(r3), (0, 0, 0, 0))

    def test_fill_keyword_args(self):
        """Ensure fill() accepts keyword arguments."""
        color = (1, 2, 3, 255)
        area = (1, 1, 2, 2)
        s1 = pygame.Surface((4, 4), 0, 32)
        s1.fill(special_flags=pygame.BLEND_ADD, color=color, rect=area)

        self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255))
        self.assertEqual(s1.get_at((1, 1)), color)

    ########################################################################

    def test_get_alpha(self):
        """Ensure a surface's alpha value can be retrieved."""
        s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32)

        self.assertEqual(s1.get_alpha(), 255)

        for alpha in (0, 32, 127, 255):
            s1.set_alpha(alpha)
            for t in range(4):
                s1.set_alpha(s1.get_alpha())

            self.assertEqual(s1.get_alpha(), alpha)

    ########################################################################

    def test_get_bytesize(self):
        """Ensure a surface's bit and byte sizes can be retrieved."""
        pygame.display.init()
        try:
            depth = 32
            depth_bytes = 4
            s1 = pygame.Surface((32, 32), pygame.SRCALPHA, depth)

            self.assertEqual(s1.get_bytesize(), depth_bytes)
            self.assertEqual(s1.get_bitsize(), depth)

            depth = 15
            depth_bytes = 2
            s1 = pygame.Surface((32, 32), 0, depth)

            self.assertEqual(s1.get_bytesize(), depth_bytes)
            self.assertEqual(s1.get_bitsize(), depth)

            depth = 12
            depth_bytes = 2
            s1 = pygame.Surface((32, 32), 0, depth)

            self.assertEqual(s1.get_bytesize(), depth_bytes)
            self.assertEqual(s1.get_bitsize(), depth)

            with self.assertRaises(pygame.error):
                surface = pygame.display.set_mode()
                pygame.display.quit()
                surface.get_bytesize()
        finally:
            pygame.display.quit()

    ########################################################################

    def test_get_flags(self):
        """Ensure a surface's flags can be retrieved."""
        s1 = pygame.Surface((32, 32), pygame.SRCALPHA, 32)

        self.assertEqual(s1.get_flags(), pygame.SRCALPHA)

    @unittest.skipIf(
        os.environ.get("SDL_VIDEODRIVER") == pygame.NULL_VIDEODRIVER,
        "requires a non-null SDL_VIDEODRIVER",
    )
    def test_get_flags__display_surf(self):
        pygame.display.init()
        try:
            # FULLSCREEN
            screen_surf = pygame.display.set_mode((600, 400), flags=0)
            self.assertFalse(screen_surf.get_flags() & pygame.FULLSCREEN)

            screen_surf = pygame.display.set_mode((600, 400), flags=pygame.FULLSCREEN)
            self.assertTrue(screen_surf.get_flags() & pygame.FULLSCREEN)

            # NOFRAME
            screen_surf = pygame.display.set_mode((600, 400), flags=0)
            self.assertFalse(screen_surf.get_flags() & pygame.NOFRAME)

            screen_surf = pygame.display.set_mode((600, 400), flags=pygame.NOFRAME)
            self.assertTrue(screen_surf.get_flags() & pygame.NOFRAME)

            # RESIZABLE
            screen_surf = pygame.display.set_mode((600, 400), flags=0)
            self.assertFalse(screen_surf.get_flags() & pygame.RESIZABLE)

            screen_surf = pygame.display.set_mode((600, 400), flags=pygame.RESIZABLE)
            self.assertTrue(screen_surf.get_flags() & pygame.RESIZABLE)

            # OPENGL
            screen_surf = pygame.display.set_mode((600, 400), flags=0)
            # it can have an OPENGL flag by default on macOS?
            if not (screen_surf.get_flags() & pygame.OPENGL):
                self.assertFalse(screen_surf.get_flags() & pygame.OPENGL)

            try:
                pygame.display.set_mode((200, 200), pygame.OPENGL, 32)
            except pygame.error:
                pass  # If we can't create OPENGL surface don't try this test
            else:
                self.assertTrue(screen_surf.get_flags() & pygame.OPENGL)
        finally:
            pygame.display.quit()

    ########################################################################

    def test_get_parent(self):
        """Ensure a surface's parent can be retrieved."""
        pygame.display.init()
        try:
            parent = pygame.Surface((16, 16))
            child = parent.subsurface((0, 0, 5, 5))

            self.assertIs(child.get_parent(), parent)

            with self.assertRaises(pygame.error):
                surface = pygame.display.set_mode()
                pygame.display.quit()
                surface.get_parent()
        finally:
            pygame.display.quit()

    ########################################################################

    def test_get_rect(self):
        """Ensure a surface's rect can be retrieved."""
        size = (16, 16)
        surf = pygame.Surface(size)
        rect = surf.get_rect()
        rect_kwargs = surf.get_rect(topleft=(1.0, 1.0), center=(100, 100))

        self.assertEqual(rect.topleft, (0.0, 0.0))
        self.assertEqual(rect.size, size)
        self.assertIsInstance(rect, pygame.Rect)
        self.assertEqual(rect_kwargs.center, (100, 100))
        self.assertNotEqual(rect_kwargs.topleft, (1.0, 1.0))
        self.assertRaises(TypeError, surf.get_rect, 5)
        self.assertRaises(TypeError, surf.get_rect, 5.0)
        self.assertRaises(TypeError, surf.get_rect, "potato")
        self.assertRaises(TypeError, surf.get_rect, center=5)
        self.assertRaises(TypeError, surf.get_rect, center=(5,))
        self.assertRaises(TypeError, surf.get_rect, center="5")
        if (
            not IS_PYPY
        ):  # PyPy doesn't raise an AttributeError, so for PyPy we can just skip it.
            with self.assertRaises(AttributeError):
                surf.get_rect(centre=(100, 100))

    ########################################################################

    def test_get_frect(self):
        """Ensure a surface's frect can be retrieved."""
        size = (16.0, 16.0)
        surf = pygame.Surface(size)
        frect = surf.get_frect()
        frect_kwargs = surf.get_frect(topleft=(1.0, 1.0), center=(100, 100))

        self.assertEqual(frect.topleft, (0.0, 0.0))
        self.assertEqual(frect.size, size)
        self.assertIsInstance(frect, pygame.FRect)
        self.assertEqual(frect_kwargs.center, (100, 100))
        self.assertNotEqual(frect_kwargs.topleft, (1.0, 1.0))
        self.assertRaises(TypeError, surf.get_frect, 5)
        self.assertRaises(TypeError, surf.get_frect, center=5)
        self.assertRaises(TypeError, surf.get_frect, center="5")
        if (
            not IS_PYPY
        ):  # PyPy doesn't raise an AttributeError, so for PyPy we can just skip it.
            with self.assertRaises(AttributeError):
                surf.get_frect(centre=(100, 100))

    ########################################################################

    def test_get_width__size_and_height(self):
        """Ensure a surface's size, width and height can be retrieved."""
        for w in range(0, 255, 32):
            for h in range(0, 127, 15):
                s = pygame.Surface((w, h))
                self.assertEqual(s.get_width(), w)
                self.assertEqual(s.get_height(), h)
                self.assertEqual(s.get_size(), (w, h))

    def test_get_view(self):
        """Ensure a buffer view of the surface's pixels can be retrieved."""
        # Check that BufferProxys are returned when array depth is supported,
        # ValueErrors returned otherwise.
        Error = ValueError
        s = pygame.Surface((5, 7), 0, 8)
        v2 = s.get_view("2")

        self.assertRaises(Error, s.get_view, "0")
        self.assertRaises(Error, s.get_view, "1")
        self.assertIsInstance(v2, BufferProxy)
        self.assertRaises(Error, s.get_view, "3")

        s = pygame.Surface((8, 7), 0, 8)
        length = s.get_bytesize() * s.get_width() * s.get_height()
        v0 = s.get_view("0")
        v1 = s.get_view("1")

        self.assertIsInstance(v0, BufferProxy)
        self.assertEqual(v0.length, length)
        self.assertIsInstance(v1, BufferProxy)
        self.assertEqual(v1.length, length)

        s = pygame.Surface((5, 7), 0, 16)
        v2 = s.get_view("2")

        self.assertRaises(Error, s.get_view, "0")
        self.assertRaises(Error, s.get_view, "1")
        self.assertIsInstance(v2, BufferProxy)
        self.assertRaises(Error, s.get_view, "3")

        s = pygame.Surface((8, 7), 0, 16)
        length = s.get_bytesize() * s.get_width() * s.get_height()
        v0 = s.get_view("0")
        v1 = s.get_view("1")

        self.assertIsInstance(v0, BufferProxy)
        self.assertEqual(v0.length, length)
        self.assertIsInstance(v1, BufferProxy)
        self.assertEqual(v1.length, length)

        s = pygame.Surface((5, 7), pygame.SRCALPHA, 16)
        v2 = s.get_view("2")

        self.assertIsInstance(v2, BufferProxy)
        self.assertRaises(Error, s.get_view, "3")

        s = pygame.Surface((5, 7), 0, 24)
        v2 = s.get_view("2")
        v3 = s.get_view("3")

        self.assertRaises(Error, s.get_view, "0")
        self.assertRaises(Error, s.get_view, "1")
        self.assertIsInstance(v2, BufferProxy)
        self.assertIsInstance(v3, BufferProxy)

        s = pygame.Surface((8, 7), 0, 24)
        length = s.get_bytesize() * s.get_width() * s.get_height()
        v0 = s.get_view("0")
        v1 = s.get_view("1")

        self.assertIsInstance(v0, BufferProxy)
        self.assertEqual(v0.length, length)
        self.assertIsInstance(v1, BufferProxy)
        self.assertEqual(v1.length, length)

        s = pygame.Surface((5, 7), 0, 32)
        length = s.get_bytesize() * s.get_width() * s.get_height()
        v0 = s.get_view("0")
        v1 = s.get_view("1")
        v2 = s.get_view("2")
        v3 = s.get_view("3")

        self.assertIsInstance(v0, BufferProxy)
        self.assertEqual(v0.length, length)
        self.assertIsInstance(v1, BufferProxy)
        self.assertEqual(v1.length, length)
        self.assertIsInstance(v2, BufferProxy)
        self.assertIsInstance(v3, BufferProxy)

        s2 = s.subsurface((0, 0, 4, 7))

        self.assertRaises(Error, s2.get_view, "0")
        self.assertRaises(Error, s2.get_view, "1")

        s2 = None
        s = pygame.Surface((5, 7), pygame.SRCALPHA, 32)

        for kind in ("2", "3", "a", "A", "r", "R", "g", "G", "b", "B"):
            self.assertIsInstance(s.get_view(kind), BufferProxy)

        # Check default argument value: '2'
        s = pygame.Surface((2, 4), 0, 32)
        v = s.get_view()
        if not IS_PYPY:
            ai = ArrayInterface(v)
            self.assertEqual(ai.nd, 2)

        # Check locking.
        s = pygame.Surface((2, 4), 0, 32)

        self.assertFalse(s.get_locked())

        v = s.get_view("2")

        self.assertFalse(s.get_locked())

        c = v.__array_interface__

        self.assertTrue(s.get_locked())

        c = None
        gc.collect()

        self.assertTrue(s.get_locked())

        v = None
        gc.collect()

        self.assertFalse(s.get_locked())

        # Check invalid view kind values.
        s = pygame.Surface((2, 4), pygame.SRCALPHA, 32)
        self.assertRaises(TypeError, s.get_view, "")
        self.assertRaises(TypeError, s.get_view, "9")
        self.assertRaises(TypeError, s.get_view, "RGBA")
        self.assertRaises(TypeError, s.get_view, 2)

        # Both unicode and bytes strings are allowed for kind.
        s = pygame.Surface((2, 4), 0, 32)
        s.get_view("2")
        s.get_view(b"2")

        # Garbage collection
        s = pygame.Surface((2, 4), 0, 32)
        weak_s = weakref.ref(s)
        v = s.get_view("3")
        weak_v = weakref.ref(v)
        gc.collect()
        self.assertTrue(weak_s() is s)
        self.assertTrue(weak_v() is v)
        del v
        gc.collect()
        self.assertTrue(weak_s() is s)
        self.assertTrue(weak_v() is None)
        del s
        gc.collect()
        self.assertTrue(weak_s() is None)

    def test_get_buffer(self):
        # Check that get_buffer works for all pixel sizes and for a subsurface.

        # Check for all pixel sizes
        for bitsize in [8, 16, 24, 32]:
            s = pygame.Surface((5, 7), 0, bitsize)
            length = s.get_pitch() * s.get_height()
            v = s.get_buffer()

            self.assertIsInstance(v, BufferProxy)
            self.assertEqual(v.length, length)
            self.assertEqual(repr(v), f"<BufferProxy({length})>")

        # Check for a subsurface (not contiguous)
        s = pygame.Surface((7, 10), 0, 32)
        s2 = s.subsurface((1, 2, 5, 7))
        length = s2.get_pitch() * s2.get_height()
        v = s2.get_buffer()

        self.assertIsInstance(v, BufferProxy)
        self.assertEqual(v.length, length)

        # Check locking.
        s = pygame.Surface((2, 4), 0, 32)
        v = s.get_buffer()
        self.assertTrue(s.get_locked())
        v = None
        gc.collect()
        self.assertFalse(s.get_locked())

    OLDBUF = hasattr(pygame.bufferproxy, "get_segcount")

    @unittest.skipIf(not OLDBUF, "old buffer not available")
    def test_get_buffer_oldbuf(self):
        from pygame.bufferproxy import get_segcount, get_write_buffer

        s = pygame.Surface((2, 4), pygame.SRCALPHA, 32)
        v = s.get_buffer()
        segcount, buflen = get_segcount(v)
        self.assertEqual(segcount, 1)
        self.assertEqual(buflen, s.get_pitch() * s.get_height())
        seglen, segaddr = get_write_buffer(v, 0)
        self.assertEqual(segaddr, s._pixels_address)
        self.assertEqual(seglen, buflen)

    @unittest.skipIf(not OLDBUF, "old buffer not available")
    def test_get_view_oldbuf(self):
        from pygame.bufferproxy import get_segcount, get_write_buffer

        s = pygame.Surface((2, 4), pygame.SRCALPHA, 32)
        v = s.get_view("1")
        segcount, buflen = get_segcount(v)
        self.assertEqual(segcount, 8)
        self.assertEqual(buflen, s.get_pitch() * s.get_height())
        seglen, segaddr = get_write_buffer(v, 7)
        self.assertEqual(segaddr, s._pixels_address + s.get_bytesize() * 7)
        self.assertEqual(seglen, s.get_bytesize())

    def test_set_colorkey(self):
        # __doc__ (as of 2008-06-25) for pygame.surface.Surface.set_colorkey:

        # Surface.set_colorkey(Color, flags=0): return None
        # Surface.set_colorkey(None): return None
        # Set the transparent colorkey

        s = pygame.Surface((16, 16), pygame.SRCALPHA, 32)

        colorkeys = ((20, 189, 20, 255), (128, 50, 50, 255), (23, 21, 255, 255))

        for colorkey in colorkeys:
            s.set_colorkey(colorkey)

            for t in range(4):
                s.set_colorkey(s.get_colorkey())

            self.assertEqual(s.get_colorkey(), colorkey)

    def test_set_masks(self):
        s = pygame.Surface((32, 32))
        r, g, b, a = s.get_masks()
        self.assertRaises(TypeError, s.set_masks, (b, g, r, a))

    def test_set_shifts(self):
        s = pygame.Surface((32, 32))
        r, g, b, a = s.get_shifts()
        self.assertRaises(TypeError, s.set_shifts, (b, g, r, a))

    def test_blit_keyword_args(self):
        color = (1, 2, 3, 255)
        s1 = pygame.Surface((4, 4), 0, 32)
        s2 = pygame.Surface((2, 2), 0, 32)
        s2.fill((1, 2, 3))
        s1.blit(special_flags=BLEND_ADD, source=s2, dest=(1, 1), area=s2.get_rect())
        self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255))
        self.assertEqual(s1.get_at((1, 1)), color)

    def test_blit_big_rects(self):
        """SDL2 can have more than 16 bits for x, y, width, height."""
        big_surf = pygame.Surface((100, 68000), 0, 32)
        big_surf_color = (255, 0, 0)
        big_surf.fill(big_surf_color)

        background = pygame.Surface((500, 500), 0, 32)
        background_color = (0, 255, 0)
        background.fill(background_color)

        # copy parts of the big_surf using more than 16bit parts.
        background.blit(big_surf, (100, 100), area=(0, 16000, 100, 100))
        background.blit(big_surf, (200, 200), area=(0, 32000, 100, 100))
        background.blit(big_surf, (300, 300), area=(0, 66000, 100, 100))

        # check that all three areas are drawn.
        self.assertEqual(background.get_at((101, 101)), big_surf_color)
        self.assertEqual(background.get_at((201, 201)), big_surf_color)
        self.assertEqual(background.get_at((301, 301)), big_surf_color)

        # areas outside the 3 blitted areas not covered by those blits.
        self.assertEqual(background.get_at((400, 301)), background_color)
        self.assertEqual(background.get_at((400, 201)), background_color)
        self.assertEqual(background.get_at((100, 201)), background_color)
        self.assertEqual(background.get_at((99, 99)), background_color)
        self.assertEqual(background.get_at((450, 450)), background_color)


class TestSurfaceBlit(unittest.TestCase):
    """Tests basic blitting functionality and options."""

    # __doc__ (as of 2008-08-02) for pygame.surface.Surface.blit:

    # Surface.blit(source, dest, area=None, special_flags = 0): return Rect
    # draw one image onto another
    #
    # Draws a source Surface onto this Surface. The draw can be positioned
    # with the dest argument. Dest can be a pair of coordinates
    # representing the upper left corner of the source. A Rect can also be
    # passed as the destination and the topleft corner of the rectangle
    # will be used as the position for the blit. The size of the
    # destination rectangle does not affect the blit.
    #
    # An optional area rectangle can be passed as well. This represents a
    # smaller portion of the source Surface to draw.
    #
    # An optional special flags is for passing in new in 1.8.0: BLEND_ADD,
    # BLEND_SUB, BLEND_MULT, BLEND_MIN, BLEND_MAX new in 1.8.1:
    # BLEND_RGBA_ADD, BLEND_RGBA_SUB, BLEND_RGBA_MULT, BLEND_RGBA_MIN,
    # BLEND_RGBA_MAX BLEND_RGB_ADD, BLEND_RGB_SUB, BLEND_RGB_MULT,
    # BLEND_RGB_MIN, BLEND_RGB_MAX With other special blitting flags
    # perhaps added in the future.
    #
    # The return rectangle is the area of the affected pixels, excluding
    # any pixels outside the destination Surface, or outside the clipping
    # area.
    #
    # Pixel alphas will be ignored when blitting to an 8 bit Surface.
    # special_flags new in pygame 1.8.

    def setUp(self):
        """Resets starting surfaces."""
        self.src_surface = pygame.Surface((256, 256), 32)
        self.src_surface.fill(pygame.Color(255, 255, 255))
        self.dst_surface = pygame.Surface((64, 64), 32)
        self.dst_surface.fill(pygame.Color(0, 0, 0))

    def test_blit_overflow_coord(self):
        """Full coverage w/ overflow, specified with Coordinate"""
        result = self.dst_surface.blit(self.src_surface, (0, 0))
        self.assertIsInstance(result, pygame.Rect)
        self.assertEqual(result.size, (64, 64))
        for k in [(x, x) for x in range(64)]:
            self.assertEqual(self.dst_surface.get_at(k), (255, 255, 255))

    def test_blit_overflow_rect(self):
        """Full coverage w/ overflow, specified with a Rect"""
        result = self.dst_surface.blit(self.src_surface, pygame.Rect(-1, -1, 300, 300))
        self.assertIsInstance(result, pygame.Rect)
        self.assertEqual(result.size, (64, 64))
        for k in [(x, x) for x in range(64)]:
            self.assertEqual(self.dst_surface.get_at(k), (255, 255, 255))

    def test_blit_overflow_nonorigin(self):
        """Test Rectangle Dest, with overflow but with starting rect with top-left at (1,1)"""
        result = self.dst_surface.blit(self.src_surface, dest=pygame.Rect((1, 1, 1, 1)))
        self.assertIsInstance(result, pygame.Rect)
        self.assertEqual(result.size, (63, 63))
        self.assertEqual(self.dst_surface.get_at((0, 0)), (0, 0, 0))
        self.assertEqual(self.dst_surface.get_at((63, 0)), (0, 0, 0))
        self.assertEqual(self.dst_surface.get_at((0, 63)), (0, 0, 0))
        self.assertEqual(self.dst_surface.get_at((1, 1)), (255, 255, 255))
        self.assertEqual(self.dst_surface.get_at((63, 63)), (255, 255, 255))

    def test_blit_area_contraint(self):
        """Testing area constraint"""
        result = self.dst_surface.blit(
            self.src_surface,
            dest=pygame.Rect((1, 1, 1, 1)),
            area=pygame.Rect((2, 2, 2, 2)),
        )
        self.assertIsInstance(result, pygame.Rect)
        self.assertEqual(result.size, (2, 2))
        self.assertEqual(self.dst_surface.get_at((0, 0)), (0, 0, 0))  # Corners
        self.assertEqual(self.dst_surface.get_at((63, 0)), (0, 0, 0))
        self.assertEqual(self.dst_surface.get_at((0, 63)), (0, 0, 0))
        self.assertEqual(self.dst_surface.get_at((63, 63)), (0, 0, 0))
        self.assertEqual(
            self.dst_surface.get_at((1, 1)), (255, 255, 255)
        )  # Blitted Area
        self.assertEqual(self.dst_surface.get_at((2, 2)), (255, 255, 255))
        self.assertEqual(self.dst_surface.get_at((3, 3)), (0, 0, 0))
        # Should stop short of filling in (3,3)

    def test_blit_zero_overlap(self):
        """Testing zero-overlap condition."""
        result = self.dst_surface.blit(
            self.src_surface,
            dest=pygame.Rect((-256, -256, 1, 1)),
            area=pygame.Rect((2, 2, 256, 256)),
        )
        self.assertIsInstance(result, pygame.Rect)
        self.assertEqual(result.size, (0, 0))  # No blitting expected
        for k in [(x, x) for x in range(64)]:
            self.assertEqual(self.dst_surface.get_at(k), (0, 0, 0))  # Diagonal
        self.assertEqual(
            self.dst_surface.get_at((63, 0)), (0, 0, 0)
        )  # Remaining corners
        self.assertEqual(self.dst_surface.get_at((0, 63)), (0, 0, 0))

    def test_blit__SRCALPHA_opaque_source(self):
        src = pygame.Surface((256, 256), SRCALPHA, 32)
        dst = src.copy()

        for i, j in test_utils.rect_area_pts(src.get_rect()):
            dst.set_at((i, j), (i, 0, 0, j))
            src.set_at((i, j), (0, i, 0, 255))

        dst.blit(src, (0, 0))

        for pt in test_utils.rect_area_pts(src.get_rect()):
            self.assertEqual(dst.get_at(pt)[1], src.get_at(pt)[1])

    def test_blit__blit_to_self(self):
        """Test that blit operation works on self, alpha value is
        correct, and that no RGB distortion occurs."""
        test_surface = pygame.Surface((128, 128), SRCALPHA, 32)
        area = test_surface.get_rect()

        for pt, test_color in test_utils.gradient(area.width, area.height):
            test_surface.set_at(pt, test_color)

        reference_surface = test_surface.copy()

        test_surface.blit(test_surface, (0, 0))

        for x in range(area.width):
            for y in range(area.height):
                (r, g, b, a) = reference_color = reference_surface.get_at((x, y))
                expected_color = (r, g, b, (a + (a * ((256 - a) // 256))))
                self.assertEqual(reference_color, expected_color)

        self.assertEqual(reference_surface.get_rect(), test_surface.get_rect())

    def test_blit__SRCALPHA_to_SRCALPHA_non_zero(self):
        """Tests blitting a nonzero alpha surface to another nonzero alpha surface
        both straight alpha compositing method. Test is fuzzy (+/- 1/256) to account for
        different implementations in SDL1 and SDL2.
        """

        size = (32, 32)

        def check_color_diff(color1, color2):
            """Returns True if two colors are within (1, 1, 1, 1) of each other."""
            for val in color1 - color2:
                if abs(val) > 1:
                    return False
            return True

        def high_a_onto_low(high, low):
            """Tests straight alpha case. Source is low alpha, destination is high alpha"""
            high_alpha_surface = pygame.Surface(size, pygame.SRCALPHA, 32)
            low_alpha_surface = high_alpha_surface.copy()
            high_alpha_color = Color(
                (high, high, low, high)
            )  # Injecting some RGB variance.
            low_alpha_color = Color((high, low, low, low))
            high_alpha_surface.fill(high_alpha_color)
            low_alpha_surface.fill(low_alpha_color)

            high_alpha_surface.blit(low_alpha_surface, (0, 0))

            expected_color = low_alpha_color + Color(
                tuple(
                    ((x * (255 - low_alpha_color.a)) // 255) for x in high_alpha_color
                )
            )
            self.assertTrue(
                check_color_diff(high_alpha_surface.get_at((0, 0)), expected_color)
            )

        def low_a_onto_high(high, low):
            """Tests straight alpha case. Source is high alpha, destination is low alpha"""
            high_alpha_surface = pygame.Surface(size, pygame.SRCALPHA, 32)
            low_alpha_surface = high_alpha_surface.copy()
            high_alpha_color = Color(
                (high, high, low, high)
            )  # Injecting some RGB variance.
            low_alpha_color = Color((high, low, low, low))
            high_alpha_surface.fill(high_alpha_color)
            low_alpha_surface.fill(low_alpha_color)

            low_alpha_surface.blit(high_alpha_surface, (0, 0))

            expected_color = high_alpha_color + Color(
                tuple(
                    ((x * (255 - high_alpha_color.a)) // 255) for x in low_alpha_color
                )
            )
            self.assertTrue(
                check_color_diff(low_alpha_surface.get_at((0, 0)), expected_color)
            )

        for low_a in range(0, 128):
            for high_a in range(128, 256):
                high_a_onto_low(high_a, low_a)
                low_a_onto_high(high_a, low_a)

    def test_blit__SRCALPHA32_to_8(self):
        # Bug: fatal
        # SDL_DisplayConvert segfaults when video is uninitialized.
        target = pygame.Surface((11, 8), 0, 8)
        test_color = target.get_palette_at(2)
        source = pygame.Surface((1, 1), pygame.SRCALPHA, 32)
        source.set_at((0, 0), test_color)
        target.blit(source, (0, 0))

    def test_blit__SCALPHA32_TO_OPAQUE32(self):
        # Apparently these types of blits need to write 0 to the destination's
        # alpha channel (it's not really an alpha channel but it's treated like one)
        # See https://github.com/pygame-community/pygame-ce/pull/2067

        alphas = [0, 10, 50, 122, 240, 255]

        combinations = list(itertools.combinations_with_replacement(alphas, 2))
        width = len(combinations)

        # masks explicitly specified so direct pixel access of bytes below is
        # guaranteed to be stable
        surf1 = pygame.Surface((width, 1), depth=32, masks=(0xFF0000, 0xFF00, 0xFF, 0))
        surf2 = pygame.Surface(
            (width, 1),
            pygame.SRCALPHA,
            depth=32,
            masks=(0xFF0000, 0xFF00, 0xFF, 0xFF000000),
        )

        for i in range(width):
            alpha1, alpha2 = combinations[i]
            surf1.set_at((i, 0), (0, 0, 0, alpha1))
            surf2.set_at((i, 0), (0, 0, 0, alpha2))

        surf1.blit(surf2, (0, 0))

        # Why use get_buffer?
        # get_at for RGBX surfaces seems to always think A=255, regardless
        # of bytes in surface, which makes sense.
        surf1_bytes = surf1.get_buffer().raw
        for i in range(width):
            # +3 gets the "alpha channel" in this pixel format
            assert surf1_bytes[i * 4 + 3] == 0


class GeneralSurfaceTests(unittest.TestCase):
    @unittest.skipIf(
        os.environ.get("SDL_VIDEODRIVER") == pygame.NULL_VIDEODRIVER,
        "requires a non-null SDL_VIDEODRIVER",
    )
    def test_image_convert_bug_131(self):
        # bug #146: Unable to Surface.convert(32) some 1-bit images.
        # https://github.com/pygame-community/pygame-ce/issues/146

        pygame.display.init()
        try:
            pygame.display.set_mode((640, 480))

            im = pygame.image.load(example_path(os.path.join("data", "city.png")))
            im2 = pygame.image.load(example_path(os.path.join("data", "brick.png")))

            self.assertEqual(im.get_palette(), ((0, 0, 0, 255), (255, 255, 255, 255)))
            self.assertEqual(im2.get_palette(), ((0, 0, 0, 255), (0, 0, 0, 255)))

            self.assertEqual(
                repr(im.convert(32)), "<Surface(24x24x32, colorkey=(0, 0, 0, 255))>"
            )
            self.assertEqual(
                repr(im2.convert(32)), "<Surface(469x137x32, colorkey=(0, 0, 0, 255))>"
            )

            # Ensure a palette format to palette format works.
            im3 = im.convert(8)
            self.assertEqual(repr(im3), "<Surface(24x24x8, colorkey=(0, 0, 0, 255))>")
            self.assertEqual(im3.get_palette(), im.get_palette())

        finally:
            pygame.display.quit()

    def test_convert_init(self):
        """Ensure initialization exceptions are raised
        for surf.convert()."""
        pygame.display.quit()
        surf = pygame.Surface((1, 1))
        filename = example_path(os.path.join("data", "alien3.png"))  # 8bit PNG
        surf8bit = pygame.image.load(filename)

        self.assertRaisesRegex(pygame.error, "display initialized", surf.convert)

        pygame.display.init()
        try:
            if os.environ.get("SDL_VIDEODRIVER") != pygame.NULL_VIDEODRIVER:
                try:
                    surf.convert(32)
                    surf.convert(pygame.Surface((1, 1)))
                except pygame.error:
                    self.fail("convert() should not raise an exception here.")

            self.assertRaisesRegex(pygame.error, "No convert format", surf.convert)
            self.assertRaisesRegex(pygame.error, "No convert format", surf8bit.convert)

            pygame.display.set_mode((640, 480))
            try:
                surf.convert()
            except pygame.error:
                self.fail("convert() should not raise an exception here.")
        finally:
            pygame.display.quit()

    def test_convert_alpha_init(self):
        """Ensure initialization exceptions are raised
        for surf.convert_alpha()."""
        pygame.display.quit()
        surf = pygame.Surface((1, 1))

        self.assertRaisesRegex(pygame.error, "display initialized", surf.convert_alpha)

        pygame.display.init()
        try:
            self.assertRaisesRegex(
                pygame.error, "No convert format", surf.convert_alpha
            )

            pygame.display.set_mode((640, 480))
            try:
                surf.convert_alpha()
            except pygame.error:
                self.fail("convert_alpha() should not raise an exception here.")
        finally:
            pygame.display.quit()

    def test_convert_alpha_SRCALPHA(self):
        """Ensure that the surface returned by surf.convert_alpha()
        has alpha blending enabled"""
        pygame.display.init()
        try:
            pygame.display.set_mode((640, 480))

            s1 = pygame.Surface((100, 100), 0, 32)
            # s2=pygame.Surface((100,100), pygame.SRCALPHA, 32)
            s1_alpha = s1.convert_alpha()
            self.assertEqual(s1_alpha.get_flags() & SRCALPHA, SRCALPHA)
            self.assertEqual(s1_alpha.get_alpha(), 255)
        finally:
            pygame.display.quit()

    def test_convert_palettize(self):
        pygame.display.init()
        try:
            pygame.display.set_mode((640, 480))

            surf = pygame.Surface((150, 250))
            surf.fill((255, 50, 0))
            surf = surf.convert(8)
            surf2 = pygame.Surface((150, 250), depth=8)
            surf2.fill((255, 50, 0))

            self.assertEqual(surf.get_at((50, 50)), surf2.get_at((50, 50)))

        finally:
            pygame.display.quit()

    def test_src_alpha_issue_1289(self):
        """blit should be white."""
        surf1 = pygame.Surface((1, 1), pygame.SRCALPHA, 32)
        surf1.fill((255, 255, 255, 100))

        surf2 = pygame.Surface((1, 1), pygame.SRCALPHA, 32)
        self.assertEqual(surf2.get_at((0, 0)), (0, 0, 0, 0))
        surf2.blit(surf1, (0, 0))

        self.assertEqual(surf1.get_at((0, 0)), (255, 255, 255, 100))
        self.assertEqual(surf2.get_at((0, 0)), (255, 255, 255, 100))

    def test_src_alpha_compatible(self):
        """ "What pygame 1.9.x did". Is the alpha blitter as before?"""

        # The table below was generated with the SDL1 blit.
        # def print_table():
        #     nums = [0, 1, 65, 126, 127, 199, 254, 255]
        #     results = {}
        #     for dest_r, dest_b, dest_a in zip(nums, reversed(nums), reversed(nums)):
        #         for src_r, src_b, src_a in zip(nums, reversed(nums), nums):
        #             src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32)
        #             src_surf.fill((src_r, 255, src_b, src_a))
        #             dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32)
        #             dest_surf.fill((dest_r, 255, dest_b, dest_a))
        #             dest_surf.blit(src_surf, (0, 0))
        #             key = ((dest_r, dest_b, dest_a), (src_r, src_b, src_a))
        #             results[key] = dest_surf.get_at((65, 33))
        #     print("(dest_r, dest_b, dest_a), (src_r, src_b, src_a): color")
        #     pprint(results)

        results_expected = {
            ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255),
            ((0, 255, 255), (1, 254, 1)): (0, 255, 255, 255),
            ((0, 255, 255), (65, 199, 65)): (16, 255, 241, 255),
            ((0, 255, 255), (126, 127, 126)): (62, 255, 192, 255),
            ((0, 255, 255), (127, 126, 127)): (63, 255, 191, 255),
            ((0, 255, 255), (199, 65, 199)): (155, 255, 107, 255),
            ((0, 255, 255), (254, 1, 254)): (253, 255, 2, 255),
            ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255),
            ((1, 254, 254), (0, 255, 0)): (1, 255, 254, 254),
            ((1, 254, 254), (1, 254, 1)): (1, 255, 254, 255),
            ((1, 254, 254), (65, 199, 65)): (17, 255, 240, 255),
            ((1, 254, 254), (126, 127, 126)): (63, 255, 191, 255),
            ((1, 254, 254), (127, 126, 127)): (64, 255, 190, 255),
            ((1, 254, 254), (199, 65, 199)): (155, 255, 107, 255),
            ((1, 254, 254), (254, 1, 254)): (253, 255, 2, 255),
            ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255),
            ((65, 199, 199), (0, 255, 0)): (65, 255, 199, 199),
            ((65, 199, 199), (1, 254, 1)): (64, 255, 200, 200),
            ((65, 199, 199), (65, 199, 65)): (65, 255, 199, 214),
            ((65, 199, 199), (126, 127, 126)): (95, 255, 164, 227),
            ((65, 199, 199), (127, 126, 127)): (96, 255, 163, 227),
            ((65, 199, 199), (199, 65, 199)): (169, 255, 95, 243),
            ((65, 199, 199), (254, 1, 254)): (253, 255, 2, 255),
            ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255),
            ((126, 127, 127), (0, 255, 0)): (126, 255, 127, 127),
            ((126, 127, 127), (1, 254, 1)): (125, 255, 128, 128),
            ((126, 127, 127), (65, 199, 65)): (110, 255, 146, 160),
            ((126, 127, 127), (126, 127, 126)): (126, 255, 127, 191),
            ((126, 127, 127), (127, 126, 127)): (126, 255, 126, 191),
            ((126, 127, 127), (199, 65, 199)): (183, 255, 79, 227),
            ((126, 127, 127), (254, 1, 254)): (253, 255, 1, 255),
            ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255),
            ((127, 126, 126), (0, 255, 0)): (127, 255, 126, 126),
            ((127, 126, 126), (1, 254, 1)): (126, 255, 127, 127),
            ((127, 126, 126), (65, 199, 65)): (111, 255, 145, 159),
            ((127, 126, 126), (126, 127, 126)): (127, 255, 126, 190),
            ((127, 126, 126), (127, 126, 127)): (127, 255, 126, 191),
            ((127, 126, 126), (199, 65, 199)): (183, 255, 78, 227),
            ((127, 126, 126), (254, 1, 254)): (254, 255, 1, 255),
            ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255),
            ((199, 65, 65), (0, 255, 0)): (199, 255, 65, 65),
            ((199, 65, 65), (1, 254, 1)): (198, 255, 66, 66),
            ((199, 65, 65), (65, 199, 65)): (165, 255, 99, 114),
            ((199, 65, 65), (126, 127, 126)): (163, 255, 96, 159),
            ((199, 65, 65), (127, 126, 127)): (163, 255, 95, 160),
            ((199, 65, 65), (199, 65, 199)): (199, 255, 65, 214),
            ((199, 65, 65), (254, 1, 254)): (254, 255, 1, 255),
            ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255),
            ((254, 1, 1), (0, 255, 0)): (254, 255, 1, 1),
            ((254, 1, 1), (1, 254, 1)): (253, 255, 2, 2),
            ((254, 1, 1), (65, 199, 65)): (206, 255, 52, 66),
            ((254, 1, 1), (126, 127, 126)): (191, 255, 63, 127),
            ((254, 1, 1), (127, 126, 127)): (191, 255, 63, 128),
            ((254, 1, 1), (199, 65, 199)): (212, 255, 51, 200),
            ((254, 1, 1), (254, 1, 254)): (254, 255, 1, 255),
            ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255),
            ((255, 0, 0), (0, 255, 0)): (0, 255, 255, 0),
            ((255, 0, 0), (1, 254, 1)): (1, 255, 254, 1),
            ((255, 0, 0), (65, 199, 65)): (65, 255, 199, 65),
            ((255, 0, 0), (126, 127, 126)): (126, 255, 127, 126),
            ((255, 0, 0), (127, 126, 127)): (127, 255, 126, 127),
            ((255, 0, 0), (199, 65, 199)): (199, 255, 65, 199),
            ((255, 0, 0), (254, 1, 254)): (254, 255, 1, 254),
            ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255),
        }

        # chosen because they contain edge cases.
        nums = [0, 1, 65, 126, 127, 199, 254, 255]
        results = {}
        for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)):
            for src_r, src_b, src_a in zip(nums, reversed(nums), nums):
                with self.subTest(
                    src_r=src_r,
                    src_b=src_b,
                    src_a=src_a,
                    dest_r=dst_r,
                    dest_b=dst_b,
                    dest_a=dst_a,
                ):
                    src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32)
                    src_surf.fill((src_r, 255, src_b, src_a))
                    dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32)
                    dest_surf.fill((dst_r, 255, dst_b, dst_a))

                    dest_surf.blit(src_surf, (0, 0))
                    key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a))
                    results[key] = dest_surf.get_at((65, 33))
                    self.assertEqual(results[key], results_expected[key])

        self.assertEqual(results, results_expected)

    def test_src_alpha_compatible_16bit(self):
        """ "What pygame 1.9.x did". Is the alpha blitter as before?"""

        # The table below was generated with the SDL1 blit.
        # def print_table():
        #     nums = [0, 1, 65, 126, 127, 199, 254, 255]
        #     results = {}
        #     for dest_r, dest_b, dest_a in zip(nums, reversed(nums), reversed(nums)):
        #         for src_r, src_b, src_a in zip(nums, reversed(nums), nums):
        #             src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16)
        #             src_surf.fill((src_r, 255, src_b, src_a))
        #             dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16)
        #             dest_surf.fill((dest_r, 255, dest_b, dest_a))
        #             dest_surf.blit(src_surf, (0, 0))
        #             key = ((dest_r, dest_b, dest_a), (src_r, src_b, src_a))
        #             results[key] = dest_surf.get_at((65, 33))
        #     print("(dest_r, dest_b, dest_a), (src_r, src_b, src_a): color")
        #     pprint(results)

        results_expected = {
            ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255),
            ((0, 255, 255), (1, 254, 1)): (0, 255, 255, 255),
            ((0, 255, 255), (65, 199, 65)): (17, 255, 255, 255),
            ((0, 255, 255), (126, 127, 126)): (51, 255, 204, 255),
            ((0, 255, 255), (127, 126, 127)): (51, 255, 204, 255),
            ((0, 255, 255), (199, 65, 199)): (170, 255, 102, 255),
            ((0, 255, 255), (254, 1, 254)): (255, 255, 0, 255),
            ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255),
            ((1, 254, 254), (0, 255, 0)): (0, 255, 255, 255),
            ((1, 254, 254), (1, 254, 1)): (0, 255, 255, 255),
            ((1, 254, 254), (65, 199, 65)): (17, 255, 255, 255),
            ((1, 254, 254), (126, 127, 126)): (51, 255, 204, 255),
            ((1, 254, 254), (127, 126, 127)): (51, 255, 204, 255),
            ((1, 254, 254), (199, 65, 199)): (170, 255, 102, 255),
            ((1, 254, 254), (254, 1, 254)): (255, 255, 0, 255),
            ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255),
            ((65, 199, 199), (0, 255, 0)): (68, 255, 204, 204),
            ((65, 199, 199), (1, 254, 1)): (68, 255, 204, 204),
            ((65, 199, 199), (65, 199, 65)): (68, 255, 204, 221),
            ((65, 199, 199), (126, 127, 126)): (85, 255, 170, 238),
            ((65, 199, 199), (127, 126, 127)): (85, 255, 170, 238),
            ((65, 199, 199), (199, 65, 199)): (187, 255, 85, 255),
            ((65, 199, 199), (254, 1, 254)): (255, 255, 0, 255),
            ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255),
            ((126, 127, 127), (0, 255, 0)): (119, 255, 119, 119),
            ((126, 127, 127), (1, 254, 1)): (119, 255, 119, 119),
            ((126, 127, 127), (65, 199, 65)): (102, 255, 136, 153),
            ((126, 127, 127), (126, 127, 126)): (119, 255, 119, 187),
            ((126, 127, 127), (127, 126, 127)): (119, 255, 119, 187),
            ((126, 127, 127), (199, 65, 199)): (187, 255, 68, 238),
            ((126, 127, 127), (254, 1, 254)): (255, 255, 0, 255),
            ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255),
            ((127, 126, 126), (0, 255, 0)): (119, 255, 119, 119),
            ((127, 126, 126), (1, 254, 1)): (119, 255, 119, 119),
            ((127, 126, 126), (65, 199, 65)): (102, 255, 136, 153),
            ((127, 126, 126), (126, 127, 126)): (119, 255, 119, 187),
            ((127, 126, 126), (127, 126, 127)): (119, 255, 119, 187),
            ((127, 126, 126), (199, 65, 199)): (187, 255, 68, 238),
            ((127, 126, 126), (254, 1, 254)): (255, 255, 0, 255),
            ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255),
            ((199, 65, 65), (0, 255, 0)): (204, 255, 68, 68),
            ((199, 65, 65), (1, 254, 1)): (204, 255, 68, 68),
            ((199, 65, 65), (65, 199, 65)): (170, 255, 102, 119),
            ((199, 65, 65), (126, 127, 126)): (170, 255, 85, 153),
            ((199, 65, 65), (127, 126, 127)): (170, 255, 85, 153),
            ((199, 65, 65), (199, 65, 199)): (204, 255, 68, 221),
            ((199, 65, 65), (254, 1, 254)): (255, 255, 0, 255),
            ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255),
            ((254, 1, 1), (0, 255, 0)): (0, 255, 255, 0),
            ((254, 1, 1), (1, 254, 1)): (0, 255, 255, 0),
            ((254, 1, 1), (65, 199, 65)): (68, 255, 204, 68),
            ((254, 1, 1), (126, 127, 126)): (119, 255, 119, 119),
            ((254, 1, 1), (127, 126, 127)): (119, 255, 119, 119),
            ((254, 1, 1), (199, 65, 199)): (204, 255, 68, 204),
            ((254, 1, 1), (254, 1, 254)): (255, 255, 0, 255),
            ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255),
            ((255, 0, 0), (0, 255, 0)): (0, 255, 255, 0),
            ((255, 0, 0), (1, 254, 1)): (0, 255, 255, 0),
            ((255, 0, 0), (65, 199, 65)): (68, 255, 204, 68),
            ((255, 0, 0), (126, 127, 126)): (119, 255, 119, 119),
            ((255, 0, 0), (127, 126, 127)): (119, 255, 119, 119),
            ((255, 0, 0), (199, 65, 199)): (204, 255, 68, 204),
            ((255, 0, 0), (254, 1, 254)): (255, 255, 0, 255),
            ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255),
        }

        # chosen because they contain edge cases.
        nums = [0, 1, 65, 126, 127, 199, 254, 255]
        results = {}
        for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)):
            for src_r, src_b, src_a in zip(nums, reversed(nums), nums):
                with self.subTest(
                    src_r=src_r,
                    src_b=src_b,
                    src_a=src_a,
                    dest_r=dst_r,
                    dest_b=dst_b,
                    dest_a=dst_a,
                ):
                    src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16)
                    src_surf.fill((src_r, 255, src_b, src_a))
                    dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 16)
                    dest_surf.fill((dst_r, 255, dst_b, dst_a))

                    dest_surf.blit(src_surf, (0, 0))
                    key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a))
                    results[key] = dest_surf.get_at((65, 33))
                    self.assertEqual(results[key], results_expected[key])

        self.assertEqual(results, results_expected)

    def test_sdl1_mimic_blitter_with_set_alpha(self):
        """does the SDL 1 style blitter in pygame 2 work with set_alpha(),
        this feature only exists in pygame 2/SDL2 SDL1 did not support
        combining surface and pixel alpha"""

        results_expected = {
            ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255),
            ((0, 255, 255), (1, 254, 1)): (0, 255, 255, 255),
            ((0, 255, 255), (65, 199, 65)): (16, 255, 241, 255),
            ((0, 255, 255), (126, 127, 126)): (62, 255, 192, 255),
            ((0, 255, 255), (127, 126, 127)): (63, 255, 191, 255),
            ((0, 255, 255), (199, 65, 199)): (155, 255, 107, 255),
            ((0, 255, 255), (254, 1, 254)): (253, 255, 2, 255),
            ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255),
            ((1, 254, 254), (0, 255, 0)): (1, 255, 254, 254),
            ((1, 254, 254), (1, 254, 1)): (1, 255, 254, 255),
            ((1, 254, 254), (65, 199, 65)): (17, 255, 240, 255),
            ((1, 254, 254), (126, 127, 126)): (63, 255, 191, 255),
            ((1, 254, 254), (127, 126, 127)): (64, 255, 190, 255),
            ((1, 254, 254), (199, 65, 199)): (155, 255, 107, 255),
            ((1, 254, 254), (254, 1, 254)): (253, 255, 2, 255),
            ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255),
            ((65, 199, 199), (0, 255, 0)): (65, 255, 199, 199),
            ((65, 199, 199), (1, 254, 1)): (64, 255, 200, 200),
            ((65, 199, 199), (65, 199, 65)): (65, 255, 199, 214),
            ((65, 199, 199), (126, 127, 126)): (95, 255, 164, 227),
            ((65, 199, 199), (127, 126, 127)): (96, 255, 163, 227),
            ((65, 199, 199), (199, 65, 199)): (169, 255, 95, 243),
            ((65, 199, 199), (254, 1, 254)): (253, 255, 2, 255),
            ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255),
            ((126, 127, 127), (0, 255, 0)): (126, 255, 127, 127),
            ((126, 127, 127), (1, 254, 1)): (125, 255, 128, 128),
            ((126, 127, 127), (65, 199, 65)): (110, 255, 146, 160),
            ((126, 127, 127), (126, 127, 126)): (126, 255, 127, 191),
            ((126, 127, 127), (127, 126, 127)): (126, 255, 126, 191),
            ((126, 127, 127), (199, 65, 199)): (183, 255, 79, 227),
            ((126, 127, 127), (254, 1, 254)): (253, 255, 1, 255),
            ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255),
            ((127, 126, 126), (0, 255, 0)): (127, 255, 126, 126),
            ((127, 126, 126), (1, 254, 1)): (126, 255, 127, 127),
            ((127, 126, 126), (65, 199, 65)): (111, 255, 145, 159),
            ((127, 126, 126), (126, 127, 126)): (127, 255, 126, 190),
            ((127, 126, 126), (127, 126, 127)): (127, 255, 126, 191),
            ((127, 126, 126), (199, 65, 199)): (183, 255, 78, 227),
            ((127, 126, 126), (254, 1, 254)): (254, 255, 1, 255),
            ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255),
            ((199, 65, 65), (0, 255, 0)): (199, 255, 65, 65),
            ((199, 65, 65), (1, 254, 1)): (198, 255, 66, 66),
            ((199, 65, 65), (65, 199, 65)): (165, 255, 99, 114),
            ((199, 65, 65), (126, 127, 126)): (163, 255, 96, 159),
            ((199, 65, 65), (127, 126, 127)): (163, 255, 95, 160),
            ((199, 65, 65), (199, 65, 199)): (199, 255, 65, 214),
            ((199, 65, 65), (254, 1, 254)): (254, 255, 1, 255),
            ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255),
            ((254, 1, 1), (0, 255, 0)): (254, 255, 1, 1),
            ((254, 1, 1), (1, 254, 1)): (253, 255, 2, 2),
            ((254, 1, 1), (65, 199, 65)): (206, 255, 52, 66),
            ((254, 1, 1), (126, 127, 126)): (191, 255, 63, 127),
            ((254, 1, 1), (127, 126, 127)): (191, 255, 63, 128),
            ((254, 1, 1), (199, 65, 199)): (212, 255, 51, 200),
            ((254, 1, 1), (254, 1, 254)): (254, 255, 1, 255),
            ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255),
            ((255, 0, 0), (0, 255, 0)): (0, 255, 255, 0),
            ((255, 0, 0), (1, 254, 1)): (1, 255, 254, 1),
            ((255, 0, 0), (65, 199, 65)): (65, 255, 199, 65),
            ((255, 0, 0), (126, 127, 126)): (126, 255, 127, 126),
            ((255, 0, 0), (127, 126, 127)): (127, 255, 126, 127),
            ((255, 0, 0), (199, 65, 199)): (199, 255, 65, 199),
            ((255, 0, 0), (254, 1, 254)): (254, 255, 1, 254),
            ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255),
        }

        # chosen because they contain edge cases.
        nums = [0, 1, 65, 126, 127, 199, 254, 255]
        results = {}
        for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)):
            for src_r, src_b, src_a in zip(nums, reversed(nums), nums):
                with self.subTest(
                    src_r=src_r,
                    src_b=src_b,
                    src_a=src_a,
                    dest_r=dst_r,
                    dest_b=dst_b,
                    dest_a=dst_a,
                ):
                    src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32)
                    src_surf.fill((src_r, 255, src_b, 255))
                    src_surf.set_alpha(src_a)
                    dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32)
                    dest_surf.fill((dst_r, 255, dst_b, dst_a))

                    dest_surf.blit(src_surf, (0, 0))
                    key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a))
                    results[key] = dest_surf.get_at((65, 33))
                    self.assertEqual(results[key], results_expected[key])

        self.assertEqual(results, results_expected)

    @unittest.skipIf(
        "arm" in platform.machine() or "aarch64" in platform.machine(),
        "sdl2 blitter produces different results on arm",
    )
    def test_src_alpha_sdl2_blitter(self):
        """Checking that the BLEND_ALPHA_SDL2 flag works - this feature
        only exists when using SDL2"""

        results_expected = {
            ((0, 255, 255), (0, 255, 0)): (0, 255, 255, 255),
            ((0, 255, 255), (1, 254, 1)): (0, 253, 253, 253),
            ((0, 255, 255), (65, 199, 65)): (16, 253, 239, 253),
            ((0, 255, 255), (126, 127, 126)): (62, 253, 190, 253),
            ((0, 255, 255), (127, 126, 127)): (63, 253, 189, 253),
            ((0, 255, 255), (199, 65, 199)): (154, 253, 105, 253),
            ((0, 255, 255), (254, 1, 254)): (252, 253, 0, 253),
            ((0, 255, 255), (255, 0, 255)): (255, 255, 0, 255),
            ((1, 254, 254), (0, 255, 0)): (1, 255, 254, 254),
            ((1, 254, 254), (1, 254, 1)): (0, 253, 252, 252),
            ((1, 254, 254), (65, 199, 65)): (16, 253, 238, 252),
            ((1, 254, 254), (126, 127, 126)): (62, 253, 189, 252),
            ((1, 254, 254), (127, 126, 127)): (63, 253, 189, 253),
            ((1, 254, 254), (199, 65, 199)): (154, 253, 105, 253),
            ((1, 254, 254), (254, 1, 254)): (252, 253, 0, 253),
            ((1, 254, 254), (255, 0, 255)): (255, 255, 0, 255),
            ((65, 199, 199), (0, 255, 0)): (65, 255, 199, 199),
            ((65, 199, 199), (1, 254, 1)): (64, 253, 197, 197),
            ((65, 199, 199), (65, 199, 65)): (64, 253, 197, 211),
            ((65, 199, 199), (126, 127, 126)): (94, 253, 162, 225),
            ((65, 199, 199), (127, 126, 127)): (95, 253, 161, 225),
            ((65, 199, 199), (199, 65, 199)): (168, 253, 93, 241),
            ((65, 199, 199), (254, 1, 254)): (252, 253, 0, 253),
            ((65, 199, 199), (255, 0, 255)): (255, 255, 0, 255),
            ((126, 127, 127), (0, 255, 0)): (126, 255, 127, 127),
            ((126, 127, 127), (1, 254, 1)): (125, 253, 126, 126),
            ((126, 127, 127), (65, 199, 65)): (109, 253, 144, 158),
            ((126, 127, 127), (126, 127, 126)): (125, 253, 125, 188),
            ((126, 127, 127), (127, 126, 127)): (126, 253, 125, 189),
            ((126, 127, 127), (199, 65, 199)): (181, 253, 77, 225),
            ((126, 127, 127), (254, 1, 254)): (252, 253, 0, 253),
            ((126, 127, 127), (255, 0, 255)): (255, 255, 0, 255),
            ((127, 126, 126), (0, 255, 0)): (127, 255, 126, 126),
            ((127, 126, 126), (1, 254, 1)): (126, 253, 125, 125),
            ((127, 126, 126), (65, 199, 65)): (110, 253, 143, 157),
            ((127, 126, 126), (126, 127, 126)): (125, 253, 125, 188),
            ((127, 126, 126), (127, 126, 127)): (126, 253, 125, 189),
            ((127, 126, 126), (199, 65, 199)): (181, 253, 77, 225),
            ((127, 126, 126), (254, 1, 254)): (252, 253, 0, 253),
            ((127, 126, 126), (255, 0, 255)): (255, 255, 0, 255),
            ((199, 65, 65), (0, 255, 0)): (199, 255, 65, 65),
            ((199, 65, 65), (1, 254, 1)): (197, 253, 64, 64),
            ((199, 65, 65), (65, 199, 65)): (163, 253, 98, 112),
            ((199, 65, 65), (126, 127, 126)): (162, 253, 94, 157),
            ((199, 65, 65), (127, 126, 127)): (162, 253, 94, 158),
            ((199, 65, 65), (199, 65, 199)): (197, 253, 64, 212),
            ((199, 65, 65), (254, 1, 254)): (252, 253, 0, 253),
            ((199, 65, 65), (255, 0, 255)): (255, 255, 0, 255),
            ((254, 1, 1), (0, 255, 0)): (254, 255, 1, 1),
            ((254, 1, 1), (1, 254, 1)): (252, 253, 0, 0),
            ((254, 1, 1), (65, 199, 65)): (204, 253, 50, 64),
            ((254, 1, 1), (126, 127, 126)): (189, 253, 62, 125),
            ((254, 1, 1), (127, 126, 127)): (190, 253, 62, 126),
            ((254, 1, 1), (199, 65, 199)): (209, 253, 50, 198),
            ((254, 1, 1), (254, 1, 254)): (252, 253, 0, 253),
            ((254, 1, 1), (255, 0, 255)): (255, 255, 0, 255),
            ((255, 0, 0), (0, 255, 0)): (255, 255, 0, 0),
            ((255, 0, 0), (1, 254, 1)): (253, 253, 0, 0),
            ((255, 0, 0), (65, 199, 65)): (205, 253, 50, 64),
            ((255, 0, 0), (126, 127, 126)): (190, 253, 62, 125),
            ((255, 0, 0), (127, 126, 127)): (190, 253, 62, 126),
            ((255, 0, 0), (199, 65, 199)): (209, 253, 50, 198),
            ((255, 0, 0), (254, 1, 254)): (252, 253, 0, 253),
            ((255, 0, 0), (255, 0, 255)): (255, 255, 0, 255),
        }

        # chosen because they contain edge cases.
        nums = [0, 1, 65, 126, 127, 199, 254, 255]
        results = {}
        for dst_r, dst_b, dst_a in zip(nums, reversed(nums), reversed(nums)):
            for src_r, src_b, src_a in zip(nums, reversed(nums), nums):
                with self.subTest(
                    src_r=src_r,
                    src_b=src_b,
                    src_a=src_a,
                    dest_r=dst_r,
                    dest_b=dst_b,
                    dest_a=dst_a,
                ):
                    src_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32)
                    src_surf.fill((src_r, 255, src_b, src_a))
                    dest_surf = pygame.Surface((66, 66), pygame.SRCALPHA, 32)
                    dest_surf.fill((dst_r, 255, dst_b, dst_a))

                    dest_surf.blit(
                        src_surf, (0, 0), special_flags=pygame.BLEND_ALPHA_SDL2
                    )
                    key = ((dst_r, dst_b, dst_a), (src_r, src_b, src_a))
                    results[key] = tuple(dest_surf.get_at((65, 33)))
                    for i in range(4):
                        self.assertAlmostEqual(
                            results[key][i], results_expected[key][i], delta=4
                        )

        # print("(dest_r, dest_b, dest_a), (src_r, src_b, src_a): color")
        # pprint(results)

    def test_opaque_destination_blit_with_set_alpha(self):
        # no set_alpha()
        src_surf = pygame.Surface((32, 32), pygame.SRCALPHA, 32)
        src_surf.fill((255, 255, 255, 200))
        dest_surf = pygame.Surface((32, 32))
        dest_surf.fill((100, 100, 100))

        dest_surf.blit(src_surf, (0, 0))

        no_surf_alpha_col = dest_surf.get_at((0, 0))

        dest_surf.fill((100, 100, 100))
        dest_surf.set_alpha(200)
        dest_surf.blit(src_surf, (0, 0))

        surf_alpha_col = dest_surf.get_at((0, 0))

        self.assertEqual(no_surf_alpha_col, surf_alpha_col)

    def todo_test_convert(self):
        # __doc__ (as of 2008-08-02) for pygame.surface.Surface.convert:

        # Surface.convert(Surface): return Surface
        # Surface.convert(depth, flags=0): return Surface
        # Surface.convert(masks, flags=0): return Surface
        # Surface.convert(): return Surface
        # change the pixel format of an image
        #
        # Creates a new copy of the Surface with the pixel format changed. The
        # new pixel format can be determined from another existing Surface.
        # Otherwise, depth, flags, and masks arguments can be used, similar to
        # the pygame.Surface() call.
        #
        # If no arguments are passed the new Surface will have the same pixel
        # format as the display Surface. This is always the fastest format for
        # blitting. It is a good idea to convert all Surfaces before they are
        # blitted many times.
        #
        # The converted Surface will have no pixel alphas. They will be
        # stripped if the original had them. See Surface.convert_alpha() for
        # preserving or creating per-pixel alphas.
        #

        self.fail()

    def test_convert__pixel_format_as_surface_subclass(self):
        """Ensure convert accepts a Surface subclass argument."""
        expected_size = (23, 17)
        convert_surface = SurfaceSubclass(expected_size, 0, 32)
        depth_surface = SurfaceSubclass((31, 61), 0, 32)

        pygame.display.init()
        try:
            surface = convert_surface.convert(depth_surface)

            self.assertIsNot(surface, depth_surface)
            self.assertIsNot(surface, convert_surface)
            self.assertIsInstance(surface, pygame.Surface)
            self.assertIsInstance(surface, SurfaceSubclass)
            self.assertEqual(surface.get_size(), expected_size)
        finally:
            pygame.display.quit()

    def test_convert_alpha_argument_deprecation(self):
        pygame.display.init()
        pygame.display.set_mode((640, 480))

        surf = pygame.Surface((64, 64))
        surf_2 = pygame.Surface((64, 64))

        with self.assertWarns(DeprecationWarning):
            surf_2.convert_alpha(surf)

    def test_convert_alpha(self):
        """Ensure the surface returned by surf.convert_alpha
        has alpha values added"""
        pygame.display.init()
        try:
            pygame.display.set_mode((640, 480))

            s2 = pygame.Surface((100, 100), 0, 32)
            s2_alpha = s2.convert_alpha()

            s3 = pygame.Surface((100, 100), 0, 8)
            s3_alpha = s3.convert_alpha()

            s4 = pygame.Surface((100, 100), 0, 12)
            s4_alpha = s4.convert_alpha()

            s5 = pygame.Surface((100, 100), 0, 15)
            s5_alpha = s5.convert_alpha()

            s6 = pygame.Surface((100, 100), 0, 16)
            s6_alpha = s6.convert_alpha()

            s7 = pygame.Surface((100, 100), 0, 24)
            s7_alpha = s7.convert_alpha()

            self.assertEqual(s2_alpha.get_alpha(), 255)
            self.assertEqual(s3_alpha.get_alpha(), 255)
            self.assertEqual(s4_alpha.get_alpha(), 255)
            self.assertEqual(s5_alpha.get_alpha(), 255)
            self.assertEqual(s6_alpha.get_alpha(), 255)
            self.assertEqual(s7_alpha.get_alpha(), 255)

            self.assertEqual(s2_alpha.get_bitsize(), 32)
            self.assertEqual(s3_alpha.get_bitsize(), 32)
            self.assertEqual(s4_alpha.get_bitsize(), 32)
            self.assertEqual(s5_alpha.get_bitsize(), 32)
            self.assertEqual(s6_alpha.get_bitsize(), 32)
            self.assertEqual(s6_alpha.get_bitsize(), 32)

            with self.assertRaises(pygame.error):
                surface = pygame.display.set_mode()
                pygame.display.quit()
                surface.convert_alpha()

        finally:
            pygame.display.quit()

    def test_convert_alpha__pixel_format_as_surface_subclass(self):
        """Ensure convert_alpha accepts a Surface subclass argument."""
        expected_size = (23, 17)
        convert_surface = SurfaceSubclass(expected_size, SRCALPHA, 32)

        pygame.display.init()
        try:
            pygame.display.set_mode((60, 60))

            surface = convert_surface.convert_alpha()

            self.assertIsNot(surface, convert_surface)
            self.assertIsInstance(surface, pygame.Surface)
            self.assertIsInstance(surface, SurfaceSubclass)
            self.assertEqual(surface.get_size(), expected_size)
        finally:
            pygame.display.quit()

    def test_get_abs_offset(self):
        pygame.display.init()
        try:
            parent = pygame.Surface((64, 64), SRCALPHA, 32)

            # Stack a bunch of subsurfaces
            sub_level_1 = parent.subsurface((2, 2), (34, 37))
            sub_level_2 = sub_level_1.subsurface((0, 0), (30, 29))
            sub_level_3 = sub_level_2.subsurface((3, 7), (20, 21))
            sub_level_4 = sub_level_3.subsurface((6, 1), (14, 14))
            sub_level_5 = sub_level_4.subsurface((5, 6), (3, 4))

            # Parent is always (0, 0)
            self.assertEqual(parent.get_abs_offset(), (0, 0))
            # Total offset: (0+2, 0+2) = (2, 2)
            self.assertEqual(sub_level_1.get_abs_offset(), (2, 2))
            # Total offset: (0+2+0, 0+2+0) = (2, 2)
            self.assertEqual(sub_level_2.get_abs_offset(), (2, 2))
            # Total offset: (0+2+0+3, 0+2+0+7) = (5, 9)
            self.assertEqual(sub_level_3.get_abs_offset(), (5, 9))
            # Total offset: (0+2+0+3+6, 0+2+0+7+1) = (11, 10)
            self.assertEqual(sub_level_4.get_abs_offset(), (11, 10))
            # Total offset: (0+2+0+3+6+5, 0+2+0+7+1+6) = (16, 16)
            self.assertEqual(sub_level_5.get_abs_offset(), (16, 16))

            with self.assertRaises(pygame.error):
                surface = pygame.display.set_mode()
                pygame.display.quit()
                surface.get_abs_offset()
        finally:
            pygame.display.quit()

    def test_get_abs_parent(self):
        pygame.display.init()
        try:
            parent = pygame.Surface((32, 32), SRCALPHA, 32)

            # Stack a bunch of subsurfaces
            sub_level_1 = parent.subsurface((1, 1), (15, 15))
            sub_level_2 = sub_level_1.subsurface((1, 1), (12, 12))
            sub_level_3 = sub_level_2.subsurface((1, 1), (9, 9))
            sub_level_4 = sub_level_3.subsurface((1, 1), (8, 8))
            sub_level_5 = sub_level_4.subsurface((2, 2), (3, 4))
            sub_level_6 = sub_level_5.subsurface((0, 0), (2, 1))

            # Can't have subsurfaces bigger than parents
            self.assertRaises(ValueError, parent.subsurface, (5, 5), (100, 100))
            self.assertRaises(ValueError, sub_level_3.subsurface, (0, 0), (11, 5))
            self.assertRaises(ValueError, sub_level_6.subsurface, (0, 0), (5, 5))

            # Calling get_abs_parent on parent should return itself
            self.assertEqual(parent.get_abs_parent(), parent)

            # On subclass "depth" of 1, get_abs_parent and get_parent should return the same
            self.assertEqual(sub_level_1.get_abs_parent(), sub_level_1.get_parent())
            self.assertEqual(sub_level_2.get_abs_parent(), parent)
            self.assertEqual(sub_level_3.get_abs_parent(), parent)
            self.assertEqual(sub_level_4.get_abs_parent(), parent)
            self.assertEqual(sub_level_5.get_abs_parent(), parent)
            self.assertEqual(
                sub_level_6.get_abs_parent(), sub_level_6.get_parent().get_abs_parent()
            )

            with self.assertRaises(pygame.error):
                surface = pygame.display.set_mode()
                pygame.display.quit()
                surface.get_abs_parent()
        finally:
            pygame.display.quit()

    def test_get_at(self):
        surf = pygame.Surface((2, 2), 0, 24)
        c00 = pygame.Color(1, 2, 3)
        c01 = pygame.Color(5, 10, 15)
        c10 = pygame.Color(100, 50, 0)
        c11 = pygame.Color(4, 5, 6)
        surf.set_at((0, 0), c00)
        surf.set_at((0, 1), c01)
        surf.set_at((1, 0), c10)
        surf.set_at((1, 1), c11)
        c = surf.get_at((0, 0))
        self.assertIsInstance(c, pygame.Color)
        c = surf.get_at(pygame.Vector2(0, 0))
        self.assertIsInstance(c, pygame.Color)
        self.assertEqual(c, c00)
        self.assertEqual(surf.get_at((0, 1)), c01)
        self.assertEqual(surf.get_at((1, 0)), c10)
        self.assertEqual(surf.get_at((1, 1)), c11)
        self.assertEqual(surf.get_at(pygame.Vector2(0, 1)), c01)
        self.assertEqual(surf.get_at(pygame.Vector2(1, 0)), c10)
        self.assertEqual(surf.get_at(pygame.Vector2(1, 1)), c11)
        for p in [(-1, 0), (0, -1), (2, 0), (0, 2)]:
            self.assertRaises(IndexError, surf.get_at, p)

    def test_get_at_mapped(self):
        color = pygame.Color(10, 20, 30)
        for bitsize in [8, 16, 24, 32]:
            surf = pygame.Surface((2, 2), 0, bitsize)
            surf.fill(color)
            pixel = surf.get_at_mapped((0, 0))
            self.assertEqual(
                pixel,
                surf.map_rgb(color),
                "%i != %i, bitsize: %i" % (pixel, surf.map_rgb(color), bitsize),
            )
            pixel = surf.get_at_mapped(pygame.Vector2(0, 0))
            self.assertEqual(
                pixel,
                surf.map_rgb(color),
                "%i != %i, bitsize: %i" % (pixel, surf.map_rgb(color), bitsize),
            )

    def test_get_bitsize(self):
        pygame.display.init()
        try:
            expected_size = (11, 21)

            # Check that get_bitsize returns passed depth
            expected_depth = 32
            surface = pygame.Surface(expected_size, pygame.SRCALPHA, expected_depth)
            self.assertEqual(surface.get_size(), expected_size)
            self.assertEqual(surface.get_bitsize(), expected_depth)

            expected_depth = 16
            surface = pygame.Surface(expected_size, pygame.SRCALPHA, expected_depth)
            self.assertEqual(surface.get_size(), expected_size)
            self.assertEqual(surface.get_bitsize(), expected_depth)

            expected_depth = 15
            surface = pygame.Surface(expected_size, 0, expected_depth)
            self.assertEqual(surface.get_size(), expected_size)
            self.assertEqual(surface.get_bitsize(), expected_depth)
            # Check for invalid depths
            expected_depth = -1
            self.assertRaises(
                ValueError, pygame.Surface, expected_size, 0, expected_depth
            )
            expected_depth = 11
            self.assertRaises(
                ValueError, pygame.Surface, expected_size, 0, expected_depth
            )
            expected_depth = 1024
            self.assertRaises(
                ValueError, pygame.Surface, expected_size, 0, expected_depth
            )

            with self.assertRaises(pygame.error):
                surface = pygame.display.set_mode()
                pygame.display.quit()
                surface.get_bitsize()
        finally:
            pygame.display.quit()

    def test_get_clip(self):
        s = pygame.Surface((800, 600))
        rectangle = s.get_clip()
        self.assertEqual(rectangle, (0, 0, 800, 600))

    def test_get_colorkey(self):
        pygame.display.init()
        try:
            # if set_colorkey is not used
            s = pygame.Surface((800, 600), 0, 32)
            self.assertIsNone(s.get_colorkey())

            # if set_colorkey is used
            s.set_colorkey(None)
            self.assertIsNone(s.get_colorkey())

            # setting up remainder of tests...
            r, g, b, a = 20, 40, 60, 12
            colorkey = pygame.Color(r, g, b)
            s.set_colorkey(colorkey)

            # test for ideal case
            self.assertEqual(s.get_colorkey(), (r, g, b, 255))

            # test for if the color_key is set using pygame.RLEACCEL
            s.set_colorkey(colorkey, pygame.RLEACCEL)
            self.assertEqual(s.get_colorkey(), (r, g, b, 255))

            # test for if the color key is not what's expected
            s.set_colorkey(pygame.Color(r + 1, g + 1, b + 1))
            self.assertNotEqual(s.get_colorkey(), (r, g, b, 255))

            s.set_colorkey(pygame.Color(r, g, b, a))
            # regardless of whether alpha is not 255
            # colorkey returned from surface is always 255
            self.assertEqual(s.get_colorkey(), (r, g, b, 255))
        finally:
            # test for using method after display.quit() is called...
            s = pygame.display.set_mode((200, 200), 0, 32)
            pygame.display.quit()
            with self.assertRaises(pygame.error):
                s.get_colorkey()

    def test_get_height(self):
        sizes = ((1, 1), (119, 10), (10, 119), (1, 1000), (1000, 1), (1000, 1000))
        for width, height in sizes:
            surf = pygame.Surface((width, height))
            found_height = surf.get_height()
            self.assertEqual(height, found_height)

    def test_get_locked(self):
        def blit_locked_test(surface):
            newSurf = pygame.Surface((10, 10))
            try:
                newSurf.blit(surface, (0, 0))
            except pygame.error:
                return True
            else:
                return False

        surf = pygame.Surface((100, 100))

        self.assertIs(surf.get_locked(), blit_locked_test(surf))  # Unlocked
        # Surface should lock
        surf.lock()
        self.assertIs(surf.get_locked(), blit_locked_test(surf))  # Locked
        # Surface should unlock
        surf.unlock()
        self.assertIs(surf.get_locked(), blit_locked_test(surf))  # Unlocked

        # Check multiple locks
        surf = pygame.Surface((100, 100))
        surf.lock()
        surf.lock()
        self.assertIs(surf.get_locked(), blit_locked_test(surf))  # Locked
        surf.unlock()
        self.assertIs(surf.get_locked(), blit_locked_test(surf))  # Locked
        surf.unlock()
        self.assertIs(surf.get_locked(), blit_locked_test(surf))  # Unlocked

        # Check many locks
        surf = pygame.Surface((100, 100))
        for i in range(1000):
            surf.lock()
        self.assertIs(surf.get_locked(), blit_locked_test(surf))  # Locked
        for i in range(1000):
            surf.unlock()
        self.assertFalse(surf.get_locked())  # Unlocked

        # Unlocking an unlocked surface
        surf = pygame.Surface((100, 100))
        surf.unlock()
        self.assertIs(surf.get_locked(), blit_locked_test(surf))  # Unlocked
        surf.unlock()
        self.assertIs(surf.get_locked(), blit_locked_test(surf))  # Unlocked

    def test_get_locks(self):
        # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_locks:

        # Surface.get_locks(): return tuple
        # Gets the locks for the Surface
        #
        # Returns the currently existing locks for the Surface.

        # test on a surface that is not initially locked
        surface = pygame.Surface((100, 100))
        self.assertEqual(surface.get_locks(), ())

        # test on the same surface after it has been locked
        surface.lock()
        self.assertEqual(surface.get_locks(), (surface,))

        # test on the same surface after it has been unlocked
        surface.unlock()
        self.assertEqual(surface.get_locks(), ())

        # test with PixelArray initialization: locks surface
        pxarray = pygame.PixelArray(surface)
        self.assertNotEqual(surface.get_locks(), ())

        # closing the PixelArray releases the surface lock
        pxarray.close()
        self.assertEqual(surface.get_locks(), ())

        # AttributeError raised when called on invalid object type (i.e. not a pygame.Surface object)
        with self.assertRaises(AttributeError):
            "DUMMY".get_locks()

        # test multiple locks and unlocks on the same surface
        surface.lock()
        surface.lock()
        surface.lock()
        self.assertEqual(surface.get_locks(), (surface, surface, surface))

        surface.unlock()
        surface.unlock()
        self.assertEqual(surface.get_locks(), (surface,))
        surface.unlock()
        self.assertEqual(surface.get_locks(), ())

    def test_get_losses(self):
        """Ensure a surface's losses can be retrieved"""
        pygame.display.init()
        try:
            # Masks for different color component configurations
            mask8 = (224, 28, 3, 0)
            mask15 = (31744, 992, 31, 0)
            mask16 = (63488, 2016, 31, 0)
            mask24 = (16711680, 65280, 255, 0)
            mask32 = (4278190080, 16711680, 65280, 255)

            # Surfaces with standard depths and masks
            display_surf = pygame.display.set_mode((100, 100))
            surf = pygame.Surface((100, 100))
            surf_8bit = pygame.Surface((100, 100), depth=8, masks=mask8)
            surf_15bit = pygame.Surface((100, 100), depth=15, masks=mask15)
            surf_16bit = pygame.Surface((100, 100), depth=16, masks=mask16)
            surf_24bit = pygame.Surface((100, 100), depth=24, masks=mask24)
            surf_32bit = pygame.Surface((100, 100), depth=32, masks=mask32)

            # Test output is correct type, length, and value range
            losses = surf.get_losses()
            self.assertIsInstance(losses, tuple)
            self.assertEqual(len(losses), 4)
            for loss in losses:
                self.assertIsInstance(loss, int)
                self.assertGreaterEqual(loss, 0)
                self.assertLessEqual(loss, 8)

            # Test each surface for correct losses
            # Display surface losses gives idea of default surface losses
            if display_surf.get_losses() == (0, 0, 0, 8):
                self.assertEqual(losses, (0, 0, 0, 8))
            elif display_surf.get_losses() == (8, 8, 8, 8):
                self.assertEqual(losses, (8, 8, 8, 8))

            self.assertEqual(surf_8bit.get_losses(), (5, 5, 6, 8))
            self.assertEqual(surf_15bit.get_losses(), (3, 3, 3, 8))
            self.assertEqual(surf_16bit.get_losses(), (3, 2, 3, 8))
            self.assertEqual(surf_24bit.get_losses(), (0, 0, 0, 8))
            self.assertEqual(surf_32bit.get_losses(), (0, 0, 0, 0))

            # Method should fail when display is not initialized
            with self.assertRaises(pygame.error):
                surface = pygame.display.set_mode((100, 100))
                pygame.display.quit()
                surface.get_losses()
        finally:
            pygame.display.quit()

    def test_get_masks__rgba(self):
        """
        Ensure that get_mask can return RGBA mask.
        """
        masks = [
            (0x0F00, 0x00F0, 0x000F, 0xF000),
            (0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000),
        ]
        depths = [16, 32]
        for expected, depth in list(zip(masks, depths)):
            surface = pygame.Surface((10, 10), pygame.SRCALPHA, depth)
            self.assertEqual(expected, surface.get_masks())

    def test_get_masks__rgb(self):
        """
        Ensure that get_mask can return RGB mask.
        """
        masks = [
            (0x60, 0x1C, 0x03, 0x00),
            (0xF00, 0x0F0, 0x00F, 0x000),
            (0x7C00, 0x03E0, 0x001F, 0x0000),
            (0xF800, 0x07E0, 0x001F, 0x0000),
            (0xFF0000, 0x00FF00, 0x0000FF, 0x000000),
            (0xFF0000, 0x00FF00, 0x0000FF, 0x000000),
        ]
        depths = [8, 12, 15, 16, 24, 32]
        for expected, depth in list(zip(masks, depths)):
            surface = pygame.Surface((10, 10), 0, depth)
            if depth == 8:
                expected = (0x00, 0x00, 0x00, 0x00)
            self.assertEqual(expected, surface.get_masks())

    def test_get_masks__no_surface(self):
        """
        Ensure that after display.quit, calling get_masks raises pygame.error.
        """
        with self.assertRaises(pygame.error):
            surface = pygame.display.set_mode((10, 10))
            pygame.display.quit()
            surface.get_masks()

    def test_get_offset(self):
        """get_offset returns the (0,0) if surface is not a child
        returns the position of child subsurface inside of parent
        """
        pygame.display.init()
        try:
            surf = pygame.Surface((100, 100))
            self.assertEqual(surf.get_offset(), (0, 0))

            # subsurface offset test
            subsurf = surf.subsurface(1, 1, 10, 10)
            self.assertEqual(subsurf.get_offset(), (1, 1))

            with self.assertRaises(pygame.error):
                surface = pygame.display.set_mode()
                pygame.display.quit()
                surface.get_offset()
        finally:
            pygame.display.quit()

    def test_get_palette(self):
        palette = [Color(i, i, i) for i in range(256)]
        surf = pygame.Surface((2, 2), 0, 8)
        surf.set_palette(palette)
        palette2 = surf.get_palette()

        self.assertEqual(len(palette2), len(palette))
        for c2, c in zip(palette2, palette):
            self.assertEqual(c2, c)
        for c in palette2:
            self.assertIsInstance(c, pygame.Color)

    def test_get_palette_at(self):
        # See also test_get_palette
        surf = pygame.Surface((2, 2), 0, 8)
        color = pygame.Color(1, 2, 3, 255)
        surf.set_palette_at(0, color)
        color2 = surf.get_palette_at(0)
        self.assertIsInstance(color2, pygame.Color)
        self.assertEqual(color2, color)
        self.assertRaises(IndexError, surf.get_palette_at, -1)
        self.assertRaises(IndexError, surf.get_palette_at, 256)

    def test_get_pitch(self):
        # Test get_pitch() on several surfaces of varying size/depth
        sizes = ((2, 2), (7, 33), (33, 7), (2, 734), (734, 2), (734, 734))
        depths = [8, 24, 32]
        for width, height in sizes:
            for depth in depths:
                # Test get_pitch() on parent surface
                surf = pygame.Surface((width, height), depth=depth)
                buff = surf.get_buffer()
                pitch = buff.length / surf.get_height()
                test_pitch = surf.get_pitch()
                self.assertEqual(pitch, test_pitch)
                # Test get_pitch() on subsurface with same rect as parent
                rect1 = surf.get_rect()
                subsurf1 = surf.subsurface(rect1)
                sub_buff1 = subsurf1.get_buffer()
                sub_pitch1 = sub_buff1.length / subsurf1.get_height()
                test_sub_pitch1 = subsurf1.get_pitch()
                self.assertEqual(sub_pitch1, test_sub_pitch1)
                # Test get_pitch on subsurface with modified rect
                rect2 = rect1.inflate(-width / 2, -height / 2)
                subsurf2 = surf.subsurface(rect2)
                sub_buff2 = subsurf2.get_buffer()
                sub_pitch2 = sub_buff2.length / float(subsurf2.get_height())
                test_sub_pitch2 = subsurf2.get_pitch()
                self.assertEqual(sub_pitch2, test_sub_pitch2)

    def test_get_shifts(self):
        """
        Tests whether Surface.get_shifts returns proper
        RGBA shifts under various conditions.
        """
        # __doc__ (as of 2008-08-02) for pygame.surface.Surface.get_shifts:
        # Surface.get_shifts(): return (R, G, B, A)
        # the bit shifts needed to convert between color and mapped integer.
        # Returns the pixel shifts need to convert between each color and a
        # mapped integer.
        # This value is not needed for normal Pygame usage.

        # Test for SDL2 on surfaces with various depths and alpha on/off
        depths = [8, 24, 32]
        alpha = 128
        off = None
        for bit_depth in depths:
            surface = pygame.Surface((32, 32), depth=bit_depth)
            surface.set_alpha(alpha)
            r1, g1, b1, a1 = surface.get_shifts()
            surface.set_alpha(off)
            r2, g2, b2, a2 = surface.get_shifts()
            self.assertEqual((r1, g1, b1, a1), (r2, g2, b2, a2))

    def test_get_size(self):
        sizes = ((1, 1), (119, 10), (1000, 1000), (1, 5000), (1221, 1), (99, 999))
        for width, height in sizes:
            surf = pygame.Surface((width, height))
            found_size = surf.get_size()
            self.assertEqual((width, height), found_size)

    def test_lock(self):
        # __doc__ (as of 2008-08-02) for pygame.surface.Surface.lock:

        # Surface.lock(): return None
        # lock the Surface memory for pixel access
        #
        # Lock the pixel data of a Surface for access. On accelerated
        # Surfaces, the pixel data may be stored in volatile video memory or
        # nonlinear compressed forms. When a Surface is locked the pixel
        # memory becomes available to access by regular software. Code that
        # reads or writes pixel values will need the Surface to be locked.
        #
        # Surfaces should not remain locked for more than necessary. A locked
        # Surface can often not be displayed or managed by Pygame.
        #
        # Not all Surfaces require locking. The Surface.mustlock() method can
        # determine if it is actually required. There is no performance
        # penalty for locking and unlocking a Surface that does not need it.
        #
        # All pygame functions will automatically lock and unlock the Surface
        # data as needed. If a section of code is going to make calls that
        # will repeatedly lock and unlock the Surface many times, it can be
        # helpful to wrap the block inside a lock and unlock pair.
        #
        # It is safe to nest locking and unlocking calls. The surface will
        # only be unlocked after the final lock is released.
        #

        # Basic
        surf = pygame.Surface((100, 100))
        surf.lock()
        self.assertTrue(surf.get_locked())

        # Nested
        surf = pygame.Surface((100, 100))
        surf.lock()
        surf.lock()
        surf.unlock()
        self.assertTrue(surf.get_locked())
        surf.unlock()
        surf.lock()
        surf.lock()
        self.assertTrue(surf.get_locked())
        surf.unlock()
        self.assertTrue(surf.get_locked())
        surf.unlock()
        self.assertFalse(surf.get_locked())

        # Already Locked
        surf = pygame.Surface((100, 100))
        surf.lock()
        surf.lock()
        self.assertTrue(surf.get_locked())
        surf.unlock()
        self.assertTrue(surf.get_locked())
        surf.unlock()
        self.assertFalse(surf.get_locked())

    def test_map_rgb(self):
        color = Color(0, 128, 255, 64)
        surf = pygame.Surface((5, 5), SRCALPHA, 32)
        c = surf.map_rgb(color)
        self.assertEqual(surf.unmap_rgb(c), color)

        self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 0))
        surf.fill(c)
        self.assertEqual(surf.get_at((0, 0)), color)

        surf.fill((0, 0, 0, 0))
        self.assertEqual(surf.get_at((0, 0)), (0, 0, 0, 0))
        surf.set_at((0, 0), c)
        self.assertEqual(surf.get_at((0, 0)), color)

    def test_mustlock(self):
        # Test that subsurfaces mustlock
        surf = pygame.Surface((1024, 1024))
        subsurf = surf.subsurface((0, 0, 1024, 1024))
        self.assertTrue(subsurf.mustlock())
        self.assertFalse(surf.mustlock())
        # Tests nested subsurfaces
        rects = ((0, 0, 512, 512), (0, 0, 256, 256), (0, 0, 128, 128))
        surf_stack = []
        surf_stack.append(surf)
        surf_stack.append(subsurf)
        for rect in rects:
            surf_stack.append(surf_stack[-1].subsurface(rect))
            self.assertTrue(surf_stack[-1].mustlock())
            self.assertTrue(surf_stack[-2].mustlock())

    def test_set_alpha_none(self):
        """surf.set_alpha(None) disables blending"""
        s = pygame.Surface((1, 1), SRCALPHA, 32)
        s.fill((0, 255, 0, 128))
        s.set_alpha(None)
        self.assertEqual(None, s.get_alpha())

        s2 = pygame.Surface((1, 1), SRCALPHA, 32)
        s2.fill((255, 0, 0, 255))
        s2.blit(s, (0, 0))
        self.assertEqual(s2.get_at((0, 0))[0], 0, "the red component should be 0")

    def test_set_alpha_value(self):
        """surf.set_alpha(x), where x != None, enables blending"""
        s = pygame.Surface((1, 1), SRCALPHA, 32)
        s.fill((0, 255, 0, 128))
        s.set_alpha(255)

        s2 = pygame.Surface((1, 1), SRCALPHA, 32)
        s2.fill((255, 0, 0, 255))
        s2.blit(s, (0, 0))
        self.assertGreater(
            s2.get_at((0, 0))[0], 0, "the red component should be above 0"
        )

    def test_palette_colorkey(self):
        """test bug discovered by robertpfeiffer
        https://github.com/pygame-community/pygame-ce/issues/541
        """
        surf = pygame.image.load(example_path(os.path.join("data", "alien2.png")))
        key = surf.get_colorkey()
        self.assertEqual(surf.get_palette()[surf.map_rgb(key)], key)

    def test_palette_colorkey_set_px(self):
        surf = pygame.image.load(example_path(os.path.join("data", "alien2.png")))
        key = surf.get_colorkey()
        surf.set_at((0, 0), key)
        self.assertEqual(surf.get_at((0, 0)), key)

    def test_palette_colorkey_fill(self):
        surf = pygame.image.load(example_path(os.path.join("data", "alien2.png")))
        key = surf.get_colorkey()
        surf.fill(key)
        self.assertEqual(surf.get_at((0, 0)), key)

    def test_set_palette(self):
        palette = [pygame.Color(i, i, i) for i in range(256)]
        palette[10] = tuple(palette[10])  # 4 element tuple
        palette[11] = tuple(palette[11])[0:3]  # 3 element tuple

        surf = pygame.Surface((2, 2), 0, 8)
        surf.set_palette(palette)
        for i in range(256):
            self.assertEqual(surf.map_rgb(palette[i]), i, "palette color %i" % (i,))
            c = palette[i]
            surf.fill(c)
            self.assertEqual(surf.get_at((0, 0)), c, "palette color %i" % (i,))
        for i in range(10):
            palette[i] = pygame.Color(255 - i, 0, 0)
        surf.set_palette(palette[0:10])
        for i in range(256):
            self.assertEqual(surf.map_rgb(palette[i]), i, "palette color %i" % (i,))
            c = palette[i]
            surf.fill(c)
            self.assertEqual(surf.get_at((0, 0)), c, "palette color %i" % (i,))
        self.assertRaises(ValueError, surf.set_palette, [Color(1, 2, 3, 254)])
        self.assertRaises(ValueError, surf.set_palette, (1, 2, 3, 254))

    def test_set_palette__fail(self):
        palette = 256 * [(10, 20, 30)]
        surf = pygame.Surface((2, 2), 0, 32)
        self.assertRaises(pygame.error, surf.set_palette, palette)

    def test_set_palette__set_at(self):
        surf = pygame.Surface((2, 2), depth=8)
        palette = 256 * [(10, 20, 30)]
        palette[1] = (50, 40, 30)
        surf.set_palette(palette)

        # calling set_at on a palettized surface should set the pixel to
        # the closest color in the palette.
        surf.set_at((0, 0), (60, 50, 40))
        self.assertEqual(surf.get_at((0, 0)), (50, 40, 30, 255))
        self.assertEqual(surf.get_at((1, 0)), (10, 20, 30, 255))

    def test_set_palette_at(self):
        surf = pygame.Surface((2, 2), 0, 8)
        original = surf.get_palette_at(10)
        replacement = Color(1, 1, 1, 255)
        if replacement == original:
            replacement = Color(2, 2, 2, 255)
        surf.set_palette_at(10, replacement)
        self.assertEqual(surf.get_palette_at(10), replacement)
        next = tuple(original)
        surf.set_palette_at(10, next)
        self.assertEqual(surf.get_palette_at(10), next)
        next = tuple(original)[0:3]
        surf.set_palette_at(10, next)
        self.assertEqual(surf.get_palette_at(10), next)
        self.assertRaises(IndexError, surf.set_palette_at, 256, replacement)
        self.assertRaises(IndexError, surf.set_palette_at, -1, replacement)

    def test_subsurface(self):
        # __doc__ (as of 2008-08-02) for pygame.surface.Surface.subsurface:

        # Surface.subsurface(Rect): return Surface
        # create a new surface that references its parent
        #
        # Returns a new Surface that shares its pixels with its new parent.
        # The new Surface is considered a child of the original. Modifications
        # to either Surface pixels will affect each other. Surface information
        # like clipping area and color keys are unique to each Surface.
        #
        # The new Surface will inherit the palette, color key, and alpha
        # settings from its parent.
        #
        # It is possible to have any number of subsurfaces and subsubsurfaces
        # on the parent. It is also possible to subsurface the display Surface
        # if the display mode is not hardware accelerated.
        #
        # See the Surface.get_offset(), Surface.get_parent() to learn more
        # about the state of a subsurface.
        #

        surf = pygame.Surface((16, 16))
        s = surf.subsurface(0, 0, 1, 1)
        s = surf.subsurface((0, 0, 1, 1))

        # s = surf.subsurface((0,0,1,1), 1)
        # This form is not acceptable.
        # s = surf.subsurface(0,0,10,10, 1)

        self.assertRaises(ValueError, surf.subsurface, (0, 0, 1, 1, 666))

        self.assertEqual(s.get_shifts(), surf.get_shifts())
        self.assertEqual(s.get_masks(), surf.get_masks())
        self.assertEqual(s.get_losses(), surf.get_losses())

        # Issue https://github.com/pygame-community/pygame-ce/issues/17
        surf = pygame.Surface.__new__(pygame.Surface)
        self.assertRaises(pygame.error, surf.subsurface, (0, 0, 0, 0))

    def test_unlock(self):
        # Basic
        surf = pygame.Surface((100, 100))
        surf.lock()
        surf.unlock()
        self.assertFalse(surf.get_locked())

        # Nested
        surf = pygame.Surface((100, 100))
        surf.lock()
        surf.lock()
        surf.unlock()
        self.assertTrue(surf.get_locked())
        surf.unlock()
        self.assertFalse(surf.get_locked())

        # Already Unlocked
        surf = pygame.Surface((100, 100))
        surf.unlock()
        self.assertFalse(surf.get_locked())
        surf.unlock()
        self.assertFalse(surf.get_locked())

        # Surface can be relocked
        surf = pygame.Surface((100, 100))
        surf.lock()
        surf.unlock()
        self.assertFalse(surf.get_locked())
        surf.lock()
        surf.unlock()
        self.assertFalse(surf.get_locked())

    def test_unmap_rgb(self):
        # Special case, 8 bit-per-pixel surface (has a palette).
        surf = pygame.Surface((2, 2), 0, 8)
        c = (1, 1, 1)  # Unlikely to be in a default palette.
        i = 67
        surf.set_palette_at(i, c)
        unmapped_c = surf.unmap_rgb(i)
        self.assertEqual(unmapped_c, c)
        # Confirm it is a Color instance
        self.assertIsInstance(unmapped_c, pygame.Color)

        # Remaining, non-pallete, cases.
        c = (128, 64, 12, 255)
        formats = [(0, 16), (0, 24), (0, 32), (SRCALPHA, 16), (SRCALPHA, 32)]
        for flags, bitsize in formats:
            surf = pygame.Surface((2, 2), flags, bitsize)
            unmapped_c = surf.unmap_rgb(surf.map_rgb(c))
            surf.fill(c)
            comparison_c = surf.get_at((0, 0))
            self.assertEqual(
                unmapped_c,
                comparison_c,
                "%s != %s, flags: %i, bitsize: %i"
                % (unmapped_c, comparison_c, flags, bitsize),
            )
            # Confirm it is a Color instance
            self.assertIsInstance(unmapped_c, pygame.Color)

    def test_scroll(self):
        scrolls = [
            (8, 2, 3),
            (16, 2, 3),
            (24, 2, 3),
            (32, 2, 3),
            (32, -1, -3),
            (32, 0, 0),
            (32, 11, 0),
            (32, 0, 11),
            (32, -11, 0),
            (32, 0, -11),
            (32, -11, 2),
            (32, 2, -11),
        ]
        for bitsize, dx, dy in scrolls:
            surf = pygame.Surface((10, 10), 0, bitsize)
            surf.fill((255, 0, 0))
            surf.fill((0, 255, 0), (2, 2, 2, 2))
            comp = surf.copy()
            comp.blit(surf, (dx, dy))
            surf.scroll(dx, dy)
            w, h = surf.get_size()
            for x in range(w):
                for y in range(h):
                    with self.subTest(x=x, y=y):
                        self.assertEqual(
                            surf.get_at((x, y)),
                            comp.get_at((x, y)),
                            "%s != %s, bpp:, %i, x: %i, y: %i"
                            % (
                                surf.get_at((x, y)),
                                comp.get_at((x, y)),
                                bitsize,
                                dx,
                                dy,
                            ),
                        )
        # Confirm clip rect containment
        surf = pygame.Surface((20, 13), 0, 32)
        surf.fill((255, 0, 0))
        surf.fill((0, 255, 0), (7, 1, 6, 6))
        comp = surf.copy()
        clip = Rect(3, 1, 8, 14)
        surf.set_clip(clip)
        comp.set_clip(clip)
        comp.blit(surf, (clip.x + 2, clip.y + 3), surf.get_clip())
        surf.scroll(2, 3)
        w, h = surf.get_size()
        for x in range(w):
            for y in range(h):
                self.assertEqual(surf.get_at((x, y)), comp.get_at((x, y)))
        # Confirm keyword arguments and per-pixel alpha
        spot_color = (0, 255, 0, 128)
        surf = pygame.Surface((4, 4), pygame.SRCALPHA, 32)
        surf.fill((255, 0, 0, 255))
        surf.set_at((1, 1), spot_color)
        surf.scroll(dx=1)
        self.assertEqual(surf.get_at((2, 1)), spot_color)
        surf.scroll(dy=1)
        self.assertEqual(surf.get_at((2, 2)), spot_color)
        surf.scroll(dy=1, dx=1)
        self.assertEqual(surf.get_at((3, 3)), spot_color)
        surf.scroll(dx=-3, dy=-3)
        self.assertEqual(surf.get_at((0, 0)), spot_color)


class SurfaceSubtypeTest(unittest.TestCase):
    """pygame-ce issue #295: Methods that return a new Surface preserve subclasses"""

    def setUp(self):
        pygame.display.init()

    def tearDown(self):
        pygame.display.quit()

    def test_copy(self):
        """Ensure method copy() preserves the surface's class

        When Surface is subclassed, the inherited copy() method will return
        instances of the subclass. Non Surface fields are uncopied, however.
        This includes instance attributes.
        """
        expected_size = (32, 32)
        ms1 = SurfaceSubclass(expected_size, SRCALPHA, 32)
        ms2 = ms1.copy()

        self.assertIsNot(ms1, ms2)
        self.assertIsInstance(ms1, pygame.Surface)
        self.assertIsInstance(ms2, pygame.Surface)
        self.assertIsInstance(ms1, SurfaceSubclass)
        self.assertIsInstance(ms2, SurfaceSubclass)
        self.assertTrue(ms1.test_attribute)
        self.assertRaises(AttributeError, getattr, ms2, "test_attribute")
        self.assertEqual(ms2.get_size(), expected_size)

    def test_convert(self):
        """Ensure method convert() preserves the surface's class

        When Surface is subclassed, the inherited convert() method will return
        instances of the subclass. Non Surface fields are omitted, however.
        This includes instance attributes.
        """
        expected_size = (32, 32)
        ms1 = SurfaceSubclass(expected_size, 0, 24)
        ms2 = ms1.convert(24)

        self.assertIsNot(ms1, ms2)
        self.assertIsInstance(ms1, pygame.Surface)
        self.assertIsInstance(ms2, pygame.Surface)
        self.assertIsInstance(ms1, SurfaceSubclass)
        self.assertIsInstance(ms2, SurfaceSubclass)
        self.assertTrue(ms1.test_attribute)
        self.assertRaises(AttributeError, getattr, ms2, "test_attribute")
        self.assertEqual(ms2.get_size(), expected_size)

    def test_convert_alpha(self):
        """Ensure method convert_alpha() preserves the surface's class

        When Surface is subclassed, the inherited convert_alpha() method will
        return instances of the subclass. Non Surface fields are omitted,
        however. This includes instance attributes.
        """
        pygame.display.set_mode((40, 40))
        expected_size = (32, 32)

        ms1 = SurfaceSubclass(expected_size, SRCALPHA, 32)
        ms2 = ms1.convert_alpha()

        self.assertIsNot(ms1, ms2)
        self.assertIsInstance(ms1, pygame.Surface)
        self.assertIsInstance(ms2, pygame.Surface)
        self.assertIsInstance(ms1, SurfaceSubclass)
        self.assertIsInstance(ms2, SurfaceSubclass)
        self.assertTrue(ms1.test_attribute)
        self.assertRaises(AttributeError, getattr, ms2, "test_attribute")
        self.assertEqual(ms2.get_size(), expected_size)

    def test_subsurface(self):
        """Ensure method subsurface() preserves the surface's class

        When Surface is subclassed, the inherited subsurface() method will
        return instances of the subclass. Non Surface fields are uncopied,
        however. This includes instance attributes.
        """
        expected_size = (10, 12)
        ms1 = SurfaceSubclass((32, 32), SRCALPHA, 32)
        ms2 = ms1.subsurface((4, 5), expected_size)

        self.assertIsNot(ms1, ms2)
        self.assertIsInstance(ms1, pygame.Surface)
        self.assertIsInstance(ms2, pygame.Surface)
        self.assertIsInstance(ms1, SurfaceSubclass)
        self.assertIsInstance(ms2, SurfaceSubclass)
        self.assertTrue(ms1.test_attribute)
        self.assertRaises(AttributeError, getattr, ms2, "test_attribute")
        self.assertEqual(ms2.get_size(), expected_size)


class SurfaceGetBufferTest(unittest.TestCase):
    # These tests require ctypes. They are disabled if ctypes
    # is not installed.
    try:
        ArrayInterface
    except NameError:
        __tags__ = ("ignore", "subprocess_ignore")

    lilendian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN

    def _check_interface_2D(self, s):
        s_w, s_h = s.get_size()
        s_bytesize = s.get_bytesize()
        s_pitch = s.get_pitch()
        s_pixels = s._pixels_address

        # check the array interface structure fields.
        v = s.get_view("2")
        if not IS_PYPY:
            flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE
            if s.get_pitch() == s_w * s_bytesize:
                flags |= PAI_FORTRAN

            inter = ArrayInterface(v)

            self.assertEqual(inter.two, 2)
            self.assertEqual(inter.nd, 2)
            self.assertEqual(inter.typekind, "u")
            self.assertEqual(inter.itemsize, s_bytesize)
            self.assertEqual(inter.shape[0], s_w)
            self.assertEqual(inter.shape[1], s_h)
            self.assertEqual(inter.strides[0], s_bytesize)
            self.assertEqual(inter.strides[1], s_pitch)
            self.assertEqual(inter.flags, flags)
            self.assertEqual(inter.data, s_pixels)

    def _check_interface_3D(self, s):
        s_w, s_h = s.get_size()
        s_bytesize = s.get_bytesize()
        s_pitch = s.get_pitch()
        s_pixels = s._pixels_address
        s_shifts = list(s.get_shifts())

        # Check for RGB or BGR surface.
        if s_shifts[0:3] == [0, 8, 16]:
            if self.lilendian:
                # RGB
                offset = 0
                step = 1
            else:
                # BGR
                offset = s_bytesize - 1
                step = -1
        elif s_shifts[0:3] == [8, 16, 24]:
            if self.lilendian:
                # xRGB
                offset = 1
                step = 1
            else:
                # BGRx
                offset = s_bytesize - 2
                step = -1
        elif s_shifts[0:3] == [16, 8, 0]:
            if self.lilendian:
                # BGR
                offset = 2
                step = -1
            else:
                # RGB
                offset = s_bytesize - 3
                step = 1
        elif s_shifts[0:3] == [24, 16, 8]:
            if self.lilendian:
                # BGRx
                offset = 2
                step = -1
            else:
                # RGBx
                offset = s_bytesize - 4
                step = -1
        else:
            return

        # check the array interface structure fields.
        v = s.get_view("3")
        if not IS_PYPY:
            inter = ArrayInterface(v)
            flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE
            self.assertEqual(inter.two, 2)
            self.assertEqual(inter.nd, 3)
            self.assertEqual(inter.typekind, "u")
            self.assertEqual(inter.itemsize, 1)
            self.assertEqual(inter.shape[0], s_w)
            self.assertEqual(inter.shape[1], s_h)
            self.assertEqual(inter.shape[2], 3)
            self.assertEqual(inter.strides[0], s_bytesize)
            self.assertEqual(inter.strides[1], s_pitch)
            self.assertEqual(inter.strides[2], step)
            self.assertEqual(inter.flags, flags)
            self.assertEqual(inter.data, s_pixels + offset)

    def _check_interface_rgba(self, s, plane):
        s_w, s_h = s.get_size()
        s_bytesize = s.get_bytesize()
        s_pitch = s.get_pitch()
        s_pixels = s._pixels_address
        s_shifts = s.get_shifts()
        s_masks = s.get_masks()

        # Find the color plane position within the pixel.
        if not s_masks[plane]:
            return
        alpha_shift = s_shifts[plane]
        offset = alpha_shift // 8
        if not self.lilendian:
            offset = s_bytesize - offset - 1

        # check the array interface structure fields.
        v = s.get_view("rgba"[plane])
        if not IS_PYPY:
            inter = ArrayInterface(v)
            flags = PAI_ALIGNED | PAI_NOTSWAPPED | PAI_WRITEABLE
            self.assertEqual(inter.two, 2)
            self.assertEqual(inter.nd, 2)
            self.assertEqual(inter.typekind, "u")
            self.assertEqual(inter.itemsize, 1)
            self.assertEqual(inter.shape[0], s_w)
            self.assertEqual(inter.shape[1], s_h)
            self.assertEqual(inter.strides[0], s_bytesize)
            self.assertEqual(inter.strides[1], s_pitch)
            self.assertEqual(inter.flags, flags)
            self.assertEqual(inter.data, s_pixels + offset)

    def test_array_interface(self):
        self._check_interface_2D(pygame.Surface((5, 7), 0, 8))
        self._check_interface_2D(pygame.Surface((5, 7), 0, 16))
        self._check_interface_2D(pygame.Surface((5, 7), pygame.SRCALPHA, 16))
        self._check_interface_3D(pygame.Surface((5, 7), 0, 24))
        self._check_interface_3D(pygame.Surface((8, 4), 0, 24))  # No gaps
        self._check_interface_2D(pygame.Surface((5, 7), 0, 32))
        self._check_interface_3D(pygame.Surface((5, 7), 0, 32))
        self._check_interface_2D(pygame.Surface((5, 7), pygame.SRCALPHA, 32))
        self._check_interface_3D(pygame.Surface((5, 7), pygame.SRCALPHA, 32))

    def test_array_interface_masks(self):
        """Test non-default color byte orders on 3D views"""

        sz = (5, 7)
        # Reversed RGB byte order
        s = pygame.Surface(sz, 0, 32)
        s_masks = list(s.get_masks())
        masks = [0xFF, 0xFF00, 0xFF0000]
        if s_masks[0:3] == masks or s_masks[0:3] == masks[::-1]:
            masks = s_masks[2::-1] + s_masks[3:4]
            self._check_interface_3D(pygame.Surface(sz, 0, 32, masks))
        s = pygame.Surface(sz, 0, 24)
        s_masks = list(s.get_masks())
        masks = [0xFF, 0xFF00, 0xFF0000]
        if s_masks[0:3] == masks or s_masks[0:3] == masks[::-1]:
            masks = s_masks[2::-1] + s_masks[3:4]
            self._check_interface_3D(pygame.Surface(sz, 0, 24, masks))

        masks = [0xFF00, 0xFF0000, 0xFF000000, 0]
        self._check_interface_3D(pygame.Surface(sz, 0, 32, masks))

    def test_array_interface_alpha(self):
        for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], [24, 16, 8, 0], [16, 8, 0, 24]]:
            masks = [0xFF << s for s in shifts]
            s = pygame.Surface((4, 2), pygame.SRCALPHA, 32, masks)
            self._check_interface_rgba(s, 3)

    def test_array_interface_rgb(self):
        for shifts in [[0, 8, 16, 24], [8, 16, 24, 0], [24, 16, 8, 0], [16, 8, 0, 24]]:
            masks = [0xFF << s for s in shifts]
            masks[3] = 0
            for plane in range(3):
                s = pygame.Surface((4, 2), 0, 24)
                self._check_interface_rgba(s, plane)
                s = pygame.Surface((4, 2), 0, 32)
                self._check_interface_rgba(s, plane)

    @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
    def test_newbuf_PyBUF_flags_bytes(self):
        from pygame.tests.test_utils import buftools

        Importer = buftools.Importer
        s = pygame.Surface((10, 6), 0, 32)
        a = s.get_buffer()
        b = Importer(a, buftools.PyBUF_SIMPLE)
        self.assertEqual(b.ndim, 0)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, 1)
        self.assertTrue(b.shape is None)
        self.assertTrue(b.strides is None)
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s._pixels_address)
        b = Importer(a, buftools.PyBUF_WRITABLE)
        self.assertEqual(b.ndim, 0)
        self.assertTrue(b.format is None)
        self.assertFalse(b.readonly)
        b = Importer(a, buftools.PyBUF_FORMAT)
        self.assertEqual(b.ndim, 0)
        self.assertEqual(b.format, "B")
        b = Importer(a, buftools.PyBUF_ND)
        self.assertEqual(b.ndim, 1)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, 1)
        self.assertEqual(b.shape, (a.length,))
        self.assertTrue(b.strides is None)
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s._pixels_address)
        b = Importer(a, buftools.PyBUF_STRIDES)
        self.assertEqual(b.ndim, 1)
        self.assertTrue(b.format is None)
        self.assertEqual(b.strides, (1,))
        s2 = s.subsurface((1, 1, 7, 4))  # Not contiguous
        a = s2.get_buffer()
        b = Importer(a, buftools.PyBUF_SIMPLE)
        self.assertEqual(b.ndim, 0)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, 1)
        self.assertTrue(b.shape is None)
        self.assertTrue(b.strides is None)
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s2._pixels_address)
        b = Importer(a, buftools.PyBUF_C_CONTIGUOUS)
        self.assertEqual(b.ndim, 1)
        self.assertEqual(b.strides, (1,))
        b = Importer(a, buftools.PyBUF_F_CONTIGUOUS)
        self.assertEqual(b.ndim, 1)
        self.assertEqual(b.strides, (1,))
        b = Importer(a, buftools.PyBUF_ANY_CONTIGUOUS)
        self.assertEqual(b.ndim, 1)
        self.assertEqual(b.strides, (1,))

    @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
    def test_newbuf_PyBUF_flags_0D(self):
        # This is the same handler as used by get_buffer(), so just
        # confirm that it succeeds for one case.
        from pygame.tests.test_utils import buftools

        Importer = buftools.Importer
        s = pygame.Surface((10, 6), 0, 32)
        a = s.get_view("0")
        b = Importer(a, buftools.PyBUF_SIMPLE)
        self.assertEqual(b.ndim, 0)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, 1)
        self.assertTrue(b.shape is None)
        self.assertTrue(b.strides is None)
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s._pixels_address)

    @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
    def test_newbuf_PyBUF_flags_1D(self):
        from pygame.tests.test_utils import buftools

        Importer = buftools.Importer
        s = pygame.Surface((10, 6), 0, 32)
        a = s.get_view("1")
        b = Importer(a, buftools.PyBUF_SIMPLE)
        self.assertEqual(b.ndim, 0)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, s.get_bytesize())
        self.assertTrue(b.shape is None)
        self.assertTrue(b.strides is None)
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s._pixels_address)
        b = Importer(a, buftools.PyBUF_WRITABLE)
        self.assertEqual(b.ndim, 0)
        self.assertTrue(b.format is None)
        self.assertFalse(b.readonly)
        b = Importer(a, buftools.PyBUF_FORMAT)
        self.assertEqual(b.ndim, 0)
        self.assertEqual(b.format, "=I")
        b = Importer(a, buftools.PyBUF_ND)
        self.assertEqual(b.ndim, 1)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, s.get_bytesize())
        self.assertEqual(b.shape, (s.get_width() * s.get_height(),))
        self.assertTrue(b.strides is None)
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s._pixels_address)
        b = Importer(a, buftools.PyBUF_STRIDES)
        self.assertEqual(b.ndim, 1)
        self.assertTrue(b.format is None)
        self.assertEqual(b.strides, (s.get_bytesize(),))

    @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
    def test_newbuf_PyBUF_flags_2D(self):
        from pygame.tests.test_utils import buftools

        Importer = buftools.Importer
        s = pygame.Surface((10, 6), 0, 32)
        a = s.get_view("2")
        # Non-dimensional requests, no PyDEF_ND, are handled by the
        # 1D surface buffer code, so only need to confirm a success.
        b = Importer(a, buftools.PyBUF_SIMPLE)
        self.assertEqual(b.ndim, 0)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, s.get_bytesize())
        self.assertTrue(b.shape is None)
        self.assertTrue(b.strides is None)
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s._pixels_address)
        # Uniquely 2D
        b = Importer(a, buftools.PyBUF_STRIDES)
        self.assertEqual(b.ndim, 2)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, s.get_bytesize())
        self.assertEqual(b.shape, s.get_size())
        self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch()))
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s._pixels_address)
        b = Importer(a, buftools.PyBUF_RECORDS_RO)
        self.assertEqual(b.ndim, 2)
        self.assertEqual(b.format, "=I")
        self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch()))
        b = Importer(a, buftools.PyBUF_RECORDS)
        self.assertEqual(b.ndim, 2)
        self.assertEqual(b.format, "=I")
        self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch()))
        b = Importer(a, buftools.PyBUF_F_CONTIGUOUS)
        self.assertEqual(b.ndim, 2)
        self.assertEqual(b.format, None)
        self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch()))
        b = Importer(a, buftools.PyBUF_ANY_CONTIGUOUS)
        self.assertEqual(b.ndim, 2)
        self.assertEqual(b.format, None)
        self.assertEqual(b.strides, (s.get_bytesize(), s.get_pitch()))
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS)
        s2 = s.subsurface((1, 1, 7, 4))  # Not contiguous
        a = s2.get_view("2")
        b = Importer(a, buftools.PyBUF_STRIDES)
        self.assertEqual(b.ndim, 2)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, s2.get_bytesize())
        self.assertEqual(b.shape, s2.get_size())
        self.assertEqual(b.strides, (s2.get_bytesize(), s.get_pitch()))
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s2._pixels_address)
        b = Importer(a, buftools.PyBUF_RECORDS)
        self.assertEqual(b.ndim, 2)
        self.assertEqual(b.format, "=I")
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS)

    @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
    def test_newbuf_PyBUF_flags_3D(self):
        from pygame.tests.test_utils import buftools

        Importer = buftools.Importer
        s = pygame.Surface((12, 6), 0, 24)
        rmask, gmask, bmask, amask = s.get_masks()
        if self.lilendian:
            if rmask == 0x0000FF:
                color_step = 1
                addr_offset = 0
            else:
                color_step = -1
                addr_offset = 2
        else:
            if rmask == 0xFF0000:
                color_step = 1
                addr_offset = 0
            else:
                color_step = -1
                addr_offset = 2
        a = s.get_view("3")
        b = Importer(a, buftools.PyBUF_STRIDES)
        w, h = s.get_size()
        shape = w, h, 3
        strides = 3, s.get_pitch(), color_step
        self.assertEqual(b.ndim, 3)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, 1)
        self.assertEqual(b.shape, shape)
        self.assertEqual(b.strides, strides)
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s._pixels_address + addr_offset)
        b = Importer(a, buftools.PyBUF_RECORDS_RO)
        self.assertEqual(b.ndim, 3)
        self.assertEqual(b.format, "B")
        self.assertEqual(b.strides, strides)
        b = Importer(a, buftools.PyBUF_RECORDS)
        self.assertEqual(b.ndim, 3)
        self.assertEqual(b.format, "B")
        self.assertEqual(b.strides, strides)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS)

    @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
    def test_newbuf_PyBUF_flags_rgba(self):
        # All color plane views are handled by the same routine,
        # so only one plane need be checked.
        from pygame.tests.test_utils import buftools

        Importer = buftools.Importer
        s = pygame.Surface((12, 6), 0, 24)
        rmask, gmask, bmask, amask = s.get_masks()
        if self.lilendian:
            if rmask == 0x0000FF:
                addr_offset = 0
            else:
                addr_offset = 2
        else:
            if rmask == 0xFF0000:
                addr_offset = 0
            else:
                addr_offset = 2
        a = s.get_view("R")
        b = Importer(a, buftools.PyBUF_STRIDES)
        w, h = s.get_size()
        shape = w, h
        strides = s.get_bytesize(), s.get_pitch()
        self.assertEqual(b.ndim, 2)
        self.assertTrue(b.format is None)
        self.assertEqual(b.len, a.length)
        self.assertEqual(b.itemsize, 1)
        self.assertEqual(b.shape, shape)
        self.assertEqual(b.strides, strides)
        self.assertTrue(b.suboffsets is None)
        self.assertFalse(b.readonly)
        self.assertEqual(b.buf, s._pixels_address + addr_offset)
        b = Importer(a, buftools.PyBUF_RECORDS_RO)
        self.assertEqual(b.ndim, 2)
        self.assertEqual(b.format, "B")
        self.assertEqual(b.strides, strides)
        b = Importer(a, buftools.PyBUF_RECORDS)
        self.assertEqual(b.ndim, 2)
        self.assertEqual(b.format, "B")
        self.assertEqual(b.strides, strides)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FORMAT)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS)
        self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS)


class SurfaceBlendTest(unittest.TestCase):
    def setUp(self):
        # Needed for 8 bits-per-pixel color palette surface tests.
        pygame.display.init()

    def tearDown(self):
        pygame.display.quit()

    _test_palette = [
        (0, 0, 0, 255),
        (10, 30, 60, 0),
        (25, 75, 100, 128),
        (200, 150, 100, 200),
        (0, 100, 200, 255),
    ]
    surf_size = (10, 12)
    _test_points = [
        ((0, 0), 1),
        ((4, 5), 1),
        ((9, 0), 2),
        ((5, 5), 2),
        ((0, 11), 3),
        ((4, 6), 3),
        ((9, 11), 4),
        ((5, 6), 4),
    ]

    def _make_surface(self, bitsize, srcalpha=False, palette=None):
        if palette is None:
            palette = self._test_palette
        flags = 0
        if srcalpha:
            flags |= SRCALPHA
        surf = pygame.Surface(self.surf_size, flags, bitsize)
        if bitsize == 8:
            surf.set_palette([c[:3] for c in palette])
        return surf

    def _fill_surface(self, surf, palette=None):
        if palette is None:
            palette = self._test_palette
        surf.fill(palette[1], (0, 0, 5, 6))
        surf.fill(palette[2], (5, 0, 5, 6))
        surf.fill(palette[3], (0, 6, 5, 6))
        surf.fill(palette[4], (5, 6, 5, 6))

    def _make_src_surface(self, bitsize, srcalpha=False, palette=None):
        surf = self._make_surface(bitsize, srcalpha, palette)
        self._fill_surface(surf, palette)
        return surf

    def _assert_surface(self, surf, palette=None, msg=""):
        if palette is None:
            palette = self._test_palette
        if surf.get_bitsize() == 16:
            palette = [surf.unmap_rgb(surf.map_rgb(c)) for c in palette]
        for posn, i in self._test_points:
            self.assertEqual(
                surf.get_at(posn),
                palette[i],
                "%s != %s: flags: %i, bpp: %i, posn: %s%s"
                % (
                    surf.get_at(posn),
                    palette[i],
                    surf.get_flags(),
                    surf.get_bitsize(),
                    posn,
                    msg,
                ),
            )

    def test_blit_blend(self):
        sources = [
            self._make_src_surface(8),
            self._make_src_surface(16),
            self._make_src_surface(16, srcalpha=True),
            self._make_src_surface(24),
            self._make_src_surface(32),
            self._make_src_surface(32, srcalpha=True),
        ]
        destinations = [
            self._make_surface(8),
            self._make_surface(16),
            self._make_surface(16, srcalpha=True),
            self._make_surface(24),
            self._make_surface(32),
            self._make_surface(32, srcalpha=True),
        ]
        blend = [
            ("BLEND_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)),
            ("BLEND_SUB", (100, 25, 0, 100), lambda a, b: max(a - b, 0)),
            ("BLEND_MULT", (100, 200, 0, 0), lambda a, b: ((a * b) + 255) >> 8),
            ("BLEND_MIN", (255, 0, 0, 255), min),
            ("BLEND_MAX", (0, 255, 0, 255), max),
        ]

        for src in sources:
            src_palette = [src.unmap_rgb(src.map_rgb(c)) for c in self._test_palette]
            for dst in destinations:
                for blend_name, dst_color, op in blend:
                    dc = dst.unmap_rgb(dst.map_rgb(dst_color))
                    p = []
                    for sc in src_palette:
                        c = [op(dc[i], sc[i]) for i in range(3)]
                        if dst.get_masks()[3]:
                            c.append(dc[3])
                        else:
                            c.append(255)
                        c = dst.unmap_rgb(dst.map_rgb(c))
                        p.append(c)
                    dst.fill(dst_color)
                    dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name))
                    self._assert_surface(
                        dst,
                        p,
                        (
                            ", op: %s, src bpp: %i"
                            ", src flags: %i"
                            % (blend_name, src.get_bitsize(), src.get_flags())
                        ),
                    )

        src = self._make_src_surface(32)
        masks = src.get_masks()
        dst = pygame.Surface(
            src.get_size(), 0, 32, [masks[2], masks[1], masks[0], masks[3]]
        )
        for blend_name, dst_color, op in blend:
            p = []
            for src_color in self._test_palette:
                c = [op(dst_color[i], src_color[i]) for i in range(3)]
                c.append(255)
                p.append(tuple(c))
            dst.fill(dst_color)
            dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name))
            self._assert_surface(dst, p, f", {blend_name}")

        # Blend blits are special cased for 32 to 32 bit surfaces.
        #
        # Confirm that it works when the rgb bytes are not the
        # least significant bytes.
        pat = self._make_src_surface(32)
        masks = pat.get_masks()
        if min(masks) == 0xFF000000:
            masks = [m >> 8 for m in masks]
        else:
            masks = [m << 8 for m in masks]
        src = pygame.Surface(pat.get_size(), 0, 32, masks)
        self._fill_surface(src)
        dst = pygame.Surface(src.get_size(), 0, 32, masks)
        for blend_name, dst_color, op in blend:
            p = []
            for src_color in self._test_palette:
                c = [op(dst_color[i], src_color[i]) for i in range(3)]
                c.append(255)
                p.append(tuple(c))
            dst.fill(dst_color)
            dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name))
            self._assert_surface(dst, p, f", {blend_name}")

    def test_blit_blend_rgba(self):
        sources = [
            self._make_src_surface(8),
            self._make_src_surface(16),
            self._make_src_surface(16, srcalpha=True),
            self._make_src_surface(24),
            self._make_src_surface(32),
            self._make_src_surface(32, srcalpha=True),
        ]
        destinations = [
            self._make_surface(8),
            self._make_surface(16),
            self._make_surface(16, srcalpha=True),
            self._make_surface(24),
            self._make_surface(32),
            self._make_surface(32, srcalpha=True),
        ]
        blend = [
            ("BLEND_RGBA_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)),
            ("BLEND_RGBA_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)),
            ("BLEND_RGBA_MULT", (0, 7, 100, 255), lambda a, b: ((a * b) + 255) >> 8),
            ("BLEND_RGBA_MIN", (0, 255, 0, 255), min),
            ("BLEND_RGBA_MAX", (0, 255, 0, 255), max),
        ]

        for src in sources:
            src_palette = [src.unmap_rgb(src.map_rgb(c)) for c in self._test_palette]
            for dst in destinations:
                for blend_name, dst_color, op in blend:
                    dc = dst.unmap_rgb(dst.map_rgb(dst_color))
                    p = []
                    for sc in src_palette:
                        c = [op(dc[i], sc[i]) for i in range(4)]
                        if not dst.get_masks()[3]:
                            c[3] = 255
                        c = dst.unmap_rgb(dst.map_rgb(c))
                        p.append(c)
                    dst.fill(dst_color)
                    dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name))
                    self._assert_surface(
                        dst,
                        p,
                        (
                            ", op: %s, src bpp: %i"
                            ", src flags: %i"
                            % (blend_name, src.get_bitsize(), src.get_flags())
                        ),
                    )

        # Blend blits are special cased for 32 to 32 bit surfaces
        # with per-pixel alpha.
        #
        # Confirm the general case is used instead when the formats differ.
        src = self._make_src_surface(32, srcalpha=True)
        masks = src.get_masks()
        dst = pygame.Surface(
            src.get_size(), SRCALPHA, 32, (masks[2], masks[1], masks[0], masks[3])
        )
        for blend_name, dst_color, op in blend:
            p = [
                tuple(op(dst_color[i], src_color[i]) for i in range(4))
                for src_color in self._test_palette
            ]
            dst.fill(dst_color)
            dst.blit(src, (0, 0), special_flags=getattr(pygame, blend_name))
            self._assert_surface(dst, p, f", {blend_name}")

        # Confirm this special case handles subsurfaces.
        src = pygame.Surface((8, 10), SRCALPHA, 32)
        dst = pygame.Surface((8, 10), SRCALPHA, 32)
        tst = pygame.Surface((8, 10), SRCALPHA, 32)
        src.fill((1, 2, 3, 4))
        dst.fill((40, 30, 20, 10))
        subsrc = src.subsurface((2, 3, 4, 4))
        subdst = dst.subsurface((2, 3, 4, 4))
        subdst.blit(subsrc, (0, 0), special_flags=BLEND_RGBA_ADD)
        tst.fill((40, 30, 20, 10))
        tst.fill((41, 32, 23, 14), (2, 3, 4, 4))
        for x in range(8):
            for y in range(10):
                self.assertEqual(
                    dst.get_at((x, y)),
                    tst.get_at((x, y)),
                    "%s != %s at (%i, %i)"
                    % (dst.get_at((x, y)), tst.get_at((x, y)), x, y),
                )

    def test_blit_blend_premultiplied(self):
        def test_premul_surf(
            src_col,
            dst_col,
            src_size=(16, 16),
            dst_size=(16, 16),
            src_bit_depth=32,
            dst_bit_depth=32,
            src_has_alpha=True,
            dst_has_alpha=True,
        ):
            if src_bit_depth == 8:
                src = pygame.Surface(src_size, 0, src_bit_depth)
                palette = [src_col, dst_col]
                src.set_palette(palette)
                src.fill(palette[0])
            elif src_has_alpha:
                src = pygame.Surface(src_size, SRCALPHA, src_bit_depth)
                src.fill(src_col)
            else:
                src = pygame.Surface(src_size, 0, src_bit_depth)
                src.fill(src_col)

            if dst_bit_depth == 8:
                dst = pygame.Surface(dst_size, 0, dst_bit_depth)
                palette = [src_col, dst_col]
                dst.set_palette(palette)
                dst.fill(palette[1])
            elif dst_has_alpha:
                dst = pygame.Surface(dst_size, SRCALPHA, dst_bit_depth)
                dst.fill(dst_col)
            else:
                dst = pygame.Surface(dst_size, 0, dst_bit_depth)
                dst.fill(dst_col)

            dst.blit(src, (0, 0), special_flags=BLEND_PREMULTIPLIED)

            actual_col = dst.get_at(
                (int(float(src_size[0] / 2.0)), int(float(src_size[0] / 2.0)))
            )

            # This is the blend pre-multiplied formula
            if src_col.a == 0:
                expected_col = dst_col
            elif src_col.a == 255:
                expected_col = src_col
            else:
                # sC + dC - (((dC + 1) * sA >> 8)
                expected_col = pygame.Color(
                    (src_col.r + dst_col.r - ((dst_col.r + 1) * src_col.a >> 8)),
                    (src_col.g + dst_col.g - ((dst_col.g + 1) * src_col.a >> 8)),
                    (src_col.b + dst_col.b - ((dst_col.b + 1) * src_col.a >> 8)),
                    (src_col.a + dst_col.a - ((dst_col.a + 1) * src_col.a >> 8)),
                )
            if not dst_has_alpha:
                expected_col.a = 255

            return (expected_col, actual_col)

        # # Colour Tests
        self.assertEqual(
            *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(40, 20, 0, 51))
        )

        self.assertEqual(
            *test_premul_surf(pygame.Color(0, 0, 0, 0), pygame.Color(40, 20, 0, 51))
        )

        self.assertEqual(
            *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(0, 0, 0, 0))
        )

        self.assertEqual(
            *test_premul_surf(pygame.Color(0, 0, 0, 0), pygame.Color(0, 0, 0, 0))
        )

        self.assertEqual(
            *test_premul_surf(pygame.Color(2, 2, 2, 2), pygame.Color(40, 20, 0, 51))
        )

        self.assertEqual(
            *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(2, 2, 2, 2))
        )

        self.assertEqual(
            *test_premul_surf(pygame.Color(2, 2, 2, 2), pygame.Color(2, 2, 2, 2))
        )

        self.assertEqual(
            *test_premul_surf(pygame.Color(9, 9, 9, 9), pygame.Color(40, 20, 0, 51))
        )

        self.assertEqual(
            *test_premul_surf(pygame.Color(40, 20, 0, 51), pygame.Color(9, 9, 9, 9))
        )

        self.assertEqual(
            *test_premul_surf(pygame.Color(9, 9, 9, 9), pygame.Color(9, 9, 9, 9))
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(127, 127, 127, 127), pygame.Color(40, 20, 0, 51)
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(40, 20, 0, 51), pygame.Color(127, 127, 127, 127)
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(127, 127, 127, 127), pygame.Color(127, 127, 127, 127)
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(200, 200, 200, 200), pygame.Color(40, 20, 0, 51)
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(40, 20, 0, 51), pygame.Color(200, 200, 200, 200)
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(200, 200, 200, 200), pygame.Color(200, 200, 200, 200)
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(255, 255, 255, 255), pygame.Color(40, 20, 0, 51)
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(40, 20, 0, 51), pygame.Color(255, 255, 255, 255)
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(255, 255, 255, 255), pygame.Color(255, 255, 255, 255)
            )
        )

        # Surface format tests
        self.assertRaises(
            IndexError,
            test_premul_surf,
            pygame.Color(255, 255, 255, 255),
            pygame.Color(255, 255, 255, 255),
            src_size=(0, 0),
            dst_size=(0, 0),
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(40, 20, 0, 51),
                pygame.Color(30, 20, 0, 51),
                src_size=(4, 4),
                dst_size=(9, 9),
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 51),
                pygame.Color(40, 20, 0, 51),
                src_size=(17, 67),
                dst_size=(69, 69),
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 255),
                pygame.Color(40, 20, 0, 51),
                src_size=(17, 67),
                dst_size=(69, 69),
                src_has_alpha=True,
            )
        )
        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 51),
                pygame.Color(40, 20, 0, 255),
                src_size=(17, 67),
                dst_size=(69, 69),
                dst_has_alpha=False,
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 255),
                pygame.Color(40, 20, 0, 255),
                src_size=(17, 67),
                dst_size=(69, 69),
                src_has_alpha=False,
                dst_has_alpha=False,
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 255),
                pygame.Color(40, 20, 0, 255),
                src_size=(17, 67),
                dst_size=(69, 69),
                dst_bit_depth=24,
                src_has_alpha=True,
                dst_has_alpha=False,
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 255),
                pygame.Color(40, 20, 0, 255),
                src_size=(17, 67),
                dst_size=(69, 69),
                src_bit_depth=24,
                src_has_alpha=False,
                dst_has_alpha=True,
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 255),
                pygame.Color(40, 20, 0, 255),
                src_size=(17, 67),
                dst_size=(69, 69),
                src_bit_depth=24,
                dst_bit_depth=24,
                src_has_alpha=False,
                dst_has_alpha=False,
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 255),
                pygame.Color(40, 20, 0, 255),
                src_size=(17, 67),
                dst_size=(69, 69),
                src_bit_depth=8,
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 255),
                pygame.Color(40, 20, 0, 255),
                src_size=(17, 67),
                dst_size=(69, 69),
                dst_bit_depth=8,
            )
        )

        self.assertEqual(
            *test_premul_surf(
                pygame.Color(30, 20, 0, 255),
                pygame.Color(40, 20, 0, 255),
                src_size=(17, 67),
                dst_size=(69, 69),
                src_bit_depth=8,
                dst_bit_depth=8,
            )
        )

    def test_blit_blend_premultiplied_pixelformats(self):
        # first create two BGRA pixel format surfaces using
        # tobytes & frombytes
        argb_surf_a = pygame.Surface((64, 64), flags=pygame.SRCALPHA)
        byte_surf_a = pygame.image.tobytes(argb_surf_a, "ARGB")
        bgra_surf_a = pygame.image.frombytes(byte_surf_a, (64, 64), "ARGB")

        argb_surf_b = pygame.Surface((64, 64), flags=pygame.SRCALPHA)
        byte_surf_b = pygame.image.tobytes(argb_surf_b, "ARGB")
        bgra_surf_b = pygame.image.frombytes(byte_surf_b, (64, 64), "ARGB")

        argb_surf_a.fill((64, 0, 0, 128))
        self.assertEqual(argb_surf_a.get_at((0, 0)), pygame.Color(64, 0, 0, 128))

        # 128 green, 128 blue at 50% alpha, premultiplied
        argb_surf_b.fill((0, 64, 64, 128))
        self.assertEqual(argb_surf_b.get_at((0, 0)), pygame.Color(0, 64, 64, 128))

        argb_surf_a.blit(argb_surf_b, (0, 0), special_flags=pygame.BLEND_PREMULTIPLIED)

        self.assertEqual(argb_surf_a.get_at((0, 0)), pygame.Color(32, 64, 64, 192))

        bgra_surf_a.fill((64, 0, 0, 128))  # 128 red at 50% alpha
        self.assertEqual(bgra_surf_a.get_at((0, 0)), pygame.Color(64, 0, 0, 128))

        # 128 green, 128 blue at 50% alpha, premultiplied
        bgra_surf_b.fill((0, 64, 64, 128))
        self.assertEqual(bgra_surf_b.get_at((0, 0)), pygame.Color(0, 64, 64, 128))

        bgra_surf_a.blit(bgra_surf_b, (0, 0), special_flags=pygame.BLEND_PREMULTIPLIED)

        self.assertEqual(bgra_surf_a.get_at((0, 0)), pygame.Color(32, 64, 64, 192))

    def test_blit_blend_big_rect(self):
        """test that an oversized rect works ok."""
        color = (1, 2, 3, 255)
        area = (1, 1, 30, 30)
        s1 = pygame.Surface((4, 4), 0, 32)
        r = s1.fill(special_flags=pygame.BLEND_ADD, color=color, rect=area)

        self.assertEqual(pygame.Rect((1, 1, 3, 3)), r)
        self.assertEqual(s1.get_at((0, 0)), (0, 0, 0, 255))
        self.assertEqual(s1.get_at((1, 1)), color)

        black = pygame.Color("black")
        red = pygame.Color("red")
        self.assertNotEqual(black, red)

        surf = pygame.Surface((10, 10), 0, 32)
        surf.fill(black)
        subsurf = surf.subsurface(pygame.Rect(0, 1, 10, 8))
        self.assertEqual(surf.get_at((0, 0)), black)
        self.assertEqual(surf.get_at((0, 9)), black)

        subsurf.fill(red, (0, -1, 10, 1), pygame.BLEND_RGB_ADD)
        self.assertEqual(surf.get_at((0, 0)), black)
        self.assertEqual(surf.get_at((0, 9)), black)

        subsurf.fill(red, (0, 8, 10, 1), pygame.BLEND_RGB_ADD)
        self.assertEqual(surf.get_at((0, 0)), black)
        self.assertEqual(surf.get_at((0, 9)), black)

    def test_GET_PIXELVALS(self):
        # surface.h GET_PIXELVALS bug regarding whether of not
        # a surface has per-pixel alpha. Looking at the Amask
        # is not enough. The surface's SRCALPHA flag must also
        # be considered. Fix rev. 1923.
        src = self._make_surface(32, srcalpha=True)
        src.fill((0, 0, 0, 128))
        src.set_alpha(None)  # Clear SRCALPHA flag.
        dst = self._make_surface(32, srcalpha=True)
        dst.blit(src, (0, 0), special_flags=BLEND_RGBA_ADD)
        self.assertEqual(dst.get_at((0, 0)), (0, 0, 0, 255))

    def test_fill_blend(self):
        destinations = [
            self._make_surface(8),
            self._make_surface(16),
            self._make_surface(16, srcalpha=True),
            self._make_surface(24),
            self._make_surface(32),
            self._make_surface(32, srcalpha=True),
        ]
        blend = [
            ("BLEND_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)),
            ("BLEND_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)),
            ("BLEND_MULT", (0, 7, 100, 255), lambda a, b: ((a * b) + 255) >> 8),
            ("BLEND_MIN", (0, 255, 0, 255), min),
            ("BLEND_MAX", (0, 255, 0, 255), max),
        ]

        for dst in destinations:
            dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) for c in self._test_palette]
            for blend_name, fill_color, op in blend:
                fc = dst.unmap_rgb(dst.map_rgb(fill_color))
                self._fill_surface(dst)
                p = []
                for dc in dst_palette:
                    c = [op(dc[i], fc[i]) for i in range(3)]
                    if dst.get_masks()[3]:
                        c.append(dc[3])
                    else:
                        c.append(255)
                    c = dst.unmap_rgb(dst.map_rgb(c))
                    p.append(c)
                dst.fill(fill_color, special_flags=getattr(pygame, blend_name))
                self._assert_surface(dst, p, f", {blend_name}")

    def test_fill_blend_rgba(self):
        destinations = [
            self._make_surface(8),
            self._make_surface(16),
            self._make_surface(16, srcalpha=True),
            self._make_surface(24),
            self._make_surface(32),
            self._make_surface(32, srcalpha=True),
        ]
        blend = [
            ("BLEND_RGBA_ADD", (0, 25, 100, 255), lambda a, b: min(a + b, 255)),
            ("BLEND_RGBA_SUB", (0, 25, 100, 255), lambda a, b: max(a - b, 0)),
            ("BLEND_RGBA_MULT", (0, 7, 100, 255), lambda a, b: ((a * b) + 255) >> 8),
            ("BLEND_RGBA_MIN", (0, 255, 0, 255), min),
            ("BLEND_RGBA_MAX", (0, 255, 0, 255), max),
        ]

        for dst in destinations:
            dst_palette = [dst.unmap_rgb(dst.map_rgb(c)) for c in self._test_palette]
            for blend_name, fill_color, op in blend:
                fc = dst.unmap_rgb(dst.map_rgb(fill_color))
                self._fill_surface(dst)
                p = []
                for dc in dst_palette:
                    c = [op(dc[i], fc[i]) for i in range(4)]
                    if not dst.get_masks()[3]:
                        c[3] = 255
                    c = dst.unmap_rgb(dst.map_rgb(c))
                    p.append(c)
                dst.fill(fill_color, special_flags=getattr(pygame, blend_name))
                self._assert_surface(dst, p, f", {blend_name}")

    def test_surface_premul_alpha(self):
        """Ensure that .premul_alpha() works correctly"""

        # basic functionality at valid bit depths - 32, 16 & 8
        s1 = pygame.Surface((100, 100), pygame.SRCALPHA, 32)
        s1.fill(pygame.Color(255, 255, 255, 100))
        s1_alpha = s1.premul_alpha()
        self.assertEqual(s1_alpha.get_at((50, 50)), pygame.Color(100, 100, 100, 100))

        # 16-bit colour has less precision
        s2 = pygame.Surface((100, 100), pygame.SRCALPHA, 16)
        s2.fill(
            pygame.Color(
                int(15 / 15 * 255),
                int(15 / 15 * 255),
                int(15 / 15 * 255),
                int(10 / 15 * 255),
            )
        )
        s2_alpha = s2.premul_alpha()
        self.assertEqual(
            s2_alpha.get_at((50, 50)),
            pygame.Color(
                int(10 / 15 * 255),
                int(10 / 15 * 255),
                int(10 / 15 * 255),
                int(10 / 15 * 255),
            ),
        )

        # invalid surface - we need alpha to pre-multiply
        invalid_surf = pygame.Surface((100, 100), 0, 32)
        invalid_surf.fill(pygame.Color(255, 255, 255, 100))
        with self.assertRaises(ValueError):
            invalid_surf.premul_alpha()

        # churn a bunch of values
        test_colors = [
            (200, 30, 74),
            (76, 83, 24),
            (184, 21, 6),
            (74, 4, 74),
            (76, 83, 24),
            (184, 21, 234),
            (160, 30, 74),
            (96, 147, 204),
            (198, 201, 60),
            (132, 89, 74),
            (245, 9, 224),
            (184, 112, 6),
        ]

        for r, g, b in test_colors:
            for a in range(255):
                with self.subTest(r=r, g=g, b=b, a=a):
                    surf = pygame.Surface((10, 10), pygame.SRCALPHA, 32)
                    surf.fill(pygame.Color(r, g, b, a))
                    surf = surf.premul_alpha()
                    self.assertEqual(
                        surf.get_at((5, 5)),
                        Color(
                            ((r + 1) * a) >> 8,
                            ((g + 1) * a) >> 8,
                            ((b + 1) * a) >> 8,
                            a,
                        ),
                    )


class SurfaceSelfBlitTest(unittest.TestCase):
    """Blit to self tests.

    This test case is in response to https://github.com/pygame-community/pygame-ce/issues/34
    """

    def setUp(self):
        # Needed for 8 bits-per-pixel color palette surface tests.
        pygame.display.init()

    def tearDown(self):
        pygame.display.quit()

    _test_palette = [(0, 0, 0, 255), (255, 0, 0, 0), (0, 255, 0, 255)]
    surf_size = (9, 6)

    def _fill_surface(self, surf, palette=None):
        if palette is None:
            palette = self._test_palette
        surf.fill(palette[1])
        surf.fill(palette[2], (1, 2, 1, 2))

    def _make_surface(self, bitsize, srcalpha=False, palette=None):
        if palette is None:
            palette = self._test_palette
        flags = 0
        if srcalpha:
            flags |= SRCALPHA
        surf = pygame.Surface(self.surf_size, flags, bitsize)
        if bitsize == 8:
            surf.set_palette([c[:3] for c in palette])
        self._fill_surface(surf, palette)
        return surf

    def _assert_same(self, a, b):
        w, h = a.get_size()
        for x in range(w):
            for y in range(h):
                self.assertEqual(
                    a.get_at((x, y)),
                    b.get_at((x, y)),
                    (
                        "%s != %s, bpp: %i"
                        % (a.get_at((x, y)), b.get_at((x, y)), a.get_bitsize())
                    ),
                )

    def test_overlap_check(self):
        # Ensure overlapping blits are properly detected. There are two
        # places where this is done, within SoftBlitPyGame() in alphablit.c
        # and PySurface_Blit() in surface.c. SoftBlitPyGame should catch the
        # per-pixel alpha surface, PySurface_Blit the colorkey and blanket
        # alpha surface. per-pixel alpha and blanket alpha self blits are
        # not properly handled by SDL 1.2.13, so Pygame does them.
        bgc = (0, 0, 0, 255)
        rectc_left = (128, 64, 32, 255)
        rectc_right = (255, 255, 255, 255)
        colors = [(255, 255, 255, 255), (128, 64, 32, 255)]
        overlaps = [
            (0, 0, 1, 0, (50, 0)),
            (0, 0, 49, 1, (98, 2)),
            (0, 0, 49, 49, (98, 98)),
            (49, 0, 0, 1, (0, 2)),
            (49, 0, 0, 49, (0, 98)),
        ]
        surfs = [pygame.Surface((100, 100), SRCALPHA, 32)]
        surf = pygame.Surface((100, 100), 0, 32)
        surf.set_alpha(255)
        surfs.append(surf)
        surf = pygame.Surface((100, 100), 0, 32)
        surf.set_colorkey((0, 1, 0))
        surfs.append(surf)
        for surf in surfs:
            for s_x, s_y, d_x, d_y, test_posn in overlaps:
                surf.fill(bgc)
                surf.fill(rectc_right, (25, 0, 25, 50))
                surf.fill(rectc_left, (0, 0, 25, 50))
                surf.blit(surf, (d_x, d_y), (s_x, s_y, 50, 50))
                self.assertEqual(surf.get_at(test_posn), rectc_right)

    # https://github.com/pygame-community/pygame-ce/issues/374, final comment by illume
    @unittest.skipIf("ppc64le" in platform.uname(), "known ppc64le issue")
    def test_colorkey(self):
        # Check a workaround for an SDL 1.2.13 surface self-blit problem
        # https://github.com/pygame-community/pygame-ce/issues/34
        pygame.display.set_mode((100, 50))  # Needed for 8bit surface
        bitsizes = [8, 16, 24, 32]
        for bitsize in bitsizes:
            surf = self._make_surface(bitsize)
            surf.set_colorkey(self._test_palette[1])
            surf.blit(surf, (3, 0))
            p = []
            for c in self._test_palette:
                c = surf.unmap_rgb(surf.map_rgb(c))
                p.append(c)
            p[1] = (p[1][0], p[1][1], p[1][2], 0)
            tmp = self._make_surface(32, srcalpha=True, palette=p)
            tmp.blit(tmp, (3, 0))
            tmp.set_alpha(None)
            comp = self._make_surface(bitsize)
            comp.blit(tmp, (0, 0))
            self._assert_same(surf, comp)

    # https://github.com/pygame-community/pygame-ce/issues/374 final comment by illume
    @unittest.skipIf("ppc64le" in platform.uname(), "known ppc64le issue")
    def test_blanket_alpha(self):
        # Check a workaround for an SDL 1.2.13 surface self-blit problem
        # https://github.com/pygame-community/pygame-ce/issues/34
        pygame.display.set_mode((100, 50))  # Needed for 8bit surface
        bitsizes = [8, 16, 24, 32]
        for bitsize in bitsizes:
            surf = self._make_surface(bitsize)
            surf.set_alpha(128)
            surf.blit(surf, (3, 0))
            p = []
            for c in self._test_palette:
                c = surf.unmap_rgb(surf.map_rgb(c))
                p.append((c[0], c[1], c[2], 128))
            tmp = self._make_surface(32, srcalpha=True, palette=p)
            tmp.blit(tmp, (3, 0))
            tmp.set_alpha(None)
            comp = self._make_surface(bitsize)
            comp.blit(tmp, (0, 0))
            self._assert_same(surf, comp)

    def test_pixel_alpha(self):
        bitsizes = [16, 32]
        for bitsize in bitsizes:
            surf = self._make_surface(bitsize, srcalpha=True)
            comp = self._make_surface(bitsize, srcalpha=True)
            comp.blit(surf, (3, 0))
            surf.blit(surf, (3, 0))
            self._assert_same(surf, comp)

    def test_blend(self):
        bitsizes = [8, 16, 24, 32]
        blends = ["BLEND_ADD", "BLEND_SUB", "BLEND_MULT", "BLEND_MIN", "BLEND_MAX"]
        for bitsize in bitsizes:
            surf = self._make_surface(bitsize)
            comp = self._make_surface(bitsize)
            for blend in blends:
                self._fill_surface(surf)
                self._fill_surface(comp)
                comp.blit(surf, (3, 0), special_flags=getattr(pygame, blend))
                surf.blit(surf, (3, 0), special_flags=getattr(pygame, blend))
                self._assert_same(surf, comp)

    def test_blend_rgba(self):
        bitsizes = [16, 32]
        blends = [
            "BLEND_RGBA_ADD",
            "BLEND_RGBA_SUB",
            "BLEND_RGBA_MULT",
            "BLEND_RGBA_MIN",
            "BLEND_RGBA_MAX",
        ]
        for bitsize in bitsizes:
            surf = self._make_surface(bitsize, srcalpha=True)
            comp = self._make_surface(bitsize, srcalpha=True)
            for blend in blends:
                self._fill_surface(surf)
                self._fill_surface(comp)
                comp.blit(surf, (3, 0), special_flags=getattr(pygame, blend))
                surf.blit(surf, (3, 0), special_flags=getattr(pygame, blend))
                self._assert_same(surf, comp)

    def test_subsurface(self):
        # Blitting a surface to its subsurface is allowed.
        surf = self._make_surface(32, srcalpha=True)
        comp = surf.copy()
        comp.blit(surf, (3, 0))
        sub = surf.subsurface((3, 0, 6, 6))
        sub.blit(surf, (0, 0))
        del sub
        self._assert_same(surf, comp)

        # Blitting a subsurface to its owner is forbidden because of
        # lock conflicts. This limitation allows the overlap check
        # in PySurface_Blit of alphablit.c to be simplified.
        def do_blit(d, s):
            d.blit(s, (0, 0))

        sub = surf.subsurface((1, 1, 2, 2))
        self.assertRaises(pygame.error, do_blit, surf, sub)

    def test_copy_alpha(self):
        """pygame-ce issue 487: alpha of surface copy with SRCALPHA is set to 0."""
        surf = pygame.Surface((16, 16), pygame.SRCALPHA, 32)
        self.assertEqual(surf.get_alpha(), 255)
        surf2 = surf.copy()
        self.assertEqual(surf2.get_alpha(), 255)


class SurfaceFillTest(unittest.TestCase):
    def setUp(self):
        pygame.display.init()

    def tearDown(self):
        pygame.display.quit()

    def test_fill(self):
        screen = pygame.display.set_mode((640, 480))

        # Green and blue test pattern
        screen.fill((0, 255, 0), (0, 0, 320, 240))
        screen.fill((0, 255, 0), (320, 240, 320, 240))
        screen.fill((0, 0, 255), (320, 0, 320, 240))
        screen.fill((0, 0, 255), (0, 240, 320, 240))

        # Now apply a clip rect, such that only the left side of the
        # screen should be effected by blit operations.
        screen.set_clip((0, 0, 320, 480))

        # Test fills with each special flag, and additionally without any.
        screen.fill((255, 0, 0, 127), (160, 0, 320, 30), 0)
        screen.fill((255, 0, 0, 127), (160, 30, 320, 30), pygame.BLEND_ADD)
        screen.fill((0, 127, 127, 127), (160, 60, 320, 30), pygame.BLEND_SUB)
        screen.fill((0, 63, 63, 127), (160, 90, 320, 30), pygame.BLEND_MULT)
        screen.fill((0, 127, 127, 127), (160, 120, 320, 30), pygame.BLEND_MIN)
        screen.fill((127, 0, 0, 127), (160, 150, 320, 30), pygame.BLEND_MAX)
        screen.fill((255, 0, 0, 127), (160, 180, 320, 30), pygame.BLEND_RGBA_ADD)
        screen.fill((0, 127, 127, 127), (160, 210, 320, 30), pygame.BLEND_RGBA_SUB)
        screen.fill((0, 63, 63, 127), (160, 240, 320, 30), pygame.BLEND_RGBA_MULT)
        screen.fill((0, 127, 127, 127), (160, 270, 320, 30), pygame.BLEND_RGBA_MIN)
        screen.fill((127, 0, 0, 127), (160, 300, 320, 30), pygame.BLEND_RGBA_MAX)
        screen.fill((255, 0, 0, 127), (160, 330, 320, 30), pygame.BLEND_RGB_ADD)
        screen.fill((0, 127, 127, 127), (160, 360, 320, 30), pygame.BLEND_RGB_SUB)
        screen.fill((0, 63, 63, 127), (160, 390, 320, 30), pygame.BLEND_RGB_MULT)
        screen.fill((0, 127, 127, 127), (160, 420, 320, 30), pygame.BLEND_RGB_MIN)
        screen.fill((255, 0, 0, 127), (160, 450, 320, 30), pygame.BLEND_RGB_MAX)

        # Update the display so we can see the results
        pygame.display.flip()

        # Compare colors on both sides of window
        for y in range(5, 480, 10):
            self.assertEqual(screen.get_at((10, y)), screen.get_at((330, 480 - y)))


if __name__ == "__main__":
    unittest.main()
