示例#1
0
    def load(self):

        self.texto = BMP(0, 0)
        try:
            self.texto.load(self.nombre)

        except:
            self.texto = None
def CollectBmpPeerUp(sock, verbose=False):
    """Collect a BMP Peer Up message.

  Args:
    sock: socket from which to read.
    verbose: be chatty, or not.

  Returns:
    nothing

  Raises:
    ValueError: an unexpected value was found in the message
  """

    indent_str = indent.IndentLevel(indent.BMP_CONTENT_INDENT)
    print_msg = []

    # collect a per peer header
    #
    per_peer_header = CollectBytes(sock, BMP.PER_PEER_HEADER_LEN_V3)
    peer_flags, msg_text = BMP.ParseBmpPerPeerHeaderV3(per_peer_header,
                                                       verbose=verbose)
    print_msg += "".join(msg_text)

    # collect local address, local and remote ports
    #
    peer_up_msg = CollectBytes(sock, BMP.PEER_UP_LEN)
    if verbose:
        msg_text = BMP.ParseBmpPeerUp(peer_up_msg, peer_flags, verbose=verbose)
        print_msg += "".join(msg_text)

    # sent BGP OPEN message
    #
    sent_header = CollectBytes(sock, BGP.HEADER_LEN)
    length, msg_type, hdr_text = BGP.ParseBgpHeader(sent_header,
                                                    verbose=verbose)
    assert msg_type == BGP.OPEN
    print_msg += "".join(hdr_text)
    sent_open = CollectBytes(sock, length)
    sent_text = BGP.ParseBgpOpen(sent_open, length)
    print_msg += "".join(sent_text)

    # received BGP OPEN message
    #
    recv_header = CollectBytes(sock, BGP.HEADER_LEN)
    length, msg_type, hdr_text = BGP.ParseBgpHeader(recv_header,
                                                    verbose=verbose)
    assert msg_type == BGP.OPEN
    print_msg += "".join(hdr_text)
    recv_open = CollectBytes(sock, length)
    recv_text = BGP.ParseBgpOpen(recv_open, length)
    print_msg += "".join(recv_text)

    # Return list of strings representing collected message.
    #
    return print_msg
示例#3
0
def fnttobmp(fnt,pal,file=None):
	b = BMP.BMP()
	b.load_data(fnt.letters[0],pal)
	for l in fnt.letters[1:]:
		for y,yd in enumerate(l):
			b.image[y].extend(yd)
	b.width = len(b.image[0])
	if file == None:
		return b
	b.save_file(file)
def CollectBmpPeerDown(sock, verbose=False):
    """Collect a BMP Peer Down message.

  Args:
    sock: socket from which to read.
    verbose: be chatty, or not.

  Returns:
    nothing

  Raises:
    ValueError: an unexpected value was found in the message
  """

    indent_str = indent.IndentLevel(indent.BMP_CONTENT_INDENT)
    print_msg = []

    reason_code = CollectBytes(sock, 1)[0]
    if reason_code in BMP.PEER_DOWN_REASON_STR:
        print_msg.append("%s%s\n" %
                         (indent_str, BMP.PEER_DOWN_REASON_STR[reason_code]))

        # If the BMP message contains a BGP NOTIFICATION message, collect
        # and parse it.
        #
        if BMP.PeerDownHasBgpNotification(reason_code):

            # Collect and parse the BGP message header
            #
            header = CollectBytes(sock, BGP.HEADER_LEN)
            length, msg_type, msg_text = BGP.ParseBgpHeader(header,
                                                            verbose=verbose)
            assert msg_type == BGP.NOTIFICATION
            print_msg.append("".join(msg_text))

            # collect and parse the BGP message body
            #
            notification = CollectBytes(sock, length)
            msg_text = BGP.ParseBgpNotification(notification,
                                                length,
                                                verbose=verbose)
            print_msg.append("".join(msg_text))

    elif DEBUG_FLAG:
        raise ValueError("Unknown BMP Peer Down reason %d" % reason_code)
    else:
        print_msg.append("Unknown BMP Peer Down reason %d\n" % reason_code)

    # Return list of strings representing collected message.
    #
    return print_msg
