bmpblk: Allow changing preview window size.

Most x86 platforms are using stretched output images and will be scaled by BIOS.
To help checking if the output images looks fine, bitmap_viewer needs to support
overriding the preview window size and scale when needed.

A new param "panel_size" is now available for the "bitmap_viewer" command, ex:

  ./bitmap_viewer DEFAULT.yaml 1366x768

This will preview bitmaps in a 1366x768 window, and scale the images (by the
first image in screen) if required.

BUG=none
TEST=manual:
  Run.
     make falco; # Saw correct panel size param (1366x768) in output.
     bitmap_viewer build/falco/DEFAULT.yaml 1366x768 # See correct images.

Change-Id: I60a4890d349681c72f02911aaba0d140215cdfe1
Reviewed-on: https://chrome-internal-review.googlesource.com/155466
Tested-by: Hung-Te Lin <[email protected]>
Reviewed-by: Bill Richardson <[email protected]>
Commit-Queue: Hung-Te Lin <[email protected]>
diff --git a/bitmap_viewer b/bitmap_viewer
index 85473ee..099829c 100755
--- a/bitmap_viewer
+++ b/bitmap_viewer
@@ -5,6 +5,7 @@
 
 """Quick-and-dirty viewer for bmpblock yaml files"""
 import os
+import re
 import sys
 import wx
 
@@ -15,23 +16,39 @@
 
 class MyApp(wx.App):
 
+  def __init__(self, prog_path, yaml_path, window_size):
+    self._prog_path = prog_path
+    self._yaml_path = yaml_path
+    self._window_size = window_size
+    wx.App.__init__(self, False)
+
   def OnInit(self):
-    progname = os.path.basename(sys.argv[0])
-    progdir = os.path.abspath(os.path.dirname(sys.argv[0]))
+    progname = os.path.basename(self._prog_path)
+    progdir = os.path.abspath(os.path.dirname(self._prog_path))
     self._bmpblock = bmpblock.BmpBlock(os.path.join(progdir, 'lib'),
-                                       sys.argv[1])
+                                       self._yaml_path)
     self._mainframe = pixcontrol.Frame(self._bmpblock, progname)
     self._mainframe.Show()
     self.SetTopWindow(self._mainframe)
-    self._imgframe = pixdisplay.Frame(self._bmpblock, sys.argv[1])
+    self._imgframe = pixdisplay.Frame(self._bmpblock, self._yaml_path,
+                                      self._window_size)
     self._imgframe.Show()
     return True
 
 def main():
-  if len(sys.argv) != 2:
-    print "You must specify a config.yaml file to view"
-    sys.exit(1)
-  MyApp(False).MainLoop()
+  def parse_window_size(size_arg):
+    matched = re.findall(r'^([0-9]+)x([0-9]+)$', size_arg)
+    if (not matched) or len(matched) != 1:
+      exit("Invalid window size: %s" % size_arg)
+    return map(int, matched[0])
+
+  if len(sys.argv) < 2 or len(sys.argv) > 3:
+    exit("Usage: %s config_yaml [override_window_size]\n\t"
+         "\tExample: %s build/std/DEFAULT.yaml 1366x768" %
+         (sys.argv[0], sys.argv[0]))
+  window_size = parse_window_size(sys.argv[2]) if len(sys.argv) == 3 else None
+
+  MyApp(sys.argv[0], sys.argv[1], window_size).MainLoop()
 
 if __name__ == '__main__':
   main()
diff --git a/images/build_images.py b/images/build_images.py
index b949b78..5470b4a 100755
--- a/images/build_images.py
+++ b/images/build_images.py
@@ -254,7 +254,9 @@
     config_database: a dictionary, the configuration from load_boards_config.
 
   Returns:
-    A string, the output folder containing all resources.
+    A list (folder, panel_size), "folder" is a string for the output folder
+    containing all resources, and panel_size is a list (w, h) for the expected
+    size of panel for the output bitmaps.
   """
 
   config = config_database.get(board, None)
@@ -348,17 +350,20 @@
   # Create YAML file.
   shell("cd %s && %s/make_default_yaml.py %s" %
         (output_dir, SCRIPT_BASE, ' '.join(locales)))
-  return output_dir
+  return (output_dir, panel_size)
 
 
-def build_bitmap_block(board, output_dir):
+def build_bitmap_block(board, output_info):
   """Builds the bitmap block output file (and archive files) in output_dir.
 
   Args:
     board: a string, the name of board to use.
