[Updated 6-23-2025] Over a decade ago, this was written to show how to load a JPG with C++ using GDI+. However, GDI+ is effectively obsolete, and so this article will show you another, more platform independent way to load a JPG (or JPEG), PNG, BMP, TGA, HDR in a very short amount of time, and also how to modify the image data and write an image file to disk.

JPG is a complicated format, and there is no need for us to reinvent the wheel. So we’ll be using stb_image.h to read a JPG image, and stb_image_write.h to write a JPG image. This is a super helpful way to include it into your C++ program, since each is just a header file, with no library to link to, and nothing else to install.

Step 1: Go ahead and grab the two header files:

https://github.com/nothings/stb/blob/master/stb_image.h

https://github.com/nothings/stb/blob/master/stb_image_write.h

At the time of writing, it is available under two license options) MIT License, or Public Domain. So it’s basically free to use for any purpose, so big shout out to Sean Barrett for making this available to us.

Step 2: Include the files

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image.h"
#include "stb_image_write.h"

Step 3: Load the data! Yes, it’s really this simple

const char* input_filename = "input.jpg";

// Load the image
int width, height, channels;
unsigned char* data = stbi_load(input_filename, &width, &height, &channels, 0);

After doing some error checking, let’s go head and modify a pixel:

int target_x = 40;
int target_y = 100;

// Calculate the pixel index in the data array
int pixel_index = (target_y * width + target_x) * channels;

// Set the pixel to red (RGB format)
if (channels >= 3) {
    data[pixel_index] = 255;     // Red
    data[pixel_index + 1] = 0;   // Green
    data[pixel_index + 2] = 0;   // Blue
    if (channels == 4) {
        data[pixel_index + 3] = 255; // Alpha (if present)
    }
}

Step 4: Write the data out to a jpg file

int success = stbi_write_jpg(output_filename, width, height, channels, data, 90);

Step 5: After you are finished with the image data, don’t forget to free it!

// Free the image data
stbi_image_free(data);

And that’s it! You may find my full code to download here

For posterity, I will include the contents of the original tutorial here, for anyone who still wishes to use GDI+ to load image files. However, I would recommend against this route for all modern development.

ORIGINAL POST BELOW


Writing raw C++ code to read a JPG or JPEG image can be a cumbersome task. Fortunately, libraries exist which we can use to perform the dirty work for us. In this tutorial, we will be using the GDI+ interface to easily read image files and draw them onto the window. In you are unfamiliar with GDI+, I would suggest you take a look at my previous tutorial on how to get up and running with GDI+. Please note that this tutorial is aimed for Windows programs only, since GDI+ is not available on other platforms.

The two GDI+ classes we will be using are Graphics and Bitmap. The Graphics class will be used to draw the Bitmap, and the Bitmap will be used to store the image inside of our program. Go ahead and create a new Win32 application with Visual Studio, and set it up to compile with GDI+ as described in the previous tutorial. At this point, we’ll be using a global variable for our bitmap.

Step 1: Prepare the Bitmap to store the image in your program

Bitmap *gpBitmap;                                           // The bitmap for displaying an image

In the WinMain function, go ahead and initialize gpBitmap to NULL. At the end of the main WinMain function, be sure to delete the bitmap if it is not NULL to prevent memory leaks.

Step 2: Create function to let user select and load a file

We will be using the standard windows file dialog by calling GetOpenFileName(). If the user selects a file, we will simply create a Bitmap object using the filename in the constructor. Afterwards, we must invalidate the window so it will essentially be forced to redraw immediately. Please note that this program is set to use Unicode. I generally recommend that all modern programs should be written with Unicode in mind because that is the way support has been trending for the last several years.

void MyOpenFile(HWND hWnd)
{
    OPENFILENAME ofn;
    wchar_t szFileName[MAX_PATH] = L"";
    ZeroMemory(&ofn, sizeof(ofn));
    ofn.lStructSize = sizeof(ofn);
    ofn.hwndOwner = hWnd;
    ofn.lpstrFilter = L"JPEG Files (*.jpg)\0*.jpg\0All Files (*.*)\0*.*\0";
    ofn.lpstrFile = szFileName;
    ofn.nMaxFile = MAX_PATH;
    ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
    ofn.lpstrDefExt = L"jpg";
    if(GetOpenFileName(&ofn))
    {
		// If we got this far, the user has chosen a file.
		// GDI+ supports BMP, GIF, JPEG, PNG, TIFF, Exif, WMF, and EMF.
		if(gpBitmap)
		{
			// We need to destroy the old bitmap
			delete gpBitmap;
		}
		gpBitmap = new Bitmap(szFileName);
		// We need to force the window to redraw itself
		InvalidateRect(hWnd, NULL, TRUE);
		UpdateWindow(hWnd);
    }
}

Step 3: Create function to draw the bitmap to the screen

Drawing a bitmap to the screen, or a DC (device context) is simple when using the GDI+ API. We can simply create the graphics object using the handle to our device context. Then we can use the DrawImage member function of our graphics object to draw the bitmap. Please keep in mind that the Bitmap class is derived from the Image class in GDI+.

void MyOnPaint(HDC hdc)
{
	if (gpBitmap)
	{
		Graphics graphics(hdc);
		graphics.DrawImage(gpBitmap, 0, 0);
	}
}

Summary

This tutorial quickly shows you how to use a standard open file dialog to let the user select, and load an image file. The image file can be a JPG, BMP, TIFF, or several other popular formats. Loading the image file is essentially done in one line, since one of the Bitmap constructor functions accepts a filename as the only argument, which makes this process very easy.

Download the source code here