示例#5
0
文件: Tilesets.py 项目: iquare/PyMS
    def export_graphics(self, tiletype, path, ids):
        bmp = BMP.BMP()
        bmp.palette = list(self.wpe.palette)
        tiles_wide = 0
        tile_width = 0
        tiles_high = 0
        tile_height = 0

        def calc_dims(tiles):
            for f in xrange(int(math.sqrt(tiles)), 0, -1):
                if not tiles % f:
                    return (tiles / f, f)
            return (tiles, 1)

        if tiletype == TILETYPE_GROUP:
            tiles_wide, tiles_high = 16, len(ids)
            tile_width, tile_height = 32, 32
            tiletype = TILETYPE_MEGA
            groups = ids
            ids = []
            for id in groups:
                ids.extend(self.cv5.groups[id][13])
        elif tiletype == TILETYPE_MEGA:
            tiles_wide, tiles_high = calc_dims(len(ids))
            tile_width, tile_height = 32, 32
        elif tiletype == TILETYPE_MINI:
            tiles_wide, tiles_high = calc_dims(len(ids))
        bmp.width = tile_width * tiles_wide
        bmp.height = tile_height * tiles_high
        bmp.image = [[] for _ in range(bmp.height)]
        if tiletype == TILETYPE_MEGA:
            for mega_n, mega_id in enumerate(ids):
                mega_y = (mega_n / tiles_wide) * tile_height
                for mini_n in xrange(16):
                    mini_y = (mini_n / 4) * 8
                    mini_id, flipped = self.vx4.graphics[mega_id][mini_n]
                    image = self.vr4.images[mini_id]
                    for row_y, row in enumerate(image):
                        if flipped:
                            row = reversed(row)
                        bmp.image[mega_y + mini_y + row_y].extend(row)
        elif tiletype == TILETYPE_MINI:
            for mini_y, mini_id in enumerate(ids):
                image = self.vr4.images[mini_id]
                for row_y, row in enumerate(image):
                    bmp.image[mini_y + row_y].extend(row)
        bmp.save_file(path)
示例#6
0
 def decompile_file(self, filepath, palette):
     width = 0
     height = 0
     for layer in self.layers:
         for star in layer.stars:
             width = max(width, star.x + star.image.width)
             height = max(height, star.y + star.image.height)
     full_height = height * len(self.layers)
     image = list([0] * width for _ in range(full_height))
     for l, layer in enumerate(self.layers):
         ly = height * l
         for star in layer.stars:
             for y in range(star.image.height):
                 for x in range(star.image.width):
                     if not image[star.y + y +
                                  ly][star.x +
                                      x] and star.image.pixels[y][x]:
                         image[star.y + y + ly][star.x +
                                                x] = star.image.pixels[y][x]
     bmp = BMP.BMP()
     bmp.load_data(image, palette.palette)
     bmp.save_file(filepath)