-    output_dir: a string, the folder containing all resources and to put the
-                output files.
+    output_info: a list (output_dir, panel_size). output_dir is a string of the
+      folder containing all resources and to put the output files, and
+      panel_size is a list (w, h) for the expected panel size.
   """
+  output_dir = output_info[0]
+  panel_size = output_info[1]
   # Git information.
   git_version = shell('git show -s --format="%h"', capture_stdout=True)
   git_dirty = shell("git diff --shortstat", capture_stdout=True) and "_mod"
@@ -370,8 +375,9 @@
         (output_dir, BMPLKU, BMPBLK_OUTPUT, archive_name, BMPBLK_OUTPUT))
   print ("\nBitmap block file generated in: %s/%s\n"
          "Archive file to upload: %s/%s\n"
-         "To preview, run ../bitmap_viewer %s/DEFAULT.yaml\n" %
-         (output_dir, BMPBLK_OUTPUT, output_dir, archive_name, output_dir))
+         "To preview, run '../bitmap_viewer %s/DEFAULT.yaml %sx%s'\n" %
+         (output_dir, BMPBLK_OUTPUT, output_dir, archive_name, output_dir,
+          panel_size[0], panel_size[1]))
   # TODO(hungte) Check and alert if output binary is too large.
   shell("ls -l %s/%s" % (output_dir, BMPBLK_OUTPUT))
 
@@ -389,8 +395,8 @@
     args = config_database.keys()
     print 'Building all boards: ', args
   for board in args:
-    output_dir = build_image(board, config_database)
-    build_bitmap_block(board, output_dir)
+    output_info = build_image(board, config_database)
+    build_bitmap_block(board, output_info)
 
 
 if __name__ == '__main__':
diff --git a/lib/pixdisplay.py b/lib/pixdisplay.py
index 339e2df..1786adc 100755
--- a/lib/pixdisplay.py
+++ b/lib/pixdisplay.py
@@ -8,11 +8,12 @@
 
 class MyPanel(wx.Panel):
 
-  def __init__(self, parent):
+  def __init__(self, parent, size):
     wx.Panel.__init__(self, parent, wx.ID_ANY)
     self.Bind(wx.EVT_PAINT, self.OnPaint)
     self.parent = parent
     self.imglist = ()
+    self._size = size
 
   def OnPaint(self, evt=None):
     if (evt):
@@ -21,21 +22,28 @@
       dc = wx.ClientDC(self)
 
     done_first = False
+    ratio = (1, 1)
     # The first image in the sequence may be used by the BIOS to set the
     # display resolution. Regardless, it should match the desired or default
     # resolution so that any previous screens get cleared.
     for x, y, filename in self.imglist:
       img = wx.Image(filename, wx.BITMAP_TYPE_ANY)
+      img_size = img.GetSize()
       if (not done_first):
-        size = img.GetSize()
+        if self._size:
+          size = self._size
+          ratio = (size[0] / float(img_size[0]), size[1] / float(img_size[1]))
+        else:
+          size = img_size
         self.SetMinSize(size)
         self.SetSize(size)
         self.Fit()
         w,h = self.parent.GetBestSize()
         self.parent.SetDimensions(-1, -1, w, h, wx.SIZE_AUTO)
         done_first = True
-      bmp = img.ConvertToBitmap()
-      dc.DrawBitmap(bmp, x, y)
+      bmp = img.Scale(img_size[0] * ratio[0], img_size[1] * ratio[1],
+                      wx.IMAGE_QUALITY_HIGH).ConvertToBitmap()
+      dc.DrawBitmap(bmp, x * ratio[0], y * ratio[1])
 
   def OnSave(self, name):
     """Draw the current image sequence into a file."""
@@ -58,7 +66,7 @@
 
 class Frame(wx.Frame):
 
-  def __init__(self, bmpblock=None, title=None):
+  def __init__(self, bmpblock=None, title=None, size=None):
     wx.Frame.__init__(self, None, wx.ID_ANY, title=title)
     self.CreateStatusBar()
     self.SetStatusText(title)
@@ -68,7 +76,7 @@
     if self.bmpblock:
       self.bmpblock.RegisterScreenDisplayObject(self)
 
-    self.p = MyPanel(self)
+    self.p = MyPanel(self, size)
 
 
   def OnQuit(self, event):