def chr(space, w_ascii): "Return a string of one character with the given ascii code." try: char = __builtin__.chr(space.int_w(w_ascii)) except ValueError: # chr(out-of-range) raise oefmt(space.w_ValueError, "character code not in range(256)") return space.wrap(char)
def chr(space, w_ascii): "Return a string of one character with the given ascii code." try: char = __builtin__.chr(space.int_w(w_ascii)) except ValueError: # chr(out-of-range) raise oefmt(space.w_ValueError, "character code not in range(256)") return space.newtext(char)
def test_cover_global_var(self): global chr self.assertEqual(' ', chr(32)) chr = 'b' self.assertEqual('b', chr) import __builtin__ as builtin self.assertEqual(' ', builtin.chr(32))
def chr(space, w_ascii): "Return a string of one character with the given ascii code." try: char = __builtin__.chr(space.int_w(w_ascii)) except ValueError: # chr(out-of-range) raise OperationError(space.w_ValueError, space.wrap("character code not in range(256)")) return space.wrap(char)
class Renderer(object): '''Object that is responsible for generating the highlighted string. :param dict theme_config: Main theme configuration. :param local_themes: Local themes. Is to be used by subclasses from ``.get_theme()`` method, base class only records this parameter to a ``.local_themes`` attribute. :param dict theme_kwargs: Keyword arguments for ``Theme`` class constructor. :param Colorscheme colorscheme: Colorscheme object that holds colors configuration. :param PowerlineLogger pl: Object used for logging. :param int ambiwidth: Width of the characters with east asian width unicode attribute equal to ``A`` (Ambigious). :param dict options: Various options. Are normally not used by base renderer, but all options are recorded as attributes. ''' segment_info = { 'environ': os.environ, 'getcwd': getattr(os, 'getcwdu', os.getcwd), 'home': os.environ.get('HOME'), } '''Basic segment info. Is merged with local segment information by ``.get_segment_info()`` method. Keys: ``environ`` Object containing environment variables. Must define at least the following methods: ``.__getitem__(var)`` that raises ``KeyError`` in case requested environment variable is not present, ``.get(var, default=None)`` that works like ``dict.get`` and be able to be passed to ``Popen``. ``getcwd`` Function that returns current working directory. Will be called without any arguments, should return ``unicode`` or (in python-2) regular string. ``home`` String containing path to home directory. Should be ``unicode`` or (in python-2) regular string or ``None``. ''' character_translations = {ord(' '): NBSP} '''Character translations for use in escape() function. See documentation of ``unicode.translate`` for details. ''' np_character_translations = dict( ((i, '^' + chr(i + 0x40)) for i in range(0x20))) '''Non-printable character translations These are used to transform characters in range 0x00—0x1F into ``^@``, ``^A`` and so on. Unilke with ``.escape()`` method (and ``character_translations``) result is passed to ``.strwidth()`` method. Note: transforms tab into ``^I``. ''' def __init__(self, theme_config, local_themes, theme_kwargs, colorscheme, pl, ambiwidth=1, **options): self.__dict__.update(options) self.theme_config = theme_config theme_kwargs['pl'] = pl self.pl = pl self.theme = Theme(theme_config=theme_config, **theme_kwargs) self.local_themes = local_themes self.theme_kwargs = theme_kwargs self.colorscheme = colorscheme self.width_data = { 'N': 1, # Neutral 'Na': 1, # Narrow 'A': ambiwidth, # Ambigious 'H': 1, # Half-width 'W': 2, # Wide 'F': 2, # Fullwidth } def strwidth(self, string): '''Function that returns string width. Is used to calculate the place given string occupies when handling ``width`` argument to ``.render()`` method. Must take east asian width into account. :param unicode string: String whose width will be calculated. :return: unsigned integer. ''' return sum((0 if combining(symbol) else self.width_data[east_asian_width(symbol)] for symbol in string)) def get_theme(self, matcher_info): '''Get Theme object. Is to be overridden by subclasses to support local themes, this variant only returns ``.theme`` attribute. :param matcher_info: Parameter ``matcher_info`` that ``.render()`` method received. Unused. ''' return self.theme def shutdown(self): '''Prepare for interpreter shutdown. The only job it is supposed to do is calling ``.shutdown()`` method for all theme objects. Should be overridden by subclasses in case they support local themes. ''' self.theme.shutdown() def _get_highlighting(self, segment, mode): segment['highlight'] = self.colorscheme.get_highlighting( segment['highlight_group'], mode, segment.get('gradient_level')) if segment['divider_highlight_group']: segment['divider_highlight'] = self.colorscheme.get_highlighting( segment['divider_highlight_group'], mode) else: segment['divider_highlight'] = None return segment def get_segment_info(self, segment_info, mode): '''Get segment information. Must return a dictionary containing at least ``home``, ``environ`` and ``getcwd`` keys (see documentation for ``segment_info`` attribute). This implementation merges ``segment_info`` dictionary passed to ``.render()`` method with ``.segment_info`` attribute, preferring keys from the former. It also replaces ``getcwd`` key with function returning ``segment_info['environ']['PWD']`` in case ``PWD`` variable is available. :param dict segment_info: Segment information that was passed to ``.render()`` method. :return: dict with segment information. ''' r = self.segment_info.copy() r['mode'] = mode if segment_info: r.update(segment_info) if 'PWD' in r['environ']: r['getcwd'] = lambda: r['environ']['PWD'] return r def render(self, mode=None, width=None, side=None, output_raw=False, segment_info=None, matcher_info=None): '''Render all segments. When a width is provided, low-priority segments are dropped one at a time until the line is shorter than the width, or only segments with a negative priority are left. If one or more filler segments are provided they will fill the remaining space until the desired width is reached. :param str mode: Mode string. Affects contents (colors and the set of segments) of rendered string. :param int width: Maximum width text can occupy. May be exceeded if there are too much non-removable segments. :param str side: One of ``left``, ``right``. Determines which side will be rendered. If not present all sides are rendered. :param bool output_raw: Changes the output: if this parameter is ``True`` then in place of one string this method outputs a pair ``(colored_string, colorless_string)``. :param dict segment_info: Segment information. See also ``.get_segment_info()`` method. :param matcher_info: Matcher information. Is processed in ``.get_theme()`` method. ''' theme = self.get_theme(matcher_info) segments = theme.get_segments( side, self.get_segment_info(segment_info, mode)) # Handle excluded/included segments for the current mode segments = [ self._get_highlighting(segment, mode) for segment in segments if mode not in segment['exclude_modes'] and (not segment['include_modes'] or mode in segment['include_modes']) ] segments = [ segment for segment in self._render_segments(theme, segments) ] if not width: # No width specified, so we don't need to crop or pad anything return construct_returned_value( ''.join([segment['_rendered_hl'] for segment in segments]) + self.hlstyle(), segments, output_raw) # Create an ordered list of segments that can be dropped segments_priority = sorted( (segment for segment in segments if segment['priority'] is not None), key=lambda segment: segment['priority'], reverse=True) while sum([segment['_len'] for segment in segments ]) > width and len(segments_priority): segments.remove(segments_priority[0]) segments_priority.pop(0) # Distribute the remaining space on spacer segments segments_spacers = [ segment for segment in segments if segment['width'] == 'auto' ] if segments_spacers: distribute_len, distribute_len_remainder = divmod( width - sum([segment['_len'] for segment in segments]), len(segments_spacers)) for segment in segments_spacers: if segment['align'] == 'l': segment['_space_right'] += distribute_len elif segment['align'] == 'r': segment['_space_left'] += distribute_len elif segment['align'] == 'c': space_side, space_side_remainder = divmod( distribute_len, 2) segment['_space_left'] += space_side + space_side_remainder segment['_space_right'] += space_side segments_spacers[0]['_space_right'] += distribute_len_remainder rendered_highlighted = ''.join([ segment['_rendered_hl'] for segment in self._render_segments(theme, segments) ]) + self.hlstyle() return construct_returned_value(rendered_highlighted, segments, output_raw) def _render_segments(self, theme, segments, render_highlighted=True): '''Internal segment rendering method. This method loops through the segment array and compares the foreground/background colors and divider properties and returns the rendered statusline as a string. The method always renders the raw segment contents (i.e. without highlighting strings added), and only renders the highlighted statusline if render_highlighted is True. ''' segments_len = len(segments) for index, segment in enumerate(segments): segment['_rendered_raw'] = '' segment['_rendered_hl'] = '' prev_segment = segments[index - 1] if index > 0 else theme.EMPTY_SEGMENT next_segment = segments[ index + 1] if index < segments_len - 1 else theme.EMPTY_SEGMENT compare_segment = next_segment if segment[ 'side'] == 'left' else prev_segment outer_padding = ' ' if (index == 0 and segment['side'] == 'left' ) or (index == segments_len - 1 and segment['side'] == 'right') else '' divider_type = 'soft' if compare_segment['highlight'][ 'bg'] == segment['highlight']['bg'] else 'hard' divider_raw = theme.get_divider(segment['side'], divider_type) divider_spaces = theme.get_spaces() divider_highlighted = '' contents_raw = segment['contents'] contents_highlighted = '' draw_divider = segment['draw_' + divider_type + '_divider'] # Pad segments first if draw_divider: if segment['side'] == 'left': contents_raw = outer_padding + ( segment['_space_left'] * ' ') + contents_raw + ( (divider_spaces + segment['_space_right']) * ' ') else: contents_raw = ( (divider_spaces + segment['_space_left']) * ' ') + contents_raw + (segment['_space_right'] * ' ') + outer_padding else: if segment['side'] == 'left': contents_raw = outer_padding + ( segment['_space_left'] * ' ') + contents_raw + (segment['_space_right'] * ' ') else: contents_raw = ( segment['_space_left'] * ' ') + contents_raw + ( segment['_space_right'] * ' ') + outer_padding # Replace spaces with no-break spaces divider_raw = divider_raw.replace(' ', NBSP) contents_raw = contents_raw.translate( self.np_character_translations) # Apply highlighting to padded dividers and contents if render_highlighted: if divider_type == 'soft': divider_highlight_group_key = 'highlight' if segment[ 'divider_highlight_group'] is None else 'divider_highlight' divider_fg = segment[divider_highlight_group_key]['fg'] divider_bg = segment[divider_highlight_group_key]['bg'] else: divider_fg = segment['highlight']['bg'] divider_bg = compare_segment['highlight']['bg'] divider_highlighted = self.hl(divider_raw, divider_fg, divider_bg, False) contents_highlighted = self.hl(self.escape(contents_raw), **segment['highlight']) # Append padded raw and highlighted segments to the rendered segment variables if draw_divider: if segment['side'] == 'left': segment['_rendered_raw'] += contents_raw + divider_raw segment[ '_rendered_hl'] += contents_highlighted + divider_highlighted else: segment['_rendered_raw'] += divider_raw + contents_raw segment[ '_rendered_hl'] += divider_highlighted + contents_highlighted else: if segment['side'] == 'left': segment['_rendered_raw'] += contents_raw segment['_rendered_hl'] += contents_highlighted else: segment['_rendered_raw'] += contents_raw segment['_rendered_hl'] += contents_highlighted segment['_len'] = self.strwidth(segment['_rendered_raw']) yield segment @classmethod def escape(cls, string): '''Method that escapes segment contents. ''' return string.translate(cls.character_translations) def hlstyle(fg=None, bg=None, attr=None): '''Output highlight style string. Assuming highlighted string looks like ``{style}{contents}`` this method should output ``{style}``. If it is called without arguments this method is supposed to reset style to its default. ''' raise NotImplementedError def hl(self, contents, fg=None, bg=None, attr=None): '''Output highlighted chunk. This implementation just outputs ``.hlstyle()`` joined with ``contents``. ''' return self.hlstyle(fg, bg, attr) + (contents or '')
from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE from powerline.theme import Theme import vim import sys try: from __builtin__ import unichr as chr except ImportError: pass vim_mode = vim_get_func('mode', rettype=str) mode_translations = { chr(ord('V') - 0x40): '^V', chr(ord('S') - 0x40): '^S', } class VimRenderer(Renderer): '''Powerline vim segment renderer.''' character_translations = Renderer.character_translations.copy() character_translations[ord('%')] = '%%' segment_info = Renderer.segment_info.copy() segment_info.update(environ=environ) def __init__(self, *args, **kwargs): if not hasattr(vim, 'strwidth'):
from powerline.colorscheme import ATTR_BOLD, ATTR_ITALIC, ATTR_UNDERLINE from powerline.theme import Theme import vim import sys try: from __builtin__ import unichr as chr except ImportError: pass vim_mode = vim_get_func('mode', rettype=str) mode_translations = { chr(ord('V') - 0x40): '^V', chr(ord('S') - 0x40): '^S', } class VimRenderer(Renderer): '''Powerline vim segment renderer.''' character_translations = Renderer.character_translations.copy() character_translations[ord('%')] = '%%' def __init__(self, *args, **kwargs): if not hasattr(vim, 'strwidth'): # Hope nobody want to change this at runtime if vim.eval('&ambiwidth') == 'double': kwargs = dict(**kwargs)
def _decompress(length, resetValue, getNextValue): dictionary = {} enlargeIn = 4 dictSize = 4 numBits = 3 entry = "" result = [] data = Object( val=getNextValue(0), position=resetValue, index=1 ) for i in range(3): dictionary[i] = i bits = 0 maxpower = math.pow(2, 2) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1; next = bits if next == 0: bits = 0 maxpower = math.pow(2, 8) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 c = chr(bits) elif next == 1: bits = 0 maxpower = math.pow(2, 16) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue; data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 c = chr(bits) elif next == 2: return "" dictionary[3] = c w = c result.append(c) counter = 0 while True: counter += 1 if data.index > length: return "" bits = 0 maxpower = math.pow(2, numBits) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue; data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 c = bits if c == 0: bits = 0 maxpower = math.pow(2, 8) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 dictionary[dictSize] = chr(bits) dictSize += 1 c = dictSize - 1 enlargeIn -= 1 elif c == 1: bits = 0 maxpower = math.pow(2, 16) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue; data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 dictionary[dictSize] = chr(bits) dictSize += 1 c = dictSize - 1 enlargeIn -= 1 elif c == 2: return "".join(result) if enlargeIn == 0: enlargeIn = math.pow(2, numBits) numBits += 1 if c in dictionary: entry = dictionary[c] else: if c == dictSize: entry = w + w[0] else: return None result.append(entry) # Add w+entry[0] to the dictionary. dictionary[dictSize] = w + entry[0] dictSize += 1 enlargeIn -= 1 w = entry if enlargeIn == 0: enlargeIn = math.pow(2, numBits) numBits += 1
def _decompress(length, resetValue, getNextValue): dictionary = {} enlargeIn = 4 dictSize = 4 numBits = 3 entry = "" result = [] data = Object(val=getNextValue(0), position=resetValue, index=1) for i in range(3): dictionary[i] = i bits = 0 maxpower = math.pow(2, 2) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 next = bits if next == 0: bits = 0 maxpower = math.pow(2, 8) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 c = chr(bits) elif next == 1: bits = 0 maxpower = math.pow(2, 16) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 c = chr(bits) elif next == 2: return "" dictionary[3] = c w = c result.append(c) counter = 0 while True: counter += 1 if data.index > length: return "" bits = 0 maxpower = math.pow(2, numBits) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 c = bits if c == 0: bits = 0 maxpower = math.pow(2, 8) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 dictionary[dictSize] = chr(bits) dictSize += 1 c = dictSize - 1 enlargeIn -= 1 elif c == 1: bits = 0 maxpower = math.pow(2, 16) power = 1 while power != maxpower: resb = data.val & data.position data.position >>= 1 if data.position == 0: data.position = resetValue data.val = getNextValue(data.index) data.index += 1 bits |= power if resb > 0 else 0 power <<= 1 dictionary[dictSize] = chr(bits) dictSize += 1 c = dictSize - 1 enlargeIn -= 1 elif c == 2: return "".join(result) if enlargeIn == 0: enlargeIn = math.pow(2, numBits) numBits += 1 if c in dictionary: entry = dictionary[c] else: if c == dictSize: entry = w + w[0] else: return None result.append(entry) # Add w+entry[0] to the dictionary. dictionary[dictSize] = w + entry[0] dictSize += 1 enlargeIn -= 1 w = entry if enlargeIn == 0: enlargeIn = math.pow(2, numBits) numBits += 1
def decodeDXT3(data): """ input: one "row" of data (i.e. will produce 4*width pixels) """ blocks = len(data) // 16 # number of blocks in row finalColor = [b"", b"", b"", b""] # row accumulators for block in xrange(blocks): block = data[block*16:block*16+16] # Decode next 16-byte block. bits = unpack(b"<8B", block[:8]) color0, color1 = unpack(b"<HH", block[8:12]) code, = unpack(b"<I", block[12:]) # color 0, packed 5-6-5 r0 = ((color0 >> 11) & 0x1f) << 3 g0 = ((color0 >> 5) & 0x3f) << 2 b0 = (color0 & 0x1f) << 3 # color 1, packed 5-6-5 r1 = ((color1 >> 11) & 0x1f) << 3 g1 = ((color1 >> 5) & 0x3f) << 2 b1 = (color1 & 0x1f) << 3 for j in xrange(4): high = False # Do we want the higher bits? for i in xrange(4): if high: high = False else: high = True alphaCodeIndex = (4*j+i) / 2 finalAlpha = bits[alphaCodeIndex] if high: finalAlpha &= 0xf else: finalAlpha >>= 4 finalAlpha *= 17 # We get a value between 0 and 15 colorCode = (code >> 2*(4*j+i)) & 0x03 if colorCode == 0: finalColor[j] += chr(r0) + chr(g0) + chr(b0) + chr(finalAlpha) elif colorCode == 1: finalColor[j] += chr(r1) + chr(g1) + chr(b1) + chr(finalAlpha) elif colorCode == 2: finalColor[j] += chr((2*r0+r1)//3) + chr((2*g0+g1)//3) + chr((2*b0+b1)//3) + chr(finalAlpha) elif colorCode == 3: finalColor[j] += chr((r0+2*r1)//3) + chr((g0+2*g1)//3) + chr((b0+2*b1)//3) + chr(finalAlpha) return tuple(finalColor)
def decodeDXT1(data, alpha=False): """ input: one "row" of data (i.e. will produce 4*width pixels) """ blocks = len(data) // 8 # number of blocks in row finalColor = [b"", b"", b"", b""] # row accumulators for block in xrange(blocks): # Decode next 8-byte block. color0, color1, bits = unpack(b"<HHI", data[block * 8 : block * 8 + 8]) # color 0, packed 5-6-5 r0 = ((color0 >> 11) & 0x1f) << 3 g0 = ((color0 >> 5) & 0x3f) << 2 b0 = (color0 & 0x1f) << 3 # color 1, packed 5-6-5 r1 = ((color1 >> 11) & 0x1f) << 3 g1 = ((color1 >> 5) & 0x3f) << 2 b1 = (color1 & 0x1f) << 3 # Decode this block into 4x4 pixels # Accumulate the results onto our 4 row accumulators for j in xrange(4): for i in xrange(4): # get next control op and generate a pixel control = bits & 3 bits = bits >> 2 if control == 0: finalColor[j] += chr(r0) + chr(g0) + chr(b0) elif control == 1: finalColor[j] += chr(r1) + chr(g1) + chr(b1) elif control == 2: if color0 > color1: finalColor[j] += chr((2 * r0 + r1) // 3) + chr((2 * g0 + g1) // 3) + chr((2 * b0 + b1) // 3) else: finalColor[j] += chr((r0 + r1) // 2) + chr((g0 + g1) // 2) + chr((b0 + b1) // 2) elif control == 3: if color0 > color1: finalColor[j] += chr((2 * r1 + r0) // 3) + chr((2 * g1 + g0) // 3) + chr((2 * b1 + b0) // 3) else: if alpha: finalColor[j] += b"\0\0\0\0" continue else: finalColor[j] += b"\0\0\0" if alpha: finalColor[j] += chr(0xFF) return tuple(finalColor)
def decodeDXT5(data): """ input: one "row" of data (i.e. will produce 4*width pixels) """ blocks = len(data) // 16 # number of blocks in row finalColor = [b"", b"", b"", b""] # row accumulators for block in xrange(blocks): block = data[block * 16 : block * 16 + 16] # Decode next 16-byte block. alpha0, alpha1 = unpack(b"<BB", block[:2]) bits = unpack(b"<6B", block[2:8]) alphaCode1 = bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24) alphaCode2 = bits[0] | (bits[1] << 8) color0, color1 = unpack(b"<HH", block[8:12]) code, = unpack(b"<I", block[12:]) # color 0, packed 5-6-5 r0 = ((color0 >> 11) & 0x1f) << 3 g0 = ((color0 >> 5) & 0x3f) << 2 b0 = (color0 & 0x1f) << 3 # color 1, packed 5-6-5 r1 = ((color1 >> 11) & 0x1f) << 3 g1 = ((color1 >> 5) & 0x3f) << 2 b1 = (color1 & 0x1f) << 3 for j in xrange(4): for i in xrange(4): # get next control op and generate a pixel alphaCodeIndex = 3*(4*j+i) if alphaCodeIndex <= 12: alphaCode = (alphaCode2 >> alphaCodeIndex) & 0x07 elif alphaCodeIndex == 15: alphaCode = (alphaCode2 >> 15) | ((alphaCode1 << 1) & 0x06) else: # alphaCodeIndex >= 18 and alphaCodeIndex <= 45 alphaCode = (alphaCode1 >> (alphaCodeIndex - 16)) & 0x07 if alphaCode == 0: finalAlpha = alpha0 elif alphaCode == 1: finalAlpha = alpha1 else: if alpha0 > alpha1: finalAlpha = ((8-alphaCode)*alpha0 + (alphaCode-1)*alpha1)/7 else: if alphaCode == 6: finalAlpha = 0 elif alphaCode == 7: finalAlpha = 255 else: finalAlpha = ((6-alphaCode)*alpha0 + (alphaCode-1)*alpha1)/5 colorCode = (code >> 2*(4*j+i)) & 0x03 if colorCode == 0: finalColor[j] += chr(r0) + chr(g0) + chr(b0) + chr(finalAlpha) elif colorCode == 1: finalColor[j] += chr(r1) + chr(g1) + chr(b1) + chr(finalAlpha) elif colorCode == 2: finalColor[j] += chr((2 * r0 + r1) // 3) + chr((2 * g0+g1) // 3) + chr((2 * b0 + b1) // 3 ) + chr(finalAlpha) elif colorCode == 3: finalColor[j] += chr((r0 + 2 * r1) // 3) + chr((g0 + 2*g1) // 3) + chr((b0 + 2 * b1) // 3 ) + chr(finalAlpha) return tuple(finalColor)