[Win32 C/C++/C#] Drawing text on bitmap and save bitmap to file example code

tom_mai78101

The Helper Connoisseur / Ex-MineCraft Host
Staff member
Reaction score
1,677
This code demonstrates the ability to use Win32 API to draw or print text to a bitmap, and then save the bitmap to a file.

Note:
  • Text is pink for testing purposes.
  • Text font uses the System Font.
  • This is merely a demonstration (proof of concept), and may not be suitable for "best practices".
  • There are many things that can be improved, such as fixing the brief moment where the text would appear on the screen.
  • This program generates a bitmap, akin to doing a Print Screen, which captures the entire screen displayed on your primary monitor.
  • The example code is self-documenting, which is not the best coding practice anyone should follow.
  • The bitmap file, saved after executing the program, is expected to be no less than 8MB at a 1080p resolution.
  • This example code is used for archival purposes, to show that printing text is possible using pure Win32 API in C / C++.
  • There are no AFX or Object-Oriented Win32 stuffs in this code. This is pure Win32 code in C, with very little C++ code in it.

Code:
#include <Windows.h>
#include <iostream>
#include <fstream>

HBITMAP CaptureScreen(HDC currentDeviceContext, std::string text) {
    int width = GetSystemMetrics(SM_CXSCREEN);
    int height = GetSystemMetrics(SM_CYSCREEN);

    HDC compatibleDeviceContext = CreateCompatibleDC(currentDeviceContext);
    HBITMAP bitmapHandle = CreateCompatibleBitmap(currentDeviceContext, width, height);
    HGDIOBJ previousSelectedHandle = SelectObject(compatibleDeviceContext, bitmapHandle);

    BOOL result = BitBlt(compatibleDeviceContext, 0, 0, width, height, currentDeviceContext, 0, 0, SRCCOPY | CAPTUREBLT);
    if (!result) {
        MessageBox(nullptr, TEXT("BitBlt() fails 1."), TEXT("Error"), MB_OK);
        return nullptr;
    }

    HFONT font = (HFONT)GetStockObject(SYSTEM_FONT);
    SetTextColor(compatibleDeviceContext, RGB(255, 0, 255));
    SetBkMode(compatibleDeviceContext, TRANSPARENT);
    HFONT previousFont = (HFONT)SelectObject(compatibleDeviceContext, font);
    TextOut(compatibleDeviceContext, 240, 360, TEXT(text.c_str()), text.size());

    result = BitBlt(currentDeviceContext, 0, 0, width, height, compatibleDeviceContext, 0, 0, SRCCOPY);
    if (!result) {
        MessageBox(nullptr, TEXT("BitBlt() fails 2."), TEXT("Error"), MB_OK);
        return nullptr;
    }

    SelectObject(compatibleDeviceContext, previousFont);
    SelectObject(compatibleDeviceContext, previousSelectedHandle);
    DeleteDC(compatibleDeviceContext);
    return bitmapHandle;
}

void SaveFile(BYTE* pixels, BITMAPINFOHEADER& bitmapInfoHeader) {
    HANDLE fileHandle = CreateFile(TEXT("Test.bmp"), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (fileHandle == INVALID_HANDLE_VALUE) {
        MessageBox(nullptr, TEXT("CreateFile() failed."), TEXT("Error"), MB_OK);
        return;
    }
    BITMAPFILEHEADER bitmapFileHeader = { 0 };
    bitmapFileHeader.bfType = 0x4d42;
    bitmapFileHeader.bfSize = sizeof(bitmapFileHeader) + bitmapInfoHeader.biSize + bitmapInfoHeader.biClrUsed * sizeof(RGBQUAD) + bitmapInfoHeader.biSizeImage;
    bitmapFileHeader.bfReserved1 = bitmapFileHeader.bfReserved2 = 0;
    bitmapFileHeader.bfOffBits = sizeof(bitmapFileHeader) + bitmapInfoHeader.biSize + bitmapInfoHeader.biClrUsed * sizeof(RGBQUAD);

    DWORD tempDword;
    if (!WriteFile(fileHandle, (LPVOID)&bitmapFileHeader, sizeof(bitmapFileHeader), (LPDWORD)&tempDword, nullptr)) {
        MessageBox(nullptr, TEXT("WriteFile() failed 1."), TEXT("Error"), MB_OK);
        return;
    }

    if (!WriteFile(fileHandle, (LPVOID) &bitmapInfoHeader, sizeof(bitmapInfoHeader) + bitmapInfoHeader.biClrUsed * sizeof(RGBQUAD), (LPDWORD) &tempDword, nullptr)) {
        MessageBox(nullptr, TEXT("WriteFile() failed 2."), TEXT("Error"), MB_OK);
        return;
    }

    if (!WriteFile(fileHandle, (LPVOID) pixels, (int)bitmapInfoHeader.biSizeImage, (LPDWORD) &tempDword, nullptr)) {
        MessageBox(nullptr, TEXT("WriteFile() failed 3."), TEXT("Error"), MB_OK);
        return;
    }

    if (!CloseHandle(fileHandle)) {
        MessageBox(nullptr, TEXT("CloseHandle() failed."), TEXT("Error"), MB_OK);
        return;
    }
}

int main() {
    HDC deviceContext = GetDC(nullptr);
    HBITMAP capturedBitmap = CaptureScreen(deviceContext, std::string("Hello world."));

    BITMAPINFO bitmapInfo = { 0 };
    bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo.bmiHeader);

    BOOL result = GetDIBits(deviceContext, capturedBitmap, 0, 0, nullptr, &bitmapInfo, DIB_RGB_COLORS);
    if (!result) {
        MessageBox(nullptr, TEXT("GetDIBits() failed."), TEXT("Error"), MB_OK);
        return -1;
    }

    BYTE* pixels = new BYTE[bitmapInfo.bmiHeader.biSizeImage];
    bitmapInfo.bmiHeader.biCompression = BI_RGB;
    result = GetDIBits(deviceContext, capturedBitmap, 0, bitmapInfo.bmiHeader.biHeight, (LPVOID)pixels, &bitmapInfo, DIB_RGB_COLORS);
    if (!result) {
        MessageBox(nullptr, TEXT("GetDIBits(), second function, failed."), TEXT("Error"), MB_OK);
        return -2;
    }

    SaveFile(pixels, bitmapInfo.bmiHeader);

    DeleteObject(capturedBitmap);
    ReleaseDC(nullptr, deviceContext);
    delete[] pixels;
    return 0;
}
 
Last edited:

tom_mai78101

The Helper Connoisseur / Ex-MineCraft Host
Staff member
Reaction score
1,677
The equivalent in C#, except it can:
  • Draw using rectangles with rounded corners, and a solid colored background.
  • Can scale text up or down, depending on how big you want it to be in pixels (px).
  • Can set the canvas size (bitmap size).
  • Can save bitmap to a PNG file.
  • Will always save to Desktop in any operating system.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading.Tasks;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace csharp_test {
    public static class Extensions {
        public static GraphicsPath RoundedRect(Rectangle bounds, int radius) {
            int diameter = radius * 2;
            Size size = new Size(diameter, diameter);
            Rectangle arc = new Rectangle(bounds.Location, size);
            GraphicsPath path = new GraphicsPath();
            if (radius == 0) {
                path.AddRectangle(bounds);
                return path;
            }
            path.AddArc(arc, 180, 90);
            arc.X = bounds.Right - diameter;
            path.AddArc(arc, 270, 90);
            arc.Y = bounds.Bottom - diameter;
            path.AddArc(arc, 0, 90);
            arc.X = bounds.Left;
            path.AddArc(arc, 90, 90);
            path.CloseFigure();
            return path;

        }

        public static void DrawRoundedRectangle(this Graphics graphics, Pen pen, Rectangle bounds, int cornerRadius) {
            if (graphics == null)
                throw new ArgumentNullException("graphics");
            if (pen == null)
                throw new ArgumentNullException("pen");
            using (GraphicsPath path = RoundedRect(bounds, cornerRadius)) {
                graphics.DrawPath(pen, path);
            }
        }

        public static void FillRoundedRectangle(this Graphics graphics, Brush brush, Rectangle bounds, int cornerRadius) {
            if (graphics == null)
                throw new ArgumentNullException("graphics");
            if (brush == null)
                throw new ArgumentNullException("brush");
            using (GraphicsPath path = RoundedRect(bounds, cornerRadius)) {
                graphics.FillPath(brush, path);
            }
        }
    }

    class Program {
        static void Main(string[] args) {
            int bitmapWidth = 100;
            int bitmapHeight = 100;
            int fontSize = 6;
            string firstText = "This is Hello world.";
            string secondText = "By tom_mai78101";
            string desktopFilePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            string imageFilePath = desktopFilePath + @"\test.png";
            PointF firstLocation = new PointF(10f, 10f);
            PointF secondLocation = new PointF(10f, 50f);
            Bitmap bitmap = new Bitmap(bitmapWidth, bitmapHeight);
            using (Graphics graphics = Graphics.FromImage(bitmap)) {
                using (Font arialFont = new Font("Arial", fontSize)) {
                    Brush brush = new SolidBrush(Color.FromArgb(128, 255, 128));
                    graphics.FillRoundedRectangle(brush, new Rectangle(0, 0, bitmapWidth, bitmapHeight), 24);
                    graphics.DrawString(firstText, arialFont, Brushes.Blue, firstLocation);
                    graphics.DrawString(secondText, arialFont, Brushes.Blue, secondLocation);
                }
            }
            bitmap.Save(imageFilePath, ImageFormat.Png);
        }
    }
}
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top