示例#7
0
文件: Tilesets.py 项目: iquare/PyMS
    def import_graphics(self, tiletype, bmpfiles, ids=None, options={}):
        if ids:
            ids = list(ids)
        else:
            ids = []
        new_ids = []
        pixels = []
        for path in bmpfiles:
            bmp = BMP.BMP()
            bmp.load_file(path)
            if tiletype == TILETYPE_GROUP and (bmp.width != 512
                                               or bmp.height % 32):
                raise PyMSError(
                    'Interpreting',
                    'The image is not the correct size for tile groups (got %sx%s, expected width to be 512 and height to be a multiple of 32)'
                    % (bmp.width, bmp.height))
            elif tiletype == TILETYPE_MEGA and (bmp.width % 32
                                                or bmp.height % 32):
                raise PyMSError(
                    'Interpreting',
                    'The image is not the correct size for megatiles (got %sx%s, expected width and height to be multiples of 32)'
                    % (bmp.width, bmp.height))
            elif tiletype == TILETYPE_MINI and (bmp.width % 8
                                                or bmp.height % 8):
                raise PyMSError(
                    'Interpreting',
                    'The image is not the correct size for minitiles (got %sx%s, expected width and height to be multiples of 8)'
                    % (bmp.width, bmp.height))
            pixels.extend(bmp.image)
        new_images = []
        mini_lookup = {}
        update_images = []  # (id,image)
        new_megatiles = []
        mega_lookup = {}
        update_megatiles = []  # (id,tile)
        new_groups = []
        update_groups = []  # (id,group)

        minis_w = len(pixels[0]) / 8
        minis_h = len(pixels) / 8
        for iy in xrange(minis_h):
            py = iy * 8
            for ix in xrange(minis_w):
                px = ix * 8
                image = tuple(
                    tuple(pixels[py + oy][px:px + 8]) for oy in xrange(8))
                new_images.append(image)
        image_details = []  # (id,isFlipped)
        new_id = len(self.vr4.images)
        i = 0
        minitiles_reuse_null_with_id = options.get(
            'minitiles_reuse_null_with_id', 0)
        minitiles_reuse_duplicates_old = options.get(
            'minitiles_reuse_duplicates_old', True)
        minitiles_reuse_duplicates_new = options.get(
            'minitiles_reuse_duplicates_new', True)
        minitiles_reuse_duplicates_flipped = options.get(
            'minitiles_reuse_duplicates_flipped', True)
        if tiletype == TILETYPE_MINI and options.get(
                'minitiles_ignore_extra',
                False) and len(new_images) > len(ids):
            new_images = new_images[:len(ids)]
        while i < len(new_images):
            image = new_images[i]
            image_hash = hash(image)
            found = False
            if tiletype != TILETYPE_MINI or not len(ids):
                flipped_hash = hash(tuple(tuple(reversed(r)) for r in image))
                existing_ids = self.vr4.lookup.get(
                    image_hash, []) + self.vr4.lookup.get(flipped_hash, [])
                if len(existing_ids) and (minitiles_reuse_duplicates_old
                                          or minitiles_reuse_null_with_id
                                          in existing_ids):
                    normal_found = image_hash in self.vr4.lookup
                    found = True
                    del new_images[i]
                    image_details.append(
                        (existing_ids[0], int(not normal_found)))
                if not found:
                    existing_ids = mini_lookup.get(
                        image_hash, []) + mini_lookup.get(flipped_hash, [])
                    if len(existing_ids) and (minitiles_reuse_duplicates_new
                                              or minitiles_reuse_null_with_id
                                              in existing_ids):
                        normal_found = image_hash in mini_lookup
                        found = True
                        del new_images[i]
                        image_details.append(
                            (existing_ids[0], int(not normal_found)))
            if not found:
                id = new_id
                if tiletype == TILETYPE_MINI and len(ids):
                    id = ids[0]
                    del ids[0]
                    update_images.append((id, new_images[i]))
                    del new_images[i]
                else:
                    if tiletype == TILETYPE_MINI:
                        new_ids.append(new_id)
                    new_id += 1
                    i += 1
                image_details.append((id, 0))
                if image_hash in mini_lookup:
                    mini_lookup[image_hash].append(id)
                else:
                    mini_lookup[image_hash] = [id]
        minitiles_expand_allowed = options.get('minitiles_expand_allowed',
                                               False)
        if len(new_images) > self.minitiles_remaining():
            if self.vx4.expanded or not minitiles_expand_allowed or (
                    callable(minitiles_expand_allowed)
                    and not minitiles_expand_allowed()):
                raise PyMSError(
                    'Importing',
                    'Import aborted because it exceeded the maximum minitile image count (%d + %d > %d)'
                    % (len(self.vr4.images), len(new_images), VR4.MAX_ID + 1))
            self.vx4.expanded = True
        if tiletype == TILETYPE_GROUP or tiletype == TILETYPE_MEGA:
            megas_w = minis_w / 4
            megas_h = minis_h / 4
            for y in xrange(megas_h):
                for x in xrange(megas_w):
                    minitiles = []
                    for oy in xrange(4):
                        o = (y * 4 + oy) * minis_w + x * 4
                        minitiles.extend(image_details[o:o + 4])
                    new_megatiles.append(tuple(minitiles))
            megatile_ids = []
            new_id = len(self.vx4.graphics)
            i = 0
            megatiles_reuse_null_with_id = options.get(
                'megatiles_reuse_null_with_id', 0)
            megatiles_reuse_duplicates_old = options.get(
                'megatiles_reuse_duplicates_old', True)
            megatiles_reuse_duplicates_new = options.get(
                'megatiles_reuse_duplicates_new', True)
            if tiletype == TILETYPE_MEGA and options.get(
                    'megatiles_ignore_extra',
                    False) and len(new_megatiles) > len(ids):
                new_megatiles = new_megatiles[:len(ids)]
            while i < len(new_megatiles):
                tile_hash = hash(new_megatiles[i])
                found = False
                if tiletype != TILETYPE_MEGA or not len(ids):
                    existing_ids = self.vx4.lookup.get(tile_hash, None)
                    if existing_ids and (megatiles_reuse_duplicates_old
                                         or megatiles_reuse_null_with_id
                                         in existing_ids):
                        del new_megatiles[i]
                        megatile_ids.append(existing_ids[0])
                        found = True
                    if not found:
                        existing_ids = mega_lookup.get(tile_hash, None)
                        if existing_ids and (megatiles_reuse_duplicates_old
                                             or megatiles_reuse_null_with_id
                                             in existing_ids):
                            del new_megatiles[i]
                            megatile_ids.append(existing_ids[0])
                            found = True
                if not found:
                    id = new_id
                    if tiletype == TILETYPE_MEGA and len(ids):
                        id = ids[0]
                        del ids[0]
                        update_megatiles.append((id, new_megatiles[i]))
                        del new_megatiles[i]
                    else:
                        if tiletype == TILETYPE_MEGA:
                            new_ids.append(new_id)
                        new_id += 1
                        i += 1
                    megatile_ids.append(id)
                    if tile_hash in mega_lookup:
                        mega_lookup[tile_hash].append(id)
                    else:
                        mega_lookup[tile_hash] = [id]
            if len(new_megatiles) > self.megatiles_remaining():
                raise PyMSError(
                    'Importing',
                    'Import aborted because it exceeded the maximum megatile count (%d + %d > %d)'
                    %
                    (len(self.vf4.flags), len(new_megatiles), VF4.MAX_ID + 1))
            if tiletype == TILETYPE_GROUP:
                groups = megas_h
                if tiletype == TILETYPE_GROUP and options.get(
                        'groups_ignore_extra', False) and groups > len(ids):
                    groups = len(ids)
                for n in xrange(groups):
                    group = megatile_ids[n * 16:(n + 1) * 16]
                    if len(ids):
                        id = ids[0]
                        del ids[0]
                        update_groups.append((id, group))
                    else:
                        if tiletype == TILETYPE_GROUP:
                            new_ids.append(
                                len(self.cv5.groups) + len(new_groups))
                        new_groups.append(group)
                if len(new_groups) > self.groups_remaining():
                    raise PyMSError(
                        'Importing',
                        'Import aborted because it exceeded the maximum megatile group count (%d + %d > %d)'
                        % (len(
                            self.cv5.groups), len(new_groups), CV5.MAX_ID + 1))
        # Update minitiles
        self.vr4.images.extend(new_images)
        for id, image in update_images:
            self.vr4.set_image(id, image)
        for image_hash in mini_lookup:
            if image_hash in self.vr4.lookup:
                self.vr4.lookup[image_hash].extend(mini_lookup[image_hash])
            else:
                self.vr4.lookup[image_hash] = mini_lookup[image_hash]
        # Update megatiles
        self.vx4.graphics.extend(new_megatiles)
        self.vf4.flags.extend([0] * 16 for _ in xrange(len(new_megatiles)))
        for id, tile in update_megatiles:
            self.vx4.set_tile(id, tile)
        for tile_hash in mega_lookup:
            if tile_hash in self.vx4.lookup:
                self.vx4.lookup[tile_hash].extend(mega_lookup[tile_hash])
            else:
                self.vx4.lookup[tile_hash] = mega_lookup[tile_hash]
        # Update megatile groups
        for group in new_groups:
            self.cv5.groups.append([0] * 13 + [group])
        for id, group in update_groups:
            self.cv5.groups[id][13] = group
        return new_ids
