Freeimage
1. What is FreeImage
FreeImage is an open-source, cross-platform C/C++ library for loading, saving, and lightly processing images. Originally started by Hervé Drolon, it focuses on developer-friendly multi-format image I/O, providing a uniform API across 30+ formats including PNG, JPEG, TIFF, BMP, GIF, JPEG 2000, RAW, and HDR.
The latest stable release is 3.18.0 (2018; the community still publishes patches). This project bundles the official binaries under pub/external/freeimage/ with 32-bit / 64-bit DLLs, import libs, and headers.
Typical use cases:
- Desktop image loading / thumbnail / format conversion tools.
- TWAIN / scanner projects (this is exactly what this project does).
- Texture preprocessing pipelines for games / 3D engines.
- Lightweight front-ends for scientific / HDR imaging.
2. Features
2.1 Formats
Reads and writes (some are read-only):
- Raster: BMP, PNG, JPEG, JPEG 2000, GIF, TIFF, TARGA, PCX, PSD, ICO, WebP, etc.
- Vector / metafile: WBMP, XBM, XPM, PFM.
- HDR / float: HDR (Radiance), EXR (OpenEXR), TIFF Float.
- Camera RAW: CR2, NEF, ARW, DNG, etc. (via LibRaw / dcraw).
- Misc: JBIG, KOALA, IFF, SGI, PICT, PNM, ...
About 30+ formats total.
2.2 Pixel / bit depth
- 1, 4, 8 bpp (palette / grayscale).
- 16 bpp (555 / 565 / gray / float).
- 24, 32 bpp (BGR / BGRA).
- 48, 64 bpp (16-bit RGB / RGBA).
- Floating point (32 / 96 / 128 bpp).
Logical types (FREE_IMAGE_TYPE): FIT_BITMAP, FIT_UINT16, FIT_INT16, FIT_UINT32, FIT_INT32, FIT_FLOAT, FIT_DOUBLE, FIT_COMPLEX, FIT_RGB16, FIT_RGBA16, FIT_RGBF, FIT_RGBAF.
2.3 Image processing
Not a full image-processing library, but it ships useful primitives:
- Geometry:
FreeImage_Rescale(multiple filters),FreeImage_Rotate,FreeImage_FlipHorizontal/Vertical,FreeImage_Copy,FreeImage_Paste. - Color:
FreeImage_ConvertTo24Bits,FreeImage_ConvertTo32Bits,FreeImage_ConvertToGreyscale,FreeImage_ConvertToRGBF. - Thresholding / dithering:
FreeImage_Threshold,FreeImage_Dither(Floyd-Steinberg, Bayer, ...). - Color adjustment:
AdjustGamma / Brightness / Contrast,Invert. - Channels:
GetChannel / SetChannel. - Palette:
GetPalette / SetPalette. - DPI metadata:
GetDotsPerMeterX/Y,SetDotsPerMeterX/Y. - EXIF / IPTC / XMP / GeoTIFF metadata.
- Multi-page TIFF / GIF / ICO.
- Memory I/O via
FIMEMORY(no filesystem required).
2.4 Resampling filters
| Filter | Notes |
|---|---|
FILTER_BOX |
Fastest, lowest quality |
FILTER_BILINEAR |
Smooth but blurry |
FILTER_BICUBIC |
General-purpose, slightly sharper |
FILTER_BSPLINE |
Smooth, low ringing |
FILTER_CATMULLROM |
Sharp |
FILTER_LANCZOS3 |
Highest quality, possible ringing |
This project uses FILTER_LANCZOS3 for DPI rescaling (see docs/implement_dpi_design.md §4.3).
2.5 Threading
FreeImage has no built-in threading. The API is re-entrant as long as different threads operate on different FIBITMAP instances; concurrent access to the same FIBITMAP must be synchronized by the caller.
3. Characteristics
- Lightweight: ~3 MB DLL with all main decoders; trimmable.
- Cross-platform: Windows, Linux, macOS, FreeBSD, Solaris.
- C-style API:
FIBITMAP*handle +FreeImage_*functions; easy bindings. - C++ wrapper (
FreeImagePlus) is available but rarely used. - Plugin architecture: formats registered as
FREE_IMAGE_FORMATplugins, custom formats can be added. - Uniform palette model: indexed images all expose RGBQUAD palettes.
- Full metadata (EXIF / IPTC / XMP / Animation / GeoTIFF).
- Memory I/O through
FIMEMORYfor streaming / network use.
4. Comparison
4.1 Candidates
| Library | Form | Use |
|---|---|---|
| FreeImage | C library + DLL | Multi-format I/O + light processing |
| libpng / libjpeg / libtiff | per-format C libs | Single-format specialists |
| stb_image | header-only C | Minimal loading |
| OpenCV | large C++ lib | CV + image processing |
| ImageMagick / GraphicsMagick | C/C++ + CLI | Full processing suite |
| WIC | COM | Native Windows imaging |
| Pillow (PIL) | Python | Python ecosystem |
| Skia | C++ | 2D rendering (Chrome) |
| DevIL | C lib | Multi-format I/O |
4.2 Feature matrix
| Aspect | FreeImage | OpenCV | stb_image | libpng+jpeg+tiff | ImageMagick | WIC |
|---|---|---|---|---|---|---|
| Formats | 30+ | tens | 6 (load only) | 1 each | 100+ | 10+ |
| Multi-page (TIFF/GIF) | yes | partial | no | TIFF yes | yes | yes |
| HDR / float | yes | yes | partial | TIFF float | yes | partial |
| Metadata (EXIF/XMP/IPTC) | yes | limited | no | no | yes | yes |
| Cross-platform | yes | yes | yes | yes | yes | Windows only |
| API complexity | medium | high | very low | medium | high | high (COM) |
| Binary size | ~3 MB | tens of MB | 0 (header) | ~1 MB each | ~30 MB | system |
| Commercial-friendly | nuanced (§5) | yes (Apache 2.0) | yes (Public Domain / MIT) | yes (each permissive) | yes | yes (system) |
| Processing | basic | rich (CV) | none | none | rich | basic |
| Active maintenance | low (patches since 2018) | high | medium | high | high | high |
4.3 Choosing for a scanner project
- Only need a few major formats:
stb_imageis simplest. - Windows-only, OK with COM: WIC is fastest and dependency-free.
- Need CV / thresholding / edge detection: OpenCV, but heavy.
- Need many formats + basic processing + cross-platform + small DLL: FreeImage is the best balance — that's why this project uses it.
- Need 100% safe commercial story: hand-pick libpng + libjpeg-turbo + libtiff (see §5).
4.4 Performance
- Per-image load/save: comparable to libpng / libjpeg (FreeImage simply wraps them).
- Resampling:
LANCZOS3is noticeably slower than OpenCV's SIMD; fine for one-image-per-scan workloads. - No internal threading; OpenCV uses TBB / OpenMP internally.
5. License analysis
5.1 Dual license
FreeImage is dual-licensed:
- FreeImage Public License (FIPL), based on Mozilla Public License 1.1 (MPL 1.1).
- GPL v2 or v3.
Pick either.
5.2 FIPL highlights
MPL 1.1 is a file-level copyleft:
- Modifications to FreeImage source files must be published.
- Applications linking FreeImage may stay closed-source.
- Distribute the FIPL license notice with your product.
- No endorsement using contributors' names.
Bottom line: using FreeImage without modifying its source allows closed-source commercial use, provided you carry the notice.
5.3 GPL path
Choosing GPL forces your entire application to be GPL-compatible with full source disclosure. Almost no closed-source commercial product picks this path.
5.4 Internal dependencies
FreeImage statically embeds several decoder libraries; these are where most commercial-license risk lives:
| Component | License | Risk |
|---|---|---|
| libpng | libpng (BSD-like) | low |
| libjpeg / libjpeg-turbo | IJG / BSD | low |
| libtiff | BSD-like | low |
| zlib | zlib | low |
| OpenJPEG | BSD 2-clause | low |
| LibRaw | LGPL 2.1 / CDDL / commercial | medium (LGPL implies dynamic-linking duties) |
| OpenEXR | BSD 3-clause | low |
| LibWebP | BSD-like | low |
| jxrlib | BSD-like | low |
The notable concern is LibRaw: linking it statically may impose LGPL obligations (provide object files for relinking, or link dynamically). If RAW support is not required, build FreeImage from source with LibRaw disabled.
5.5 Commercial checklist
- Keep notices: include the FIPL text and FreeImage copyright in About / docs / LICENSE.
This software uses the FreeImage open source image library. See http://freeimage.sourceforge.net for details. FreeImage is used under the FreeImage Public License (FIPL), version 1.0. - Do not modify FreeImage source if you want to skip publishing changes.
- Publish patches if you modify FreeImage.
- Decide on LibRaw: disable it if you do not need RAW decoding.
- Ship as a DLL: keep FreeImage as a clearly separated dynamic library to avoid boundary debates.
- Track updates: FreeImage has slowed since 2018; maintain a private fork for CVE follow-ups if compliance is strict.
5.6 Common misconceptions
- "FreeImage is free so it's safe commercially" — true if you respect the notice and do not modify source.
- "I can static-link it into my EXE" — technically possible, but file-level copyleft interpretation is contested; this project uses dynamic linking + notice for safety.
- "GPL is safer" — no, GPL forces source disclosure of your whole product.
6. Usage in this project
The official prebuilt DLLs live in pub/external/freeimage/. CMakeLists.txt wires them via find_library / target_link_libraries. At runtime FreeImage.dll ships next to the .ds file (see README "Installed files").
Key call sites:
- Load source image:
FreeImage_GetFileTypeU+FreeImage_LoadU. - Color / depth:
FreeImage_ConvertTo24Bits/FreeImage_ConvertToGreyscale/FreeImage_Threshold. - Resize:
FreeImage_Rescale(..., FILTER_LANCZOS3). - DPI metadata:
FreeImage_SetDotsPerMeterX / Y. - Save:
FreeImage_Save(FIF_PNG | FIF_JPEG | FIF_BMP | FIF_TIFF, ...). - Native Transfer DIB:
FreeImage_GetWidth / GetHeight / GetBPP / GetScanLine / GetPalettefeed directly intoBITMAPINFOHEADER.
7. Code samples
Minimal runnable snippets; production code should add full error-handling and resource release.
7.1 Init / shutdown
#include <FreeImage.h>
int main() {
// Static build needs explicit init; DLL version auto-inits at load.
// FreeImage_Initialise();
// ... work ...
// FreeImage_DeInitialise();
return 0;
}
7.2 Load an image
FIBITMAP* LoadImage(const wchar_t* path) {
FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeU(path, 0);
if (fif == FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilenameU(path);
if (fif == FIF_UNKNOWN || !FreeImage_FIFSupportsReading(fif)) return nullptr;
return FreeImage_LoadU(fif, path, 0); // caller frees via FreeImage_Unload
}
7.3 Upscale 2x (Lanczos3)
FIBITMAP* RescaleTwoTimes(FIBITMAP* src) {
if (!src) return nullptr;
int w = FreeImage_GetWidth(src);
int h = FreeImage_GetHeight(src);
return FreeImage_Rescale(src, w * 2, h * 2, FILTER_LANCZOS3);
}
7.4 Save an image
bool SaveImage(FIBITMAP* bmp, const wchar_t* path, FREE_IMAGE_FORMAT fif) {
if (!bmp) return false;
int flags = 0;
if (fif == FIF_JPEG) flags = JPEG_QUALITYGOOD; // ~85
if (fif == FIF_TIFF) flags = TIFF_LZW;
if (fif == FIF_PNG) flags = PNG_DEFAULT;
return FreeImage_SaveU(fif, bmp, path, flags) == TRUE;
}
7.5 Full example: BMP → PNG with 2x upscale and DPI metadata
#include <FreeImage.h>
#include <cstdio>
int wmain(int argc, wchar_t** argv) {
if (argc < 3) {
std::wprintf(L"usage: bmp2png <in.bmp> <out.png>\n");
return 1;
}
const wchar_t* in_path = argv[1];
const wchar_t* out_path = argv[2];
// 1. Load BMP. FreeImage detects format by signature; extension is a hint.
FREE_IMAGE_FORMAT in_fif = FreeImage_GetFileTypeU(in_path, 0);
if (in_fif == FIF_UNKNOWN) in_fif = FreeImage_GetFIFFromFilenameU(in_path);
FIBITMAP* src = FreeImage_LoadU(in_fif, in_path, 0);
if (!src) { std::wprintf(L"failed to load: %ls\n", in_path); return 2; }
// 2. Normalize to 24-bit BGR to avoid surprises after rescaling.
FIBITMAP* rgb24 = FreeImage_ConvertTo24Bits(src);
FreeImage_Unload(src);
if (!rgb24) { std::wprintf(L"convert to 24-bit failed\n"); return 3; }
// 3. Upscale 2x with Lanczos3.
int src_w = FreeImage_GetWidth(rgb24);
int src_h = FreeImage_GetHeight(rgb24);
FIBITMAP* scaled = FreeImage_Rescale(rgb24, src_w * 2, src_h * 2,
FILTER_LANCZOS3);
FreeImage_Unload(rgb24);
if (!scaled) { std::wprintf(L"rescale failed\n"); return 4; }
// 4. Write DPI metadata: 300 DPI ~= 300 * 39.37 pixels per meter.
unsigned ppm = static_cast<unsigned>(300.0 * 39.37);
FreeImage_SetDotsPerMeterX(scaled, ppm);
FreeImage_SetDotsPerMeterY(scaled, ppm);
// 5. Save as PNG.
BOOL ok = FreeImage_SaveU(FIF_PNG, scaled, out_path, PNG_DEFAULT);
FreeImage_Unload(scaled);
if (!ok) { std::wprintf(L"failed to save: %ls\n", out_path); return 5; }
std::wprintf(L"ok: %ls -> %ls (2x, 300dpi)\n", in_path, out_path);
return 0;
}
Build (MSVC):
cl /EHsc /W4 bmp2png.cpp /link FreeImage.lib
bmp2png in.bmp out.png
The resulting out.png is the input BMP at 2x size with 300 DPI metadata.
7.6 Memory I/O variant (no filesystem)
FIMEMORY* SaveToMemory(FIBITMAP* bmp, FREE_IMAGE_FORMAT fif) {
FIMEMORY* mem = FreeImage_OpenMemory();
FreeImage_SaveToMemory(fif, bmp, mem, 0);
return mem; // caller frees with FreeImage_CloseMemory
}
void ReadFromMemory(BYTE* buffer, DWORD size) {
FIMEMORY* mem = FreeImage_OpenMemory(buffer, size);
FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(mem, 0);
FIBITMAP* bmp = FreeImage_LoadFromMemory(fif, mem, 0);
// ...
FreeImage_Unload(bmp);
FreeImage_CloseMemory(mem);
}
Useful for HTTP uploads / database BLOBs.
8. Summary
- Pros: clean API, many formats, small binary, cross-platform, near-zero ceremony for scanner-style projects.
- Cons: slow release cadence, no SIMD filters, commercial licensing requires care around LibRaw.
- In BN Tech Virtual Scanner, FreeImage is the foundation of
VirtualScanner: loading, DPI rescaling, pixel-type conversion, and final DIB / file output all run through it. Outside the TWAIN protocol layer, virtually every pixel operation is delegated to FreeImage.