在将以下C#代码移植到C ++时遇到问题:
protected override void OnPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects
, System.IntPtr buffer, int width, int height)
{
if (isPainting == true)
return;
isPainting = true;
// Save the provided buffer (a bitmap image) as a PNG.
using (System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height, width * 4, System.Drawing.Imaging.PixelFormat.Format32bppRgb, buffer))
{
bitmap.Save(@"LastOnPaint.png", System.Drawing.Imaging.ImageFormat.Png);
} // End Using bitmap
}
它的作用:
根据最新版的Chromium嵌入的图像从WebSite / SVG创建图像并将其保存为文件。
这是C ++中相应的渲染处理程序:
void RenderHandler::OnPaint(
CefRefPtr<CefBrowser> browser,
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const void* buffer, int width, int height
) {
// size_t len = sizeof(buffer) / sizeof(void*);
// printf("buffer length: %zu\n", len); // 1...
// Array size is probably: width*height * 4;
}
所以我一直在研究C#在位图构造函数中的作用,如下所示:
public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0)
{
IntPtr bitmap = IntPtr.Zero;
int status = Gdip.GdipCreateBitmapFromScan0(width, height, stride, unchecked((int)format), new HandleRef(null, scan0), out bitmap);
Gdip.CheckStatus(status);
SetNativeImage(bitmap);
}
internal void SetNativeImage(IntPtr handle) {
if (handle == IntPtr.Zero)
throw new ArgumentException(SR.GetString(SR.NativeHandle0), "handle");
nativeImage = handle;
}
可追溯到
internal const string Gdiplus = "gdiplus.dll";
[DllImport(ExternDll.Gdiplus, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Unicode)] // 3 = Unicode
[ResourceExposure(ResourceScope.Machine)]
internal static extern int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, HandleRef scan0, out IntPtr bitmap);
所以我想我可以在gdibitmapflat中调用GdipCreateBitmapFromScan0并快要完成了
GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width
, INT height, INT stride, PixelFormat format
, BYTE* scan0, GpBitmap** bitmap)
所以我为GDI收集了必要的头文件,这真是可怕的经历
#ifndef __BITMAPHELPER_H__
#define __BITMAPHELPER_H__
// #define WIN32_LEAN_AND_MEAN
#pragma warning(disable:4458)
#include <Windows.h>
#include <ObjIdl.h>
#include <minmax.h>
#include <gdiplus.h>
#include <wingdi.h>
#include <gdiplusbitmap.h>
#include <gdiplusflat.h>
using namespace Gdiplus;
#pragma comment (lib,"gdiplus.lib")
#pragma warning(default:4458)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <cstdbool>
#include <algorithm>
#include <memory>
并认为这样做会
#include "BitmapHelper.h"
static void Test()
{
GpBitmap *bitmap = NULL;
GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); // create a bitmap object with specified width/height/color
// GpGraphics *graph;
// Image * syntaxTest = NULL;
//syntaxTest->FromFile(TEXT("d:\\abc.jpg"), true); // create an image object
// Bitmap::FromBITMAPINFO
// GpImage *image = NULL;
// Gdiplus::Image()
Bitmap *bmp = NULL;
// GdipLoadImageFromFile(TEXT("d:\\abc.jpg"), &image); // create an image object
// GdipGetImageGraphicsContext(bitmap, &graph); // create a graphic object via bitmap object
// GdipDrawImageI(graph, image, 100, 100); // draw image to this graphic object, it can be done
}
但是,事实证明编译器不知道GdipCreateBitmapFromScan0,尽管它肯定位于#include <gdiplusflat.h>
...
如何从Scan0创建位图/图像?
注意:
在此过程中,我不想求助于C ++。NET,理想情况下也不要求助于WinAPI。因为我也希望它也能在Linux上工作。而且也不是像SDL这样的可怕依赖。
到目前为止,看来我可能的替代方法正在使用以下代码:
https://codereview.stackexchange.com/questions/196084/read-and-write-bmp-file-in-c
这意味着我必须自己创建bitmap header。
或者我可以使用ImageIO中的一些代码。
我不敢相信,即使在单个操作系统上创建一个简单的位图也是如此困难……
真的没有更好的(可移植的)方法来从琐碎的像素颜色数组中创建简单的位图吗?
为什么编译器找不到GdipCreateBitmapFromScan0?
如果我使用LoadLibrary和GetProcAddress来调用它而不是查找Windows头文件,那么我现在就可以完成了...
为什么#include <gdiplus.h>
不包括它自己的依赖关系?
答案 0 :(得分:1)
您对.NET的内部了解使您开始使用一个功能,该功能不属于GDI +的书面公开接口。在我看来,这是造成您大多数问题的真正原因。
我想您可能想做的就是从像素创建一个GdiPlus::Bitmap
对象开始。它有一个constructor that looks like it'll directly accept your data。
一旦创建了Bitmap
对象,就调用其Save
成员函数。 Bitmap
是从Image
公开派生的,因此您基本上是在处理普通的Image::Save
来生成PNG。
如果要消除对Windows代码的依赖性,则可以考虑使用libpng(一种明显的可能性)。这使您对过程有更多的控制权,但要付出更多的工作量(取决于您要执行的工作,可能大约是六到十二行代码,而不是一行)。或两个)。
答案 1 :(得分:0)
因此,在GDI +和原始C语言中都完成此操作之后,我可以肯定地说它实际上更快,更不用说不用GDI / GDI +进行图像处理了,问题也就更少了,Google的工作也更少了。实施GDI +的任何人都会严重大脑受损。
由于我尚未正确处理透明性,并且尚未合并lodepng,因此暂时添加了GDI +作为可选的附加选项。
// A program to read, write, and crop BMP image files.
#include "Bmp.h"
// Make a copy of a string on the heap.
// - Postcondition: the caller is responsible to free
// the memory for the string.
char *_string_duplicate(const char *string)
{
char *copy = (char*)malloc(sizeof(*copy) * (strlen(string) + 1));
if (copy == NULL)
{
// return "Not enough memory for error message";
const char* error_message = "Not enough memory for error message";
size_t len = strlen(error_message);
char* error = (char*)malloc(len * sizeof(char) + 1);
strcpy(error, error_message);
return error;
}
strcpy(copy, string);
return copy;
}
// Check condition and set error message.
bool _check(bool condition, char **error, const char *error_message)
{
bool is_valid = true;
if (!condition)
{
is_valid = false;
if (*error == NULL) // to avoid memory leaks
{
*error = _string_duplicate(error_message);
}
}
return is_valid;
}
// Write an image to an already open file.
// - Postcondition: it is the caller's responsibility to free the memory
// for the error message.
// - Return: true if and only if the operation succeeded.
bool write_bmp(FILE *fp, BMPImage *image, char **error)
{
// Write header
rewind(fp);
size_t num_read = fwrite(&image->header, sizeof(image->header), 1, fp);
if (!_check(num_read == 1, error, "Cannot write image"))
{
return false;
}
// Write image data
num_read = fwrite(image->data, image->header.image_size_bytes, 1, fp);
if (!_check(num_read == 1, error, "Cannot write image"))
{
return false;
}
return true;
}
// Free all memory referred to by the given BMPImage.
void free_bmp(BMPImage *image)
{
free(image->data);
free(image);
}
// Open file. In case of error, print message and exit.
FILE *_open_file(const char *filename, const char *mode)
{
FILE *fp = fopen(filename, mode);
if (fp == NULL)
{
fprintf(stderr, "Could not open file %s\n", filename);
exit(EXIT_FAILURE);
}
return fp;
}
// Close file and release memory.void _clean_up(FILE *fp, BMPImage *image, char **error)
void _clean_up(FILE *fp, BMPImage *image, char **error)
{
if (fp != NULL)
{
fclose(fp);
}
free_bmp(image);
free(*error);
}
// Print error message and clean up resources.
void _handle_error(char **error, FILE *fp, BMPImage *image)
{
fprintf(stderr, "ERROR: %s\n", *error);
_clean_up(fp, image, error);
exit(EXIT_FAILURE);
}
void write_image(const char *filename, BMPImage *image, char **error)
{
FILE *output_ptr = _open_file(filename, "wb");
if (!write_bmp(output_ptr, image, error))
{
_handle_error(error, output_ptr, image);
}
fflush(output_ptr);
fclose(output_ptr);
_clean_up(output_ptr, image, error);
}
// Return the size of an image row in bytes.
// - Precondition: the header must have the width of the image in pixels.
uint32_t computeImageSize(BMPHeader *bmp_header)
{
uint32_t bytes_per_pixel = bmp_header->bits_per_pixel / BITS_PER_BYTE;
uint32_t bytes_per_row_without_padding = bmp_header->width_px * bytes_per_pixel;
uint32_t padding = (4 - (bmp_header->width_px * bytes_per_pixel) % 4) % 4;
uint32_t row_size_bytes = bytes_per_row_without_padding + padding;
return row_size_bytes * bmp_header->height_px;
}
#ifdef USE_GDI
#pragma warning(disable:4189)
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
} // if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
} // Next j
free(pImageCodecInfo);
return -1; // Failure
}
// https://github.com/lvandeve/lodepng
static bool notInitialized = true;
void WriteBitmapToFile(const char *filename, int width, int height, const void* buffer)
{
// HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (notInitialized)
{
// https://docs.microsoft.com/en-us/windows/desktop/api/gdiplusinit/nf-gdiplusinit-gdiplusstartup
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::Status isOk = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
if (isOk != Gdiplus::Status::Ok)
{
printf("Failed on GdiplusStartup\n");
}
notInitialized = false;
// defer
// GdiplusShutdown(gdiplusToken);
} // End if (notInitialized)
// https://docs.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-constant-image-pixel-format-constants
Gdiplus::Bitmap* myBitmap = new Gdiplus::Bitmap(width, height, width*4, PixelFormat32bppARGB, (BYTE*)buffer);
// myBitmap->RotateFlip(Gdiplus::Rotate180FlipY);
CLSID pngClsid;
// int result = GetEncoderClsid(L"image/tiff", &tiffClsid);
int result = GetEncoderClsid(L"image/png", &pngClsid);
printf("End GetEncoderClsid:\n");
if (result == -1)
printf("Error: GetEncoderClsid\n");
// throw std::runtime_error("Bitmap::Save");
// if (Ok != myBitmap->Save(L"D\foobartest.png", &pngClsid)) printf("Error: Bitmap::Save");
// WTF ? I guess a standard C/C++-stream would have been too simple ?
IStream* oStream = nullptr;
if (CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&oStream) != S_OK)
printf("Error on creating an empty IStream\n");
Gdiplus::EncoderParameters encoderParameters;
encoderParameters.Count = 1;
encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;
ULONG quality = 100;
encoderParameters.Parameter[0].Value = &quality;
// https://docs.microsoft.com/en-us/windows/desktop/api/gdiplusheaders/nf-gdiplusheaders-image-save(inistream_inconstclsid_inconstencoderparameters)
if (Gdiplus::Status::Ok != myBitmap->Save(oStream, &pngClsid, &encoderParameters))
printf("Error: Bitmap::Save\n");
// throw std::runtime_error("Bitmap::Save");
ULARGE_INTEGER ulnSize;
LARGE_INTEGER lnOffset;
lnOffset.QuadPart = 0;
oStream->Seek(lnOffset, STREAM_SEEK_END, &ulnSize);
oStream->Seek(lnOffset, STREAM_SEEK_SET, NULL);
uint8_t *pBuff = new uint8_t[(unsigned int)ulnSize.QuadPart];
ULONG ulBytesRead;
oStream->Read(pBuff, (ULONG)ulnSize.QuadPart, &ulBytesRead);
FILE *output_ptr = _open_file(filename, "wb");
fwrite((void*)pBuff, sizeof(uint8_t), (unsigned int)ulnSize.QuadPart, output_ptr);
fflush(output_ptr);
fclose(output_ptr);
oStream->Release();
delete pBuff;
delete myBitmap;
// https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
// std::string rotated_string = base64_encode((const unsigned char*)pBuff, ulnSize.QuadPart);
}
#pragma warning(default:4189)
#else
// TODO: PNG-Encoder
// https://github.com/lvandeve/lodepng
// https://lodev.org/lodepng/
BMPImage * CreateBitmapFromScan0(int32_t w, int32_t h, uint8_t* scan0)
{
BMPImage *new_image = (BMPImage *)malloc(sizeof(*new_image));
BMPHeader *header = (BMPHeader *)malloc(sizeof(*header));
new_image->header = *header;
new_image->header.type = MAGIC_VALUE;
new_image->header.bits_per_pixel = BITS_PER_PIXEL;
new_image->header.width_px = w;
new_image->header.height_px = h;
new_image->header.image_size_bytes = computeImageSize(&new_image->header);
new_image->header.size = BMP_HEADER_SIZE + new_image->header.image_size_bytes;
new_image->header.dib_header_size = DIB_HEADER_SIZE;
new_image->header.offset = (uint32_t) sizeof(BMPHeader);
new_image->header.num_planes = 1;
new_image->header.compression = 0;
new_image->header.reserved1 = 0;
new_image->header.reserved2 = 0;
new_image->header.num_colors = 0;
new_image->header.important_colors = 0;
new_image->header.x_resolution_ppm = 3780; // image->header.x_resolution_ppm;
new_image->header.y_resolution_ppm = 3780; // image->header.y_resolution_ppm;
new_image->data = (uint8_t*)malloc(sizeof(*new_image->data) * new_image->header.image_size_bytes);
memcpy(new_image->data, scan0, new_image->header.image_size_bytes);
return new_image;
}
void WriteBitmapToFile(const char *filename, int width, int height, const void* buffer)
{
BMPImage * image = CreateBitmapFromScan0((int32_t)width, (int32_t)height, (uint8_t*)buffer);
char *error = NULL;
write_image(filename, image, &error);
}
#endif
标题:
#ifndef BITMAPLION_BITMAPINFORMATION_H
#define BITMAPLION_BITMAPINFORMATION_H
#ifdef __cplusplus
// #include <iostream>
// #include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#else
#include <stdio.h>
#include <stdlib.h> // for malloc
#include <stdint.h>
#include <stdbool.h>
#include <string.h> // for strlen, strcopy
#endif
#ifdef __linux__
//linux specific code goes here
#elif _WIN32
// windows specific code goes here
#pragma warning(disable:4458)
#include <Windows.h>
#include <ObjIdl.h>
#include <minmax.h>
#include <gdiplus.h>
// #include <gdiplusheaders.h>
// #include <wingdi.h>
// #include <gdiplusbitmap.h>
// #include <gdiplusflat.h>
// #include <Gdipluspixelformats.h>
#pragma comment (lib,"gdiplus.lib")
// using namespace Gdiplus;
#pragma warning(default:4458)
#else
#endif
#define BMP_HEADER_SIZE 54
#define DIB_HEADER_SIZE 40
// Correct values for the header
#define MAGIC_VALUE 0x4D42
#define NUM_PLANE 1
#define COMPRESSION 0
#define NUM_COLORS 0
#define IMPORTANT_COLORS 0
#define BITS_PER_BYTE 8
// #define BITS_PER_PIXEL 24
#define BITS_PER_PIXEL 32
#ifdef _MSC_VER
#pragma pack(push) // save the original data alignment
#pragma pack(1) // Set data alignment to 1 byte boundary
#endif
typedef struct
#ifndef _MSC_VER
__attribute__((packed))
#endif
{
uint16_t type; // Magic identifier: 0x4d42
uint32_t size; // File size in bytes
uint16_t reserved1; // Not used
uint16_t reserved2; // Not used
uint32_t offset; // Offset to image data in bytes from beginning of file
uint32_t dib_header_size; // DIB Header size in bytes
int32_t width_px; // Width of the image
int32_t height_px; // Height of image
uint16_t num_planes; // Number of color planes
uint16_t bits_per_pixel; // Bits per pixel
uint32_t compression; // Compression type
uint32_t image_size_bytes; // Image size in bytes
int32_t x_resolution_ppm; // Pixels per meter
int32_t y_resolution_ppm; // Pixels per meter
uint32_t num_colors; // Number of colors
uint32_t important_colors; // Important colors
} BMPHeader;
#ifdef _MSC_VER
#pragma pack(pop) // restore the previous pack setting
#endif
typedef struct {
BMPHeader header;
// unsigned char* data;
// It is more informative and will force a necessary compiler error
// on a rare machine with 16-bit char.
uint8_t* data;
} BMPImage;
// #define USE_GDI true
#ifndef USE_GDI
BMPImage * CreateBitmapFromScan0(int32_t w, int32_t h, uint8_t* scan0);
#endif
void WriteBitmapToFile(const char *filename, int width, int height, const void* buffer);
#endif //BITMAPLION_BITMAPINFORMATION_H