|  | # Lint as: python2, python3 | 
|  | # Copyright 2016 The Chromium OS Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  | """The canvas object of LCM display. | 
|  |  | 
|  | It is the temporary memory for storing the bitmap of black-and-white LCM | 
|  | display, by the format of 2-D byte array. | 
|  |  | 
|  | Based on NT7534 SPI data write protocol, display area is divided into pages per | 
|  | 8 pixels in height. Display data is transfered by unit of a byte (8-pixel), | 
|  | and each represents a 8(height)x1(width) pixel values in the same page. | 
|  |  | 
|  | For example, let's say a pixel[64][128] array. It will have 64/8=8 pages in | 
|  | total. For each page it has 128 bytes data, each byte counts as a column. In | 
|  | canvas, the whole bitmap will be stored in byte[8][128] array. | 
|  | """ | 
|  |  | 
|  | from __future__ import absolute_import | 
|  | from __future__ import division | 
|  | from __future__ import print_function | 
|  |  | 
|  | import array | 
|  |  | 
|  | from . import chameleon_common  # pylint: disable=W0611 | 
|  | from chameleond.utils import lcm_font | 
|  | from six.moves import range | 
|  |  | 
|  |  | 
|  | class LcmCanvasError(Exception): | 
|  | """Exception raise when any unexpected behavior happened on LCM canvas.""" | 
|  | pass | 
|  |  | 
|  |  | 
|  | class LcmCanvas(object): | 
|  | """A Class for LCM canvas.""" | 
|  |  | 
|  | _PAGE_HEIGHT = 8 | 
|  |  | 
|  | def __init__(self, height, width, boot_screen=None): | 
|  | """Constructs a LcmCanvas object. | 
|  |  | 
|  | Args: | 
|  | height: The display height in pixels. | 
|  | width: The display width in pixels. | 
|  | boot_screen: The bitmap file for boot screen; None for blank screen only. | 
|  | """ | 
|  | if height % self._PAGE_HEIGHT != 0: | 
|  | raise LcmCanvasError('Canvas height must be the multiple of %d...' % | 
|  | self._PAGE_HEIGHT) | 
|  | self._height = height | 
|  | # Total pages of LcdCanvas. | 
|  | self.pages = height / self._PAGE_HEIGHT | 
|  | # Total columns of LcdCanvas. | 
|  | self.columns = width | 
|  | # The maximum lengths of printing characters in a line of LcdCanvas. | 
|  | self.max_char_length = self.columns / lcm_font.FONT_WIDTH | 
|  | # The maximum lines for printing characters of LcdCanvas. | 
|  | self.max_char_lines = height / lcm_font.FONT_HEIGHT | 
|  |  | 
|  | # Create an 2-D byte array of bitmap. | 
|  | self.bitmap = [] | 
|  | for _ in range(self.pages): | 
|  | self.bitmap.append(array.array('c', [chr(0x00)] * self.columns)) | 
|  | if boot_screen: | 
|  | self.DrawFromFile(boot_screen) | 
|  |  | 
|  | def Clear(self, byte=0x00): | 
|  | """Clears the bitmap with default byte. | 
|  |  | 
|  | Args: | 
|  | byte: The defaulkt byte. | 
|  | """ | 
|  | for page in range(self.pages): | 
|  | for column in range(self.columns): | 
|  | self.SetByte(byte, page, column) | 
|  |  | 
|  | def DrawChar(self, char, line, column, highlight, underline): | 
|  | """Draws a character to bitmap. | 
|  |  | 
|  | Args: | 
|  | char: The character. | 
|  | line: The line number of location. | 
|  | column: The column number of location. | 
|  | highlight: Whether character is highlighted. | 
|  | underline: Whether character is underlined. | 
|  | """ | 
|  | page = line * lcm_font.FONT_PAGES | 
|  | if highlight: | 
|  | font = lcm_font.GetHighlightFont(char) | 
|  | elif underline: | 
|  | font = lcm_font.GetUnderlineFont(char) | 
|  | else: | 
|  | font = lcm_font.GetFont(char) | 
|  | for y in range(lcm_font.FONT_HEIGHT / self._PAGE_HEIGHT): | 
|  | for x in range(lcm_font.FONT_WIDTH): | 
|  | font_index = y * lcm_font.FONT_WIDTH + x | 
|  | self.SetByte(font[font_index], page + y, column + x) | 
|  |  | 
|  | def DrawFromFile(self, bitmap_file): | 
|  | """Draws the bitmap from given bitmap file. | 
|  |  | 
|  | Args: | 
|  | bitmap_file: The bitmap file. | 
|  | """ | 
|  | with open(bitmap_file) as f: | 
|  | bitmap_in = f.read() | 
|  | for page in range(self.pages): | 
|  | for column in range(self.columns): | 
|  | self.SetByte(ord(bitmap_in[page * self.columns + column]), page, column) | 
|  |  | 
|  | def SetByte(self, byte, page, column): | 
|  | """Sets a byte to bitmap. | 
|  |  | 
|  | Args: | 
|  | byte: The byte. | 
|  | page: The page number of location. | 
|  | column: The column number of location. | 
|  | """ | 
|  | try: | 
|  | self.bitmap[page][column] = chr(byte) | 
|  | except IndexError: | 
|  | raise LcmCanvasError('Request index out of range: page=%d column=%d' % | 
|  | (page, column)) | 
|  |  | 
|  | def SetPixel(self, pixel, y, x): | 
|  | """Sets a pixel to bitmap. | 
|  |  | 
|  | Args: | 
|  | pixel: 1 for filled pixel; 0 for blank pixel. | 
|  | y: The y-direction location in pixels. | 
|  | x: The x-direction location in pixels. | 
|  | """ | 
|  | page = y / self._PAGE_HEIGHT | 
|  | bitmask = 0x1 << (y % self._PAGE_HEIGHT) | 
|  | try: | 
|  | byte = ord(self.bitmap[page][x]) | 
|  | if pixel: | 
|  | self.bitmap[page][x] = chr(byte | bitmask) | 
|  | else: | 
|  | self.bitmap[page][x] = chr(byte & ~bitmask) | 
|  | except IndexError: | 
|  | raise LcmCanvasError('Request index out of range: page=%d column=%d' % | 
|  | (page, x)) |