示例#8
0
class Texture(object):

    #obtenemos el nombre
    def __init__(self, filename):

        self.nombre = filename
        self.texto = None

        #cargamos el archivo
        self.load()

#    def read(self):
#        image = open(self.path, "rb")
#
#        # we ignore all the header stuff
#
#        image.seek(2 + 4 + 4)  # skip BM, skip bmp size, skip zeros
#
#        header_size = struct.unpack("=l", image.read(4))[0]  # read header size
#        image.seek(2 + 4 + 4 + 4 + 4)
#
#
#
#        self.width = struct.unpack("=l", image.read(4))[0]  # read width
#        self.height = struct.unpack("=l", image.read(4))[0]  # read width
#        self.pixels = []
#
#        image.seek(header_size)
#        for y in range(self.height):
#
#            self.pixels.append([])
#            for x in range(self.width):
#
#                b = ord(image.read(1))
#                g = ord(image.read(1))
#                r = ord(image.read(1))
#                self.pixels[y].append(color(r,g,b))
#        image.close()

    def load(self):

        self.texto = BMP(0, 0)
        try:
            self.texto.load(self.nombre)

        except:
            self.texto = None
            #colorblanco
    def write(self):
        self.texto.write(self.nombre[:len(self.nombre) - 4] + "text.bmp")

        #texturas
    def isTextured(self):

        return True if self.texto else False
        #color

