Font drawing issues

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
OmeletHopper
Posts: 4
Joined: Mon Jun 04, 2018 6:37 am

Font drawing issues

Post by OmeletHopper »

Hello all, I'm fairly new to OSDevving. I have been trying to draw characters in a video mode set by grub, 1024x768x16. I can write text, and draw and stuff, but my problem is that my font characters have weird color issues. I know it's between my Font Data Lookup Table (that I got from the wiki), and my font drawing function (that I also originally got from the wiki).

The lookup table:

Code: Select all

unsigned int FontDataTable[] = {0x00000000, 0x000000FF, 0x0000FF00, 0x0000FFFF,
                                0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00FFFFFF,
                                0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF,
                                0xFFFF0000, 0xFFFF00FF, 0xFFFFFF00, 0xFFFFFFFF};
And the function:

Code: Select all

void kernelDrawChar(unsigned int Character, unsigned char Foreground, unsigned char Background) {
  char *Where = &Framebuffer[FramebufferPosition];
  unsigned char RowData;
  unsigned int Mask;
  unsigned char *CharacterBMP = &Font[(Character)*16];

  unsigned int PackedForeground =
      (Foreground << 24) | (Foreground << 16) | (Foreground << 8) | Foreground;
  unsigned int PackedBackground =
      (Background << 24) | (Background << 16) | (Background << 8) | Background;

  for (int Row = 0; Row < FontHeight; Row++) {
    RowData = CharacterBMP[Row];
    Mask = FontDataTable[RowData >> 16] | FontDataTable[RowData & 0x0F];
    *(unsigned int *)Where =
        (PackedForeground & Mask) | (PackedBackground & ~Mask);
    RowData = CharacterBMP[Row] >> 4;
    Mask = FontDataTable[RowData >> 16] | FontDataTable[RowData & 0x0F];
    *(unsigned int *)&Where[4] =
        (PackedForeground & Mask) | (PackedBackground & ~Mask);

    Where += FramebufferPitch;
  }
  FramebufferPosition += FontWidth;
}
There is a screenshot of my kernel running under QEMU attached. The panic shown is just a test.

It's also probably worth noting I'm using the font from Apple's Darwin Kernel. https://github.com/apple/darwin-xnu/blo ... iso_font.c

I haven't ever done anything like this, so it's all a very new fun learning experience.
I've been trying to fix this for a while because to me it seems like it would be easy to fix, but I just can't solve it. I apologize if it's something simple or if my code is just all wrong :) .

Thanks in advance!
Attachments
Screen Shot 2018-09-04 at 7.33.51 PM.png
Screen Shot 2018-09-04 at 7.33.51 PM.png (25.13 KiB) Viewed 1719 times
"You’re free to praise Microsoft Windows on your own if you want, but please don’t do this in GNU packages." - GNU C Portability Standards
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Font drawing issues

Post by Brendan »

Hi,
OmeletHopper wrote:Hello all, I'm fairly new to OSDevving. I have been trying to draw characters in a video mode set by grub, 1024x768x16. I can write text, and draw and stuff, but my problem is that my font characters have weird color issues. I know it's between my Font Data Lookup Table (that I got from the wiki), and my font drawing function (that I also originally got from the wiki).
The example code in the wiki is designed for 8 bits per pixel; but you're not using 8 bits per pixel - you're using "1024x768x16".

I'm not entirely sure if you're using "16 colours, 4 bits per pixel" or "65536 colours, 16 bits per pixel". For 16 colour modes the frame buffer is extremely different (arranged as four monochrome planes) and the example code in the wiki wouldn't make sense. For 16 bits per pixel there's a lot that would need to be changed (starting with the function's parameters - "unsigned char" isn't large enough for a 16-bit colour).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Font drawing issues

Post by Brendan »

Hi again,

I was looking at the code on the wiki and found a bug (a ">> 16" that should have been a "/ 16" or ">> 4"), but this wouldn't effect your main problem.

Assuming you're using "65536 colour, 16 bits per pixel" the code might look something like:

Code: Select all

