SyntaxHighlighter

2013年5月3日金曜日

[EDK]GraphicsOutputProtocolでBMPを表示させて遊んでみた

UEFIでなにかやってみたかったので、Graphics Output Protocolで画面にBMP画像を表示させてみました。
悲しいことにVMWarePlayerではGraphics Output Protocolに実装不足があったのでNt32Pkgを使って試してます。

使った画像は/MdeModulePkg/Logo/Logo.bmp。
何か表示出来ればいいという程度で作ったのでBMPの処理関係はいろいろ決め打ちです。

infファイル


## @file
#  This is the shell application
#
#  Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.

#
#  This program and the accompanying materials
#  are licensed and made available under the terms and conditions of the BSD License
#  which accompanies this distribution. The full text of the license may be found at
#  http://opensource.org/licenses/bsd-license.php
#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
#
##

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = TestApp
  FILE_GUID                      = 305671d4-c671-4792-8ada-e424a87f4632
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  ENTRY_POINT                    = UefiMain

#
# The following information is for reference only and not required by the build tools.
#
#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC
#

[Sources]
  Main.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  UefiApplicationEntryPoint
  UefiLib
  
[Guids]
  gEfiFileInfoGuid
  
[Protocols]
  gEfiGraphicsOutputProtocolGuid
  gEfiSimpleFileSystemProtocolGuid

と、cソースファイル

#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>

#include <Guid/FileInfo.h>

#include <Protocol/DevicePath.h>
#include <Protocol/GraphicsOutput.h>
#include <Protocol/SimpleFileSystem.h>

#define  MAX_BUFFER_SIZE  SIZE_1MB

EFI_SYSTEM_TABLE   *gST;
EFI_BOOT_SERVICES  *gBS;


#pragma pack(1)
typedef struct {
  CHAR8   Type[2];
  UINT32  Size;
  UINT32  Reserved;
  UINT32  Offset;
  UINT32  CoreHeaderSize;
  UINT32  Width;
  UINT32  Height;
  UINT16  Planes;
  UINT16  BitCount;
} BITMAP_FILE_HEADER;
#pragma pack()

STATIC
EFI_STATUS
LoadBitmapFile (
  IN     CHAR16  *Path,
     OUT VOID    **BmpBuffer,
     OUT UINTN   *BmpSize
  );

STATIC
EFI_STATUS
DrawBmp (
  IN     EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput,
  IN     VOID                          *BmpBuffer,
  IN     UINTN                         BmpSize
  );


