Skip to content

04. Point

Dated: 12-04-2025

Pixel

The smallest dot illuminated that can be seen on screen.
This is also called the picture element.

Picture

Composition of pixels makes picture that forms on whole screen.

Resolution

Resolution is defined as number of rows (scan lines) from top to bottom, times, number of pixels from left to right.

Example

Mode 19 uses 200 scan lines, each with 320 pixels.
Therefore, resolution is \(320 \times 200\).

Higher resolution means more clear, pleasing image quality, alongside more memory required for that image and less staircase effect.

Text and Graphics Modes

Video cards1 are responsible to send picture data to monitor each time it refresh itself.
They support both, text and graphics modes, each having their own refresh rates, resolutions and colors.

Examples from VGA1

  • \(25 \times 80\) with \(16\) colors support (text mode).
  • \(320 \times 200\) with \(8\) colors support (graphics mode).
  • \(640 \times 480\) with \(16\) colors support (graphics mode).
  • \(640 \times 480\) with \(8, 16, 24, 32\) colors support (graphics mode).
  • \(800 \times 600\) with \(8, 16, 24, 32\) colors support (graphics mode).

Text and Graphics

The amount of memory required representing a character on screen in text mode and a pixel in graphics mode varies from mode to mode.

Mode No. Type Resolution Memory Required
\(3\) Text \(80 \times 25\) \(2\) bytes per char
\(6\) Graphics \(640 \times 200\) \(1\) bit per pixel
\(7\) Text \(80 \times 25\) \(2\) bytes per char
\(18\) Graphics \(640 \times 480\) \(1\) bit per pixel
\(19\) Graphics \(320 \times 200\) \(1\) byte per pixel

In mode \(6\), each pixel displayed onto the screen occupies \(1\) bit memory in VDU (Video Display Unit) memory. Therefore, only 2 colors can be used for this pixel.

How Text Displays

Text mode requires \(2\) bytes, first one for the ASCII value of the character to be printed and second one is the attribute byte, controlling the colors of it.
The character generator, found in VBIOS or video cards1 is responsible for translating the ASCII value into a character to be drawn on screen.

Example

  • CGA1 uses \(8\) scan lines, each with \(8\) pixels.
  • MA1 uses \(9\) scan lines, each with \(14\) pixels.

Therefore, MA1 has better resolution for characters.

The character generator for MA1 and CGA1 is located in ROM meanwhile for EGA1 and VGA,1 it is loaded into plane 2 of VRAM1 which makes it easy to load custom character sets.
Multiple character sets (\(4\) for EGA1 and \(8\) for VGA1) can be loaded at the same time.

A set of BIOS services is available for easy loading of character sets, each containing \(256\) characters.
Either one or two character sets may be active and \(1\) bit inside attribute byte determines which character set to use for the current character.

ROM-BIOS service allows us to select the active character set.
EGA1 uses \(8 \times 14\) and VGA1 uses \(9 \times 16\) resolution for each character.
Custom character sets can also be loaded using BIOS VDU services.

The advantage of graphics mode is that we can draw characters with different shapes, styles, size etc. because this mode draws very differently.

Text Mode Colors

Let's break down the attribute byte.

  • \(7\) - Blinking component
  • \(6\) - Red component of background color
  • \(5\) - Green component of background color
  • \(4\) - Blue component of background color
  • \(3\) - Intensity component of foreground color.
  • \(2\) - Red component of foreground color
  • \(1\) - Green component of foreground color
  • \(0\) - Blue component of foreground color

Graphics Mode Colors

There are few differences in comparison to text mode.

  • There is no blinking.
  • The pixel is a discrete dot of color (there is no background or foreground).
  • The way colors are generated for different video cards1 can drastically differ.

Colors in VGA1

IBM introduces VGA1 in 1987.
It has \(4\) color planes.

  • Red
  • Green
  • Blue
  • Intensity

Each is associated with \(1\) bit.

Following are the ways to write pixel on screen.

  • Using video BIOS services to write pixel.
  • Accessing memory and registers directly to write pixel on screen.
  • Using library functions to write pixel on screen.

Practical Approach to Write Pixel on Screen

Using Video BIOS Service

  • Settings desired video mode.
  • Using BIOS service to set color of a screen pixel.
  • Calling BIOS interrupt to executing pixel writing on the screen.

Source Code

asm (
    mov ah, 0   ; service number
    mov al, 13h ; mode number from 0 to 19
    int 10h     ; video bios interrupt number, sets the mode
);

