我有一个应用程序使用这个库(实际上是D 的直接端口)进行一些图像处理。我正在寻找一些其他类似风格的库来加载其他文件类型。
我需要/想要的东西:
- 损失较少的格式。
- 简单的 C API。
- 以原始像素格式将数据加载到缓冲区中。
- 开源(因为我可以获取源文件并编译它们以供我自己使用,除此之外,许可无关紧要)
任何人都知道这样的事情吗?
PNG:对于加载和保存,您可以尝试 LodePNG 库
C/C++: http: //members.gamedev.net/lode/projects/LodePNG/
D 端口:www.dsource.org/projects/scrapple/wiki/LodePngLibrary
devIL 和 SDL_Image 支持很多格式。Derelict 提供了它们的绑定。
我自己的代码用于使用这些并有一个原始缓冲区:
开发者: http ://codepad.org/tKonvsJ0
SDL_Image:http ://codepad.org/jLJDNstw
FreeImage 非常全面,非常干净且易于使用。
您可能想尝试 libpng,尽管我并不认为它易于使用。
除此之外,您可以尝试直接在位图上工作,根本不需要库。
我会考虑使用 imageMagick ( http://www.imagemagick.org/script/index.php ) 来满足您所有的图像加载需求。它支持许多不同位深度的许多格式,大多数格式都可以读写。
它可能做的比你需要的要多得多,但它是一个设计非常好的库,我已经在几个项目中使用过它。
它与 GPL 兼容。(而且我认为商业许可证也可用)
您可以随时尝试gdimage库。我从来没有遇到过任何问题,尽管我使用它所做的工作的迷雾一直是在 PHP 中。
您可以使用Netpbm等软件转换为 PPM 格式/从PPM 格式转换,无需外部库即可从任何程序中轻松读取/写入。
PPM 文件如下所示:
P6 800 600 255 # 后跟 800x600x3 字节的 0 到 255 之间的值,即 \xFF\x00\x00\x80\x80\x00\x00\xFF\x00\x00\x80\x80\x00\x00\xFF... # 但没有转义
或像这样:
P3 800 600 255 # 后跟 0 到 255 之间的 800x600x3 十进制数,即 255 0 0 128 128 0 0 255 0 0 128 128 0 0 255 ...
我认为SOIL(简单的 OpenGL 图像库)非常适合您的描述。它有多种格式,iirc 的 jpg 代码甚至是从与您相同的源中移植的。
CAPI 项目现在可以在GitHub 上用于图像处理。该库API简单,体积小,兼容性好。目前正在努力提高速度。经过测试并在 Windows 和 Linux 上运行。该库目前支持以下图像格式:
在下面的示例中,我将在 Windows 上的 Visual Studio 中使用 C++。
首先,我们需要一些简单的例程来加载和保存文件。为此,我创建了函数 LoadFile 和 SaveFile。以下是将.ico格式文件转换为.png格式文件的示例控制台程序。
我们将使用的 CAPI 函数是:
#include <Windows.h>
#include <CAPI.h>
#ifdef UNICODE
#define app_fopen _wfopen_s
#define app_printf wprintf
#else
#define app_fopen fopen_s
#define app_printf printf
#endif // UNICODE
void* LoadFile(const STRING* FilePath, U64* pFileSize)
{
FILE* Stream;
size_t BufferLength;
void* pThisFile;
void* pNewBlock;
if ((FilePath == 0) || (pFileSize == 0)) return 0;
*pFileSize = 0;
if (app_fopen(&Stream, FilePath, STR("rb")) != 0) return 0;
if (Stream == 0) return 0;
pThisFile = 0;
BufferLength = 0;
do
{
BufferLength += 0x1000;
pNewBlock = capi_realloc(pThisFile, BufferLength);
if (pNewBlock == 0)
{
if (pThisFile != 0) capi_free(pThisFile);
fclose(Stream);
return 0;
}
pThisFile = pNewBlock;
*pFileSize += fread(&((U8*)pThisFile)[*pFileSize], 1, 0x1000, Stream);
} while (!(feof(Stream)));
fclose(Stream);
return pThisFile;
}
I32 SaveFile(const STRING* FilePath, void* pFilePointer, U64 FileSize)
{
FILE* Stream;
size_t nBytesWrite;
I32 ErrorCode;
if ((FilePath == 0) || (pFilePointer == 0) || (FileSize == 0)) return CAPI_ERROR_INVALID_PARAMETER;
ErrorCode = CAPI_ERROR_NONE;
if (app_fopen(&Stream, FilePath, STR("w+b")) != 0) return CAPI_ERROR_ACCESS_DENIED;
if (Stream == 0) return CAPI_ERROR_ACCESS_DENIED;
nBytesWrite = fwrite(pFilePointer, 1, (size_t)FileSize, Stream);
if (nBytesWrite != FileSize)
{
ErrorCode = CAPI_ERROR_ACCESS_DENIED;
goto exit_func;
}
exit_func:
fclose(Stream);
return ErrorCode;
}
int main()
{
IMAGE exampleImage;
void* myLoadedFile;
I32 ErrorCode;
U64 FileSize;
PNG* myNewFile;
PNG_PARAMETERS png_params;
myLoadedFile = LoadFile(STR("C:/Users/Public/myTestImage.ico"), &FileSize);
if (myLoadedFile == 0)
{
app_printf(STR("LoadFile Failed."));
app_printf(STR("\n"));
while (true) { Sleep(100); }
}
ErrorCode = capi_LoadImageFromMemory(&exampleImage, 1, myLoadedFile, FileSize);
if (ErrorCode != CAPI_ERROR_NONE)
{
app_printf(STR("capi_LoadImageFromMemory Failed. ErrorCode: "));
app_printf(capi_ErrorCodeToString(ErrorCode));
app_printf(STR("\n"));
while (true) { Sleep(100); }
}
png_params.CompressionMethod = 0;
png_params.Level = Z_DEFAULT_COMPRESSION;
png_params.FilterMethod = 0;
png_params.InterlaceMethod = 0;
png_params.IDAT_Length = 0x20000;
ErrorCode = capi_Create_PNG_ImageToMemory(&myNewFile, &FileSize, &exampleImage, &png_params);
if (ErrorCode != CAPI_ERROR_NONE)
{
app_printf(STR("capi_Create_PNG_ImageToMemory Failed. ErrorCode: "));
app_printf(capi_ErrorCodeToString(ErrorCode));
app_printf(STR("\n"));
while (true) { Sleep(100); }
}
ErrorCode = SaveFile(STR("C:/Users/Public/myTestImage.png"), myNewFile, FileSize);
if (ErrorCode != CAPI_ERROR_NONE)
{
app_printf(STR("SaveFile Failed. ErrorCode: "));
app_printf(capi_ErrorCodeToString(ErrorCode));
app_printf(STR("\n"));
while (true) { Sleep(100); }
}
app_printf(STR("Done!"));
while (true) { Sleep(100); }
}
在下一个示例中,我将创建一个具有双缓冲的简单窗口。
我们将在 Window_Paint_Handler 函数中将测试图像绘制到窗口。
我们将使用的 CAPI 函数是:
#include <Windows.h>
#include <CAPI.h>
#define WinClassName STR("ExampleProgram")
#define WinTitle STR("Example Program")
#ifdef UNICODE
#define app_fopen _wfopen_s
#define app_printf wprintf
#define ApplicationEntryPoint wWinMain
#else
#define app_fopen fopen_s
#define app_printf printf
#define ApplicationEntryPoint WinMain
#endif // UNICODE
static HWND Window_hWnd;
static HDC BufferHDC;
static BITMAPINFO* pDisplayBitmap;
static HBITMAP hBitmap;
static int ClientWidth;
static int ClientHeight;
static IMAGE FrameBuffer;
static IMAGE exampleImage;
void* LoadFile(const STRING* FilePath, U64* pFileSize)
{
FILE* Stream;
size_t BufferLength;
void* pThisFile;
void* pNewBlock;
if ((FilePath == 0) || (pFileSize == 0)) return 0;
*pFileSize = 0;
if (app_fopen(&Stream, FilePath, STR("rb")) != 0) return 0;
if (Stream == 0) return 0;
pThisFile = 0;
BufferLength = 0;
do
{
BufferLength += 0x1000;
pNewBlock = capi_realloc(pThisFile, BufferLength);
if (pNewBlock == 0)
{
if (pThisFile != 0) capi_free(pThisFile);
fclose(Stream);
return 0;
}
pThisFile = pNewBlock;
*pFileSize += fread(&((U8*)pThisFile)[*pFileSize], 1, 0x1000, Stream);
} while (!(feof(Stream)));
fclose(Stream);
return pThisFile;
}
I32 SaveFile(const STRING* FilePath, void* pFilePointer, U64 FileSize)
{
FILE* Stream;
size_t nBytesWrite;
I32 ErrorCode;
if ((FilePath == 0) || (pFilePointer == 0) || (FileSize == 0)) return CAPI_ERROR_INVALID_PARAMETER;
ErrorCode = CAPI_ERROR_NONE;
if (app_fopen(&Stream, FilePath, STR("w+b")) != 0) return CAPI_ERROR_ACCESS_DENIED;
if (Stream == 0) return CAPI_ERROR_ACCESS_DENIED;
nBytesWrite = fwrite(pFilePointer, 1, (size_t)FileSize, Stream);
if (nBytesWrite != FileSize)
{
ErrorCode = CAPI_ERROR_ACCESS_DENIED;
goto exit_func;
}
exit_func:
fclose(Stream);
return ErrorCode;
}
void FreeSysInternal()
{
if (BufferHDC != 0)
{
DeleteDC(BufferHDC);
BufferHDC = 0;
}
if (pDisplayBitmap != 0)
{
capi_free(pDisplayBitmap);
pDisplayBitmap = 0;
}
if (hBitmap != 0)
{
DeleteObject(hBitmap);
hBitmap = 0;
}
}
U32 SetupWindowFrameBuffer(HDC WindowHDC, RECT* ClientRt)
{
FreeSysInternal();
pDisplayBitmap = (BITMAPINFO*)capi_malloc(sizeof(BITMAPINFOHEADER));
if (pDisplayBitmap == 0) return 1;
pDisplayBitmap->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pDisplayBitmap->bmiHeader.biWidth = ClientRt->right;
pDisplayBitmap->bmiHeader.biHeight = -ClientRt->bottom;
pDisplayBitmap->bmiHeader.biPlanes = 1;
pDisplayBitmap->bmiHeader.biBitCount = 32;
pDisplayBitmap->bmiHeader.biCompression = 0;
pDisplayBitmap->bmiHeader.biSizeImage = 0;
pDisplayBitmap->bmiHeader.biXPelsPerMeter = 0;
pDisplayBitmap->bmiHeader.biYPelsPerMeter = 0;
pDisplayBitmap->bmiHeader.biClrUsed = 0;
pDisplayBitmap->bmiHeader.biClrImportant = 0;
BufferHDC = CreateCompatibleDC(WindowHDC);
if (BufferHDC == 0)
{
capi_free(pDisplayBitmap);
return 2;
}
hBitmap = CreateDIBSection(BufferHDC, pDisplayBitmap, 0,
(void**)&FrameBuffer.Pixels, 0, 0);
if (hBitmap == 0)
{
DeleteDC(BufferHDC);
capi_free(pDisplayBitmap);
return 3;
}
GdiFlush();
FrameBuffer.ScanLine = ClientRt->right;
FrameBuffer.Width = ClientRt->right;
FrameBuffer.Height = ClientRt->bottom;
SelectObject(BufferHDC, hBitmap);
return 0;
}
void Window_Paint_Handler(IMAGE* pDisplay)
{
// Fill the background of our window.
capi_FillImage(pDisplay, COLOR(195, 195, 195, 255));
// Now we draw our test image.
// Note: The last parameter (Alpha) must be 255 for the image to be displayed normally.
capi_DrawImageA(pDisplay, &exampleImage, 0, 0, 255);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC WinDC;
PAINTSTRUCT ps;
RECT WinArea;
RECT ClientArea;
int BorderWidth;
int BorderHeight;
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_ERASEBKGND:
return TRUE;
case WM_PAINT:
{
WinDC = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &ClientArea);
if ((ClientArea.right == 0) || (ClientArea.bottom == 0)) goto exit_paint_message;
if ((ClientArea.right != FrameBuffer.Width) || (ClientArea.bottom != FrameBuffer.Height))
{
if (SetupWindowFrameBuffer(WinDC, &ClientArea) != 0) goto exit_paint_message;
}
Window_Paint_Handler(&FrameBuffer);
SetDIBits(BufferHDC, hBitmap, 0,
pDisplayBitmap->bmiHeader.biHeight, FrameBuffer.Pixels, pDisplayBitmap, 0);
BitBlt(WinDC, 0, 0, ClientArea.right, ClientArea.bottom, BufferHDC, 0, 0, 0x00CC0020);
exit_paint_message:
EndPaint(hWnd, &ps);
break;
}
case WM_CREATE:
{
Window_hWnd = hWnd;
GetWindowRect(hWnd, &WinArea);
GetClientRect(hWnd, &ClientArea);
BorderWidth = WinArea.right - ClientArea.right;
BorderHeight = WinArea.bottom - ClientArea.bottom;
SetWindowPos(hWnd, NULL,
0, 0,
BorderWidth + ClientWidth, BorderHeight + ClientHeight, SWP_NOMOVE | SWP_NOZORDER);
GetWindowRect(hWnd, &WinArea);
SetWindowPos(hWnd, NULL,
(GetSystemMetrics(SM_CXSCREEN) - (WinArea.right - WinArea.left)) / 2,
(GetSystemMetrics(SM_CYSCREEN) - (WinArea.bottom - WinArea.top)) / 2,
0, 0, SWP_NOSIZE | SWP_NOZORDER);
break;
}
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI ApplicationEntryPoint(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ STRING* pCmdLine, _In_ int nCmdShow)
{
MSG msg;
WNDCLASSEX wcex;
void* myLoadedFile;
U64 FileSize;
I32 ErrorCode;
// Load our image to draw to the window frame buffer.
myLoadedFile = LoadFile(STR("C:/Users/Public/myTestImage.ico"), &FileSize);
if (myLoadedFile == 0)
{
MessageBox(0, STR("LoadFile Failed."), STR("Error!"), MB_OK);
return 0;
}
ErrorCode = capi_LoadImageFromMemory(&exampleImage, 1, myLoadedFile, FileSize);
if (ErrorCode != CAPI_ERROR_NONE)
{
MessageBox(0, STR("capi_LoadImageFromMemory Failed."), STR("Error!"), MB_OK);
return 0;
}
// The client area of our window will be the same size as our test image.
ClientWidth = exampleImage.Width;
ClientHeight = exampleImage.Height;
// Create the display window and enter the thread/window message loop.
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = WinClassName;
wcex.hIconSm = NULL;
if (!RegisterClassEx(&wcex))
{
MessageBox(0, STR("Failed to register the window class."), STR("Error!"), MB_OK);
return 0;
}
// Note: The 700 (Width) and 500 (Height) values are just dummy values. The Width and Height get set in the WM_CREATE message handler.
CreateWindowEx(0, WinClassName, WinTitle,
WS_VISIBLE | WS_CLIPCHILDREN | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX | WS_MAXIMIZEBOX,
0, 0, 700, 500, NULL, NULL, hInstance, NULL);
if (!Window_hWnd)
{
MessageBox(0, STR("Failed to create the window."), STR("Error!"), MB_OK);
return 0;
}
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
FreeSysInternal();
return 0;
}