はじめに
今回は RGB を意識して
Frame Buffer を制御してみる。
前回のコードからの変更点も含めて必要なフローをまとめた
PIXEL control flow
諸情報を構造体で Kernel に共有
4 章のソースからヘッダファイル"frame_buffer_config.hpp"が追加されます。
#pragma once
#include <stdint.h>
enum PixelFormat {
kPixelRGBResv8BitPerColor,
kPixelBGRResv8BitPerColor,
};
struct FrameBufferConfig {
uint8_t* frame_buffer;
uint32_t pixels_per_scan_line;
uint32_t horizontal_resolution;
uint32_t vertical_resolution;
enum PixelFormat pixel_format;
};
enum は初対面。ココが分かりやすい。
なぜ、このような事をするのか、答えは Main.c にありました。
struct FrameBufferConfig config = { //struct FrameBufferConfig{
(UINT8*)gop->Mode->FrameBufferBase, // uint8_t* frame_buffer;
gop->Mode->Info->PixelsPerScanLine, // uint32_t pixels_per_scan_line;
gop->Mode->Info->HorizontalResolution, // uint32_t horizontal_resolution;
gop->Mode->Info->VerticalResolution, // vertical_resolution;
0 // enum PixelFormat pixel_format;};
};
FrameBufferBase はピクセルそのものなので、
それ以外が何者か調べてみました。
前述の struct FrameZBufferConfig config の後に
以下の記述が Main.c で続きます。
switch (gop->Mode->Info->PixelFormat) {
case PixelRedGreenBlueReserved8BitPerColor:
config.pixel_format = kPixelRGBResv8BitPerColor;
break;
case PixelBlueGreenRedReserved8BitPerColor:
config.pixel_format = kPixelBGRResv8BitPerColor;
break;
default:
Print(L"Unimplemented pixel format: %d\n", gop->Mode->Info->PixelFormat);
Halt();
}
gop->Mode->Info->PixelFormat を確認して,
RGB であれば、kPixelRGBResv8BitPerColor
BGR であれば、kPixelBGRResv8BitPerColorとしています。
※kPixelRGBResv8BitPerColor/kPixelBGRResv8BitPerColor は
UEFI の規格書にないパラメータなので有難くコピーさせていただきます。
画像に関する緒情報を構造体 config に集約している事が分かります。
これらを kernel に渡すために Main.c の記述は以下のように変更します。
//[before]
typedef void EntryPointType(UINT64, UINT64);
EntryPointType* entry_point = (EntryPointType*)entry_addr;
entry_point(gop->Mode->FrameBufferBase, gop->Mode->FrameBufferSize);
//[after]
typedef void EntryPointType(const struct FrameBufferConfig*);
EntryPointType* entry_point = (EntryPointType*)entry_addr;
entry_point(&config);
画面全体を白塗り
白線 1 本目 (x,y): (0,0)->(0,1)->(0,2),,,,,->(0,vertical_resolution-1)
白線 2 本目 (x,y): (1,0)->(1,1)->(1,2),,,,,->(1,vertical_resolution-1)
っというように sample code では縦の白線を繋ぎ合わせて画面を白くしています。
extern "C" void KernelMain(const FrameBufferConfig& frame_buffer_config) {
for (int x = 0; x < frame_buffer_config.horizontal_resolution; ++x) {
for (int y = 0; y < frame_buffer_config.vertical_resolution; ++y) {
/*ココ=>*/ WritePixel(frame_buffer_config, x, y, {255, 255, 255});
}
}
while (1) __asm__("hlt");
}
次に 1 ピクセルに色付けするための事前知識を整理します。
・1 ピクセル 32bit(4Byte)
=>RGB の場合
8bit(1Byte) : Red
8bit(1Byte) : Green
8bit(1Byte) : Blue
8bit(1Byte) : Reserved
=>BGR の場合
8bit(1Byte) : Blue
8bit(1Byte) : Green
8bit(1Byte) : Red
8bit(1Byte) : Reserved
・各ピクセルを総合し、配列として一塊で考えて制御する
・x 軸には表示できない領域もピクセルを配置している
シンプルに簡潔に伝えたかったのですが
むしろ長いコメントになった(伝わってるかなー、ボキャ貧でスマソ)。
struct PixelColor {
uint8_t r, g, b;
};
int WritePixel(const FrameBufferConfig& config,
int x, int y, const PixelColor& c) {
/*↓ 何個目の pixel を編集するか整理*/
const int pixel_position = config.pixels_per_scan_line * y + x;
/*↓ pixel_position が分かれば、1pixel=32bit/uint8=8bitなので
pixel_position *4 とすると &config.frame_buffer[4 * pixel_position] は
[R / G / B / Reserved] or [B / G / R / Reserved]の
最初の R or B のアドレスを呼び出せる*/
uint8_t* p = &config.frame_buffer[4 * pixel_position];
/*ポインタを使って各 pixel のアドレスを p と定義しているため p[0],p[1],p[2] とシンプルに書ける*/
p[0] = c.r;
p[1] = c.g;
p[2] = c.b;
return 0;
}
あとは pixel format に併せて RGB/BGR の並びを変える記述を追記
struct PixelColor {
uint8_t r, g, b;
};
int WritePixel(const FrameBufferConfig& config,
int x, int y, const PixelColor& c) {
const int pixel_position = config.pixels_per_scan_line * y + x;
if (config.pixel_format == kPixelRGBResv8BitPerColor) {
uint8_t* p = &config.frame_buffer[4 * pixel_position];
p[0] = c.r;
p[1] = c.g;
p[2] = c.b;
} else if (config.pixel_format == kPixelBGRResv8BitPerColor) {
uint8_t* p = &config.frame_buffer[4 * pixel_position];
p[0] = c.b;
p[1] = c.g;
p[2] = c.r;
} else {
return -1;
}
return 0;
}
画面一部を緑塗り
以下の白塗りの領域に一部上書きで緑塗りの領域を指定します。
//[before]
extern "C" void KernelMain(const FrameBufferConfig& frame_buffer_config) {
for (int x = 0; x < frame_buffer_config.horizontal_resolution; ++x) {
for (int y = 0; y < frame_buffer_config.vertical_resolution; ++y) {
WritePixel(frame_buffer_config, x, y, {255, 255, 255});
}
}
while (1) __asm__("hlt");
}
/***********/
//[after]
extern "C" void KernelMain(const FrameBufferConfig& frame_buffer_config) {
for (int x = 0; x < frame_buffer_config.horizontal_resolution; ++x) {
for (int y = 0; y < frame_buffer_config.vertical_resolution; ++y) {
WritePixel(frame_buffer_config, x, y, {255, 255, 255});
}
}
for (int x = 0; x < 200; ++x) {
for (int y = 0; y < 100; ++y) {
WritePixel(frame_buffer_config, 100 + x, 100 + y, {0, 255, 0});
}
}
while (1) __asm__("hlt");
}
後編はブートローダーの修正と
C++ クラスを使った main.cpp の書き換えとかボンヤリ考えてます。
(C++ 初心者だから勉強ですね、ワクワク)
make を整理するかは悩み中です。