Source Code for Writing Pixel

asm (
    mov ah, 0ch       ; service number
    mov al, color_num ; color number, ranging 0 - 255
    mov bh, 0         ; page number (0 is default), this mode only supports one page
    mov cx, row_num   
    mov dx, column_num 
    int 10h           ; BIOS interrupt
);

Writing Pixel by Accessing Memory Directly

Steps

  • Set video mode by using video BIOS routine.
  • Set any pointer to the video graphics memory address(0x0A0000).
  • Write color value.

Source Code

asm(
    mov ax,0a000h 
    mov ds,ax           ; segment address changed 
    mov si,10           ; column number 
    mov [si], color_num

    ; Formula: row * 320 + column in si to control position
);

Writing Character Directly on Screen

Steps

  • Use BIOS service to set text mode (mode number \(3\)).
  • Set pointer to text memory address 0x0b8000.

Source Code

asm (
    mov ax,   0b8000h 
    mov ds,   ax 
    mov si,   10      ; column number 
    mov [si], 'a'     ; character to write
);

Using Library Functions

These graphics library functions use BIOS routines or use direct memory access drivers to draw pixel on screen.

#include <graphics.h> // Borland Graphics Interface
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    int gdriver = DETECT
    int gmode;
    int errorcode;

    // Initialize graphics mode
    initgraph(&gdriver, &gmode, "");

    // Read the result of initialization
    errorcode = graphresult();
    if (errorcode != grOk) {
        // An error occurred
        printf("Graphics error: %s\n", getch());
        exit(1);
    }

    // Draw a pixel at (10, 10) with blue color
    putpixel(10, 10, BLUE);

    // Pause the screen
    getch();

    // Close the graphics mode
    closegraph();

    return 0;
}

Discussion on Pixel Drawing Methods

  • BIOS routines are built-in VGA functions but are slow.
  • Drawing shapes (triangles, rectangles, circles) using pixels via BIOS is slower than direct memory access (DMA).
  • Direct Memory Access (DMA) is faster and bypasses BIOS, but it's mainly convenient in mode 13h.
  • Library functions (provided by companies) are the easiest and fastest, as they are optimized and come with special drivers.

Drawing Pixel in Microsoft Windows

Windows does not allow access to BIOS or directory memory that easily and we have to rely on library functions (APIs) which requires knowledge of Windows GDI (Graphics Device Interface).

Source Code Example

// a.cpp : Defines the entry point for the application.

#include "stdafx.h"
#include "resource.h"
#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING];           // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];     // The window class name

// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine,
                     int nCmdShow)
{
    // TODO: Place code here.
    MSG msg;
    HACCEL hAccelTable;

    // Initialize global strings
    LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadString(hInstance, IDC_A, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance(hInstance, nCmdShow)) {
        return FALSE;
    }

    hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_A);

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0)) {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return msg.wParam;
}

// FUNCTION: MyRegisterClass()
// PURPOSE: Registers the window class.
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = (WNDPROC)WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_A);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = (LPCSTR)IDC_A;
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

    return RegisterClassEx(&wcex);
}

// FUNCTION: InitInstance()
// PURPOSE: Saves instance handle and creates main window
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    HWND hWnd;

    hInst = hInstance; // Store instance handle in our global variable

    hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
                        NULL, NULL, hInstance, NULL);

    if (!hWnd) {
        return FALSE;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return TRUE;
}

// FUNCTION: WndProc()
// PURPOSE: Processes messages for the main window.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;
    TCHAR szHello[MAX_LOADSTRING];

    LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);

    switch (message) {
    case WM_COMMAND:
        wmId = LOWORD(wParam);
        wmEvent = HIWORD(wParam);

        // Parse the menu selections:
        switch (wmId) {
        case IDM_ABOUT:
            DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
            break;
        case IDM_EXIT:
            DestroyWindow(hWnd);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        break;

    case WM_PAINT:
        {
            hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code here...
            RECT rt;
            GetClientRect(hWnd, &rt);
            int j = 0;
            // To draw some pixels of RED colour on the screen
            for (int i = 0; i < 100; i++) {
                SetPixel(hdc, i + j, 10, RGB(255, 0, 0));
                j += 6;
            }
            EndPaint(hWnd, &ps);
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// Message handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
    case WM_INITDIALOG:
        return TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
            EndDialog(hDlg, LOWORD(wParam));
            return TRUE;
        }
        break;
    }
    return FALSE;
}

References