EFI_STATUS
EFIAPI
UefiMain (
  IN     EFI_HANDLE        ImageHandle,
  IN     EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                    Status = EFI_SUCCESS;
  EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput;
  VOID                          *BmpBuffer = NULL;
  UINTN                         BmpSize;

  gST = SystemTable;
  gBS = gST->BootServices;

  gBS->LocateProtocol (
         &gEfiGraphicsOutputProtocolGuid,
         NULL,
         &GraphicsOutput
       );

  Status = LoadBitmapFile (L"Logo.bmp", &BmpBuffer, &BmpSize);
  if (EFI_ERROR (Status)) {
    if (BmpBuffer != NULL) {
      FreePool (BmpBuffer);
      return Status;
    }
  }

  Status = DrawBmp (GraphicsOutput, BmpBuffer, BmpSize);

  if (BmpBuffer != NULL) {
    FreePool (BmpBuffer);
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
LoadBitmapFile (
  IN     CHAR16  *Path,
     OUT VOID    **BmpBuffer,
     OUT UINTN   *BmpSize
)
{
  EFI_STATUS                       Status = EFI_SUCCESS;
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *SimpleFile;
  EFI_FILE_PROTOCOL                *Root;
  EFI_FILE_PROTOCOL                *File;
  UINTN                            BufferSize;
  VOID                             *Buffer = NULL;

  Status = gBS->LocateProtocol (
                  &gEfiSimpleFileSystemProtocolGuid,
                  NULL,
                  &SimpleFile
                );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%r on Locate EFI Simple File System Protocol.\n", Status));
    return Status;
  }

  Status = SimpleFile->OpenVolume (SimpleFile, &Root);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%r on Open volume.\n", Status));
    return Status;
  }

  Status = Root->Open (Root, &File, Path, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%r on Open file.\n", Status));
    return Status;
  }

  BufferSize = MAX_BUFFER_SIZE;
  Buffer = AllocatePool (BufferSize);
  if (Buffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  Status = File->Read (
                   File,
                   &BufferSize,
                   Buffer
                 );
  if (BufferSize == MAX_BUFFER_SIZE) {
    DEBUG ((EFI_D_ERROR, "Buffer Size Too Small.\n"));
    if (Buffer != NULL) {
      FreePool (Buffer);
    }
    return EFI_OUT_OF_RESOURCES;
  }

  Buffer = ReallocatePool (BufferSize, MAX_BUFFER_SIZE, Buffer);

  *BmpBuffer = Buffer;
  *BmpSize   = BufferSize;

  return EFI_SUCCESS;
}


STATIC
EFI_STATUS
DrawBmp (
  IN     EFI_GRAPHICS_OUTPUT_PROTOCOL  *GraphicsOutput,
  IN     VOID                          *BmpBuffer,
  IN     UINTN                         BmpSize
)
{
  EFI_STATUS                     Status = EFI_SUCCESS;
  BITMAP_FILE_HEADER             *BitmapHeader;
  UINT8                          *BitmapData;
  UINT32                         *Palette;
  UINTN                          Pixels;
  UINTN                          XIndex;
  UINTN                          YIndex;
  UINTN                          Pos;
  UINTN                          BltPos;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL  *BltBuffer;

  BitmapHeader = (BITMAP_FILE_HEADER *) BmpBuffer;

  DEBUG ((EFI_D_ERROR, "%d x %d\n", BitmapHeader->Width, BitmapHeader->Height));

  if (BitmapHeader->CoreHeaderSize != 40) {
    return EFI_UNSUPPORTED;
  }
  if (BitmapHeader->BitCount != 8) {
    return EFI_UNSUPPORTED;
  }

  BitmapData = (UINT8*)BmpBuffer + BitmapHeader->Offset;
  Palette    = (UINT32*) ((UINT8*)BmpBuffer + 0x36);/*決め打ち*/

  Pixels = BitmapHeader->Width * BitmapHeader->Height;
  BltBuffer = AllocateZeroPool (
                sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * Pixels
              );
  if (BltBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  for (YIndex = BitmapHeader->Height; YIndex > 0; YIndex--) {
    for (XIndex = 0; XIndex < BitmapHeader->Width; XIndex++) {
      Pos    = (YIndex - 1) * ((BitmapHeader->Width + 3) / 4) * 4 + XIndex;
      BltPos = (BitmapHeader->Height - YIndex) * BitmapHeader->Width + XIndex;
      BltBuffer[BltPos].Blue     = (UINT8) BitFieldRead32 (Palette[BitmapData[Pos]], 0 , 7 );
      BltBuffer[BltPos].Green    = (UINT8) BitFieldRead32 (Palette[BitmapData[Pos]], 8 , 15);
      BltBuffer[BltPos].Red      = (UINT8) BitFieldRead32 (Palette[BitmapData[Pos]], 16, 23);
      BltBuffer[BltPos].Reserved = (UINT8) BitFieldRead32 (Palette[BitmapData[Pos]], 24, 31);
    }
  }

  Status = GraphicsOutput->Blt (
                    GraphicsOutput,
                    BltBuffer,
                    EfiBltBufferToVideo,
                    0                  , 0                   , /*Source X, Y*/
                    200                , 200                 , /*Dest X, Y*/
                    BitmapHeader->Width, BitmapHeader->Height, /*Width, Height*/
                    0
                  );

  FreePool (BltBuffer);

  return EFI_SUCCESS;
}


実行結果はこんな感じです。


地味ではありますが、画像ファイルが表示されました。

今回はBitmapファイルのフォーマットに触れることが出来、色々と悩まされつつも楽しかったです。
標準でBMPファイルを扱えるプロトコルがあればよかったのですが、軽く調べた中では見つからず、結果として普段触らない領域に触れることが出来ました。


参考:
UEFI仕様
Bitmapフォーマット情報

0 件のコメント:

コメントを投稿