#
#    def get_color(self, tx, ty, intensity=1):
#
#        x = int(tx * self.width)
#        y = int(ty * self.height)
#
#        try:
#            return bytes(map(lambda b: round(b*intensity) if b*intensity > 0 else 0, self.pixels[y][x]))
#        except:
#            pass

    def getColor(self, tx, ty, intensity=1):

        x = self.texto.width - 1 if ty == 1 else int(ty * self.texto.width)

        y = self.texto.height - 1 if tx == 1 else int(tx * self.texto.height)

        #retornamos
        return bytes(
            map(lambda b: round(b * intensity)
                if b * intensity > 0 else 0, self.texto.framebuffer[y][x]))
示例#9
0
def makeTeddy():
    print("Painting Teddy...")
    image.clear_zbuffer()
    image.loadTextureImage("Teddy/Col", 0, 0)
    x = int(0.220 * WIDTH)
    y = int(0.440 * HEIGHT)
    image.objMaker("Teddy/Teddy", 10, x, y, True, False)


def makeIvysaur():
    print("Painting Ivysaur...")
    image.clear_zbuffer()
    image.loadTextureImage("Ivysaur/Col", 0, 0)
    x = int(0.200 * WIDTH)
    y = int(0.460 * HEIGHT)
    image.objMaker("Ivysaur/Ivy", 13, x, y, True, False)