uint64_t font_data_lookup_table[16] = {
    0x0000000000000000UL,
    0x000000000000FFFFUL,
    0x00000000FFFF0000UL,
    0x00000000FFFFFFFFUL,
    0x0000FFFF00000000UL,
    0x0000FFFF0000FFFFUL,
    0x0000FFFFFFFF0000UL,
    0x0000FFFFFFFFFFFFUL,
    0xFFFF000000000000UL,
    0xFFFF00000000FFFFUL,
    0xFFFF0000FFFF0000UL,
    0xFFFF0000FFFFFFFFUL,
    0xFFFFFFFF00000000UL,
    0xFFFFFFFF0000FFFFUL,
    0xFFFFFFFFFFFF0000UL,
    0xFFFFFFFFFFFFFFFFUL
}

Code: Select all

void draw_char(uint8_t *where, uint32_t character, uint_fast16_t foreground_colour, uint_fast16_t background_colour) {
    int row;
    uint8_t row_data;
    uint64_t mask1, mask2;
    uint8_t *font_data_for_char = &system_font_data_address[character * 8];
    uint64_t packed_foreground = (foreground << 48) | (foreground << 32) | (foreground << 16) | foreground;
    uint64_t packed_background = (background << 48) | (background << 32) | (background << 16) | background;
 
    for (row = 0; row < 8; row++) {
        row_data = font_data_for_char[row];
        mask1 = font_data_lookup_table[row_data / 16];
        mask2 = font_data_lookup_table[row_data & 0x0F];
        *(uint64_t *)where = (packed_foreground & mask1) | (packed_background & ~mask1);
        *(uint64_t *)(&where[8]) = (packed_foreground & mask2) | (packed_background & ~mask2);
        where += bytes_per_line;
    }
}

Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
OmeletHopper
Posts: 4
Joined: Mon Jun 04, 2018 6:37 am

Re: Font drawing issues

Post by OmeletHopper »

Brendan,

I've got to thank you for your fast reply. The different font lookup table you posted seems too be working great! However, the function you posted I kept having issues with. I made a few modifications to my existing function, and you can see what I've got from the screenshot. Seriously, thanks!
I'm not entirely sure if you're using "16 colours, 4 bits per pixel" or "65536 colours, 16 bits per pixel". For 16 colour modes the frame buffer is extremely different (arranged as four monochrome planes) and the example code in the wiki wouldn't make sense.
I honestly don't know which one I'm using. The function only went to a black screen, and after playing around with it I only got garbled colors. I've uploaded a screenshot of this.

Here is my modified function and it's output:

Code: Select all

void kernelDrawChar(unsigned int Character, char Foreground, int Background) {
  char *Where = &Framebuffer[FramebufferPosition];
  unsigned char RowData;
  unsigned int Mask;
  unsigned char *CharacterBMP = &Font[(Character)*16];

  unsigned int PackedForeground =
      (Foreground << 24) | (Foreground << 16) | (Foreground << 8) | Foreground;
  unsigned int PackedBackground =
      (Background << 24) | (Background << 16) | (Background << 8) | Background;

  for (int Row = 0; Row < FontHeight; Row++) {
    RowData = CharacterBMP[Row];
    Mask = FontDataTable[RowData & 0x0F];
    *(unsigned int *)Where =
        (PackedForeground & Mask) | (PackedBackground & ~Mask);
    Mask = FontDataTable[RowData / 16];
    *(unsigned int *)&Where[4] =
        (PackedForeground & Mask) | (PackedBackground & ~Mask);
    Where += FramebufferPitch;
  }
  FramebufferPosition += FontWidth;
}
Screen Shot 2018-09-07 at 5.30.17 PM.png
Screen Shot 2018-09-07 at 5.30.17 PM.png (9.99 KiB) Viewed 1644 times
And here is the modified version of the one you posted and it's output:

Code: Select all