# image
image = BMP.bmpImage(WIDTH, HEIGHT)
image.setBackground()
makeSword()
makeIvysaur()
makeTV()
makeCat()
makeTeddy()
makeTable()
makeSofa()
image.setLight(WIDTH / 2, HEIGHT)
image.writeImage("out")
示例#10
0
def main(argv):
    global RECORD_SESSION  # pylint: disable-msg=W0603
    global DEBUG_FLAG  # pylint: disable-msg=W0603

    # Process command-line arguments.
    #
    try:
        opts, args = getopt.getopt(argv, "dv24p:f:c:s:", [
            "debug", "verbose", "norfc4893", "rfc4893", "port=", "file=",
            "count=", "skip="
        ])
    except getopt.GetoptError:
        Usage()
        sys.exit(2)

    if args:
        Usage()
        sys.exit(2)

    port = PORT
    count_updates = 0
    skip_updates = 0
    rfc4893_updates = True
    verbose_flag = False
    record_file = ""
    for o, a in opts:
        if o in ("-p", "--port"):
            port = int(a)
        elif o in ("-f", "--file"):
            record_file = a
        elif o in ("-2", "--norfc4893"):
            rfc4893_updates = False
        elif o in ("-4", "--rfc4893"):
            rfc4893_updates = True
        elif o in ("-d", "--debug"):
            DEBUG_FLAG += 1
        elif o in ("-v", "--verbose"):
            verbose_flag = True
        elif o in ("-c", "--count"):
            count_updates = int(a)
        elif o in ("-s", "--skip"):
            skip_updates = int(a)
        else:
            raise ValueError("unhandled option")

    # If recording, open the file for write.
    #
    if port and record_file:
        try:
            RECORD_SESSION = open(record_file, "wb")
        except Exception:
            raise Exception("error opening %s for write" % record_file)
    else:
        RECORD_SESSION = None

    # If port is non-zero open a listening socket, wait for a connection.
    #
    if port != 0:
        listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        listener.bind((HOST, port))
        listener.listen(1)

    # We have either a connection from a BMP sender, or a file from which
    # to read; we want to loop over connections, if we're reading from a
    # file an exit will take place on EOF.
    #
    while True:

        # If port is non-zero, we got a connection; accept it.
        #
        if port != 0:
            conn, addr = listener.accept()
            print "Connection from %s port %d" % (addr[0], addr[1])
        else:
            conn = open(record_file, "rb", 0)
            print "Reading from file %s" % record_file

        # Loop over either the data stream from the socket, or the contents
        # of a file.
        #
        while True:

            msg_text = []

            # Read a BMP header. First get the version number, and use it to
            # figure out the rest of what to do
            #
            temp = CollectBytes(conn, 1)
            bmp_version = temp[0]
            msg_length = 0

            # Process the rest of the header information based on the BMP version
            #
            if bmp_version == 1:
                header = CollectBytes(conn, BMP.HEADER_LEN_V1 - 1)
                msg_type, tmp_text = BMP.ParseBmpHeaderV1(header,
                                                          verbose=verbose_flag)
            elif bmp_version == 3:
                header = CollectBytes(conn, BMP.HEADER_LEN_V3 - 1)
                msg_type, msg_length, tmp_text = BMP.ParseBmpHeaderV3(
                    header, verbose=verbose_flag)
            else:
                print "Version %d out of range" % bmp_version
                sys.exit(3)
            msg_text += "".join(tmp_text)

            # Process the specific type of BMP message
            #
            # Route Monitoring message
            # draft-ietf-grow-bmp-01.txt section 2.1
            # The body of the message is a BGP UPDATE.
            #
            if msg_type == BMP.MSG_TYPE_ROUTE_MONITORING:

                # if version 3, collect a per peer header
                #
                if bmp_version == 3:
                    per_peer_header = CollectBytes(conn,
                                                   BMP.PER_PEER_HEADER_LEN_V3)
                    peer_flags, peer_text = BMP.ParseBmpPerPeerHeaderV3(
                        per_peer_header, verbose_flag)
                    msg_text += "".join(peer_text)

                # Collect and parse the BGP message header.
                #
                header = CollectBytes(conn, BGP.HEADER_LEN)
                length, msg_type, hdr_text = BGP.ParseBgpHeader(
                    header, verbose=verbose_flag)
                assert msg_type == BGP.UPDATE
                msg_text += "".join(hdr_text)

                # Collect and parse the BGP message body.
                #
                update = CollectBytes(conn, length)
                try:
                    msg_text += "".join(
                        BGP.ParseBgpUpdate(update,
                                           length,
                                           rfc4893_updates=rfc4893_updates,
                                           verbose=verbose_flag))
                except Exception, esc:  # pylint: disable-msg=W0703
                    print "Exception during ParseBgpUpdate: %s\n" % str(esc)

            # Statistics Report
            # draft-ietf-grow-bmp-01.txt section 2.2
            #
            elif msg_type == BMP.MSG_TYPE_STATISTICS_REPORT:

                # if version 3, collect a per peer header
                #
                if bmp_version == 3:
                    per_peer_header = CollectBytes(conn,
                                                   BMP.PER_PEER_HEADER_LEN_V3)
                    peer_flags, peer_text = BMP.ParseBmpPerPeerHeaderV3(
                        per_peer_header, verbose_flag)
                    msg_text += "".join(peer_text)

                # collect the SR message
                #
                msg_text += "".join(CollectBmpStatsMsg(conn))

            # Peer Down message
            # draft-ietf-grow-bmp-01.txt section 2.3
            #
            elif msg_type == BMP.MSG_TYPE_PEER_DOWN_NOTIFICATION:
                msg_text += "".join(
                    CollectBmpPeerDown(conn, verbose=verbose_flag))