void kernelDrawChar(uint32_t character, uint16_t foreground, uint16_t background) {
    char *where = &Framebuffer[FramebufferPosition];
    int row;
    uint8_t row_data;
    uint64_t mask1, mask2;
    uint8_t *font_data_for_char = &Font[character * 8];
    uint64_t packed_foreground = (foreground << 24) | (foreground << 16) | (foreground << 8) | foreground;
    uint64_t packed_background = (background << 24) | (background << 16) | (background << 8) | background;

    for (row = 0; row < 16; row++) {
        row_data = font_data_for_char[row];
        mask1 = FontDataTable[row_data / 16];
        mask2 = FontDataTable[row_data & 0x0F];
        *(uint64_t *)where = (packed_foreground & mask1) | (packed_background & ~mask1);
        *(uint64_t *)(&where[8]) = (packed_foreground & mask2) | (packed_background & ~mask2);
        where += FramebufferPitch;
    }
    FramebufferPosition += FontWidth;
}
Screen Shot 2018-09-07 at 5.37.54 PM.png
What is causing these other problems with my function? Is it just that yours is the correct one and I'm doing something wrong?
I am aware that I swapped the Mask assignments in mine, this makes it draw the font correctly. Is this part of it?
I will say the new font lookup table you've provided definitely is better.
Thanks!
"You’re free to praise Microsoft Windows on your own if you want, but please don’t do this in GNU packages." - GNU C Portability Standards
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Font drawing issues

Post by Brendan »

Hi,
OmeletHopper wrote:I've got to thank you for your fast reply. The different font lookup table you posted seems too be working great! However, the function you posted I kept having issues with. I made a few modifications to my existing function, and you can see what I've got from the screenshot. Seriously, thanks!
I'm not entirely sure if you're using "16 colours, 4 bits per pixel" or "65536 colours, 16 bits per pixel". For 16 colour modes the frame buffer is extremely different (arranged as four monochrome planes) and the example code in the wiki wouldn't make sense.
I honestly don't know which one I'm using.
You can't write correct code without knowing how the video card expects a pixel to be represented. How a pixel is represented includes its size (how many bits) it's location in the frame buffer (e.g. planar vs. "packed pixel") and what each bit means (e.g. if it's an index into the palette or if it's a direct RGB value, and if it is a direct RGB value which bits represent what - e.g. if the 16 bits are "rrrrrggggggbbbbb" or "bbbbbggggggrrrrr" or something else).

Trying to work backwards from the pictures, there's a few things that I've noticed.

First; both of the pieces of code you've posted have almost exactly the same calculation controlling where pixel data is stored. Specifically, these lines are identical in both versions:

Code: Select all

void kernelDrawChar(uint32_t character, uint16_t foreground, uint16_t background) {
    char *where = &Framebuffer[FramebufferPosition];

    for (row = 0; row < 16; row++) {

        *(uint64_t *)where = ...

        where += FramebufferPitch;
    }
    FramebufferPosition += FontWidth;
}
However, despite using exactly the same calculation for "where", the screenshots/pictures are showing that both versions are writing data in completely different places on the screen. That doesn't make any sense (unless there's bug/s or differences somewhere else).

Second, for the first picture (I opened it in GIMP and blew it up to take a close look) characters are 5 pixels wide and look like several columns of pixels are being skipped in each character. That indicates something is wrong with the calculation of "where". Maybe "FontWidth" is 5 when it should be 8; and maybe "FontWidth" is 8 but "Framebuffer" is an array of bytes or a pointer to bytes (and not an array of "uint16_t" or pointer to "uint16_t") and you're doing "FramebufferPosition += FontWidth;" when you should be doing "FramebufferPosition += FontWidth * bytes_per_pixel;", and then doing an extra "FramebufferPosition += 2;" somewhere else (in the code that calls your "kernelDrawChar()" function?) to make it add up to 10 bytes (or 5 pixels).

Third; if you are using a "2 bytes per pixel" video mode (either 15 bits per pixel or 16 bits per pixel) there's multiple other things wrong (e.g. the shift counts in "uint64_t packed_foreground = (foreground << 24) | (foreground << 16) | (foreground << 8) | foreground;").

Based on all of this (and that changing the "font_data_lookup_table" to suit 16 bits per pixel got rid of the blue/yellow colours you were getting before); I'd say that it is a 16 bit per pixel video mode, and while you were trying to fix symptoms of the "FramebufferPosition += FontWidth" bug you made everything else wrong for 16 bits per pixel (and "more right" for the 8 bits per pixel code you started with that at least gave you something that vaguely looked like characters before).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Post Reply