示例#11
0
 def interpret_file(self, filepath, layer_count):
     bmp = BMP.BMP()
     try:
         bmp.load_file(filepath)
     except:
         raise PyMSError('Interpreting',
                         "Could not load file '%s'" % filepath)
     width = bmp.width
     height = int(bmp.height / float(layer_count))
     if bmp.height % height:
         raise PyMSError(
             'Interpreting',
             "Image is not the correct height to fit %d layers" %
             layer_count)
     layers = list(SPKLayer() for _ in range(layer_count))
     runs_by_row = []
     for row in bmp.image:
         runs = []
         runs_by_row.append(runs)
         run = None
         for x, i in enumerate(row):
             if i and not run:
                 run = [x, x]
                 runs.append(run)
             elif not i and run:
                 run[1] = x - 1
                 run = None
         if run:
             run[1] = len(row)
     images = {}
     for y, row in enumerate(runs_by_row):
         for x1, x2 in row:
             sx1, sy1, sx2, sy2 = x1, y, x2, y
             runs = [[x1, x2]]
             found = True
             ny = y + 1
             lx1, lx2 = x1, x2
             while ny < len(runs_by_row) and found:
                 found = False
                 i = 0
                 while i < len(runs_by_row[ny]):
                     nx1, nx2 = runs_by_row[ny][i]
                     if lx1 <= nx1 <= lx2 or lx1 <= nx2 <= lx2 or (
                             nx1 <= lx1 and lx2 <= nx2):
                         runs.append([nx1, nx2])
                         found = True
                         sx1 = min(sx1, nx1)
                         sx2 = max(sx2, nx2)
                         lx1 = nx1
                         lx2 = nx2
                         sy2 = ny
                         del runs_by_row[ny][i]
                         break
                     elif nx1 > sx2:
                         break
                     i += 1
                 ny += 1
             w = (sx2 - sx1) + 1
             h = (sy2 - sy1) + 1
             pixels = list([0] * w for _ in range(h))
             for iy, (ix1, ix2) in enumerate(runs):
                 pixels[iy][ix1 - sx1:ix2 - sx1 +
                            1] = bmp.image[sy1 + iy][ix1:ix2 + 1]
             check = tuple(tuple(r) for r in pixels)
             if check in images:
                 image = images[check]
             else:
                 image = SPKImage()
                 image.width = w
                 image.height = h
                 image.pixels = pixels
                 images[check] = image
             l = sy1 / height
             star = SPKStar()
             star.x = sx1
             star.y = sy1 - height * l
             star.image = image
             layers[l].stars.append(star)
     self.layers = layers
     self.images = images.values()