356 lines
13 KiB
C++
356 lines
13 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
||
|
||
#include "QRCodeBlueprintFunctionLibrary.h"
|
||
#include "qrencode.h"
|
||
#include <string>
|
||
#include <fstream>
|
||
#include "IImageWrapper.h"
|
||
//#include "ModuleManager.h"
|
||
#include "ImageWrapper/Public/IImageWrapper.h"
|
||
#include "IImageWrapperModule.h"
|
||
//#include "FileManagerGeneric.h"
|
||
#include "HAL/FileManagerGeneric.h"
|
||
using namespace std;
|
||
|
||
|
||
#define BI_RGB 0
|
||
|
||
//#pragma pack(push, 2)必须要加。bfType是2个字节,对应“BM”,后4个字节是文件大小,又对应4字节。
|
||
//如果不设定对齐方式,而按默认的8字节或4字节对应,这些属性就错位了,而别人又是按标准来读取的,
|
||
//对齐方式不同,自然出错。一般情况下的编程之所以不考虑这些问题,是因为读取和保存都是你个人完成的,
|
||
//对齐方式是一样的,自然不出错。而这里你生成的图片可能还要供别人使用,自然要严格遵守标准。
|
||
|
||
#pragma pack(push, 2)//必须得写,否则sizeof得不到正确的结果
|
||
typedef unsigned char BYTE;
|
||
typedef unsigned short WORD;
|
||
typedef unsigned long DWORD;
|
||
typedef long LONG;
|
||
typedef struct {
|
||
WORD bfType;//位图文件的类型,必须为BM(1-2字节)
|
||
DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
|
||
WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
|
||
WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
|
||
DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)
|
||
} BITMAPFILEHEADER;
|
||
|
||
typedef struct {
|
||
DWORD biSize;//本结构所占用字节数(15-18字节)
|
||
LONG biWidth;//位图的宽度,以像素为单位(19-22字节)
|
||
LONG biHeight;//位图的高度,以像素为单位(23-26字节)
|
||
WORD biPlanes;//目标设备的级别,必须为1(27-28字节)
|
||
WORD biBitCount;//每个像素所需的位数,必须是1(双色),4(16色),8(256色)16(高彩色)或24(真彩色)之一,(29-30字节)
|
||
DWORD biCompression;//位图压缩类型,必须是0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一,(31-34字节)
|
||
DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)
|
||
LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
|
||
LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
|
||
DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)
|
||
DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节)
|
||
} BITMAPINFOHEADER;
|
||
|
||
#pragma pack(pop)
|
||
|
||
void UQRCodeBlueprintFunctionLibrary::GenerateQRCodeBitmap(const int32& Width, const int32& Height, const FString& Name, const FString& Outfile, int32 Margin /* = 0 */)
|
||
{
|
||
std::string StdName(TCHAR_TO_UTF8(*Name));
|
||
const char* QRCodeStr = StdName.c_str();
|
||
QRcode* QRCodePtr = nullptr;
|
||
QRCodePtr = QRcode_encodeString(QRCodeStr, 1, QR_ECLEVEL_L, QR_MODE_8, 1);
|
||
if (QRCodePtr)
|
||
{
|
||
uint32 QRWidth, QRWidthAdjustedX, QRHeightAdjustedY, QRDataBytes;
|
||
|
||
QRWidth = QRCodePtr->width;//矩阵的维数
|
||
uint32 ScaleX = (Width - 2 * Margin) / QRWidth;
|
||
uint32 ScaleY = (Height - 2 * Margin) / QRWidth;
|
||
QRWidthAdjustedX = QRWidth * ScaleX;//水平维度占的像素个数(ScaleX)
|
||
QRHeightAdjustedY = QRWidth * ScaleY;//垂直维度占的像素个数(ScaleY)
|
||
QRDataBytes = QRWidthAdjustedX * QRHeightAdjustedY * 3;//每一个像素3个字节(BGR)
|
||
|
||
//create data
|
||
uint8* RGBDataPtr = (uint8 *)malloc(QRDataBytes);
|
||
if (!RGBDataPtr)
|
||
{
|
||
printf("out of memory!!");
|
||
return;
|
||
}
|
||
uint8* QRCodeSourceDatas = QRCodePtr->data;
|
||
uint8* QRCodeDestData;
|
||
memset(RGBDataPtr, 0xFF, QRDataBytes); //分配内存,并且填充为白色
|
||
|
||
//由于Windows规定一个扫描行所占的字节数必须是4的倍数,这里必须是4的整数倍
|
||
int32 WidthAdjusted = Width % 4 ? (Width / 4 + 1) * 4 : Width;
|
||
uint32 ImageBytes = WidthAdjusted * Height * 3;
|
||
|
||
//create bitmap file header
|
||
BITMAPFILEHEADER FileHeader;
|
||
FileHeader.bfType = 0x4D42; //"BM"
|
||
FileHeader.bfReserved1 = 0;
|
||
FileHeader.bfReserved2 = 0;
|
||
FileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + ImageBytes;
|
||
FileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
|
||
|
||
//create bitmap info header
|
||
BITMAPINFOHEADER InfoHeader = {0};//所有值默认为0
|
||
InfoHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||
InfoHeader.biWidth = WidthAdjusted;
|
||
//说明图象的高度,以象素为单位。注:这个值除了用于描述图像的高度之外,它还有另一个用处,
|
||
//就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的,
|
||
//如果该值是一个负数,则说明图像是正向的。大多数的BMP文件都是倒向的位图,也就是时,
|
||
//高度值是一个正数。(注:当高度值是一个负数时(正向图像),图像将不能被压缩
|
||
//(也就是说biCompression成员将不能是BI_RLE8或BI_RLE4)。
|
||
InfoHeader.biHeight = -(int32)Height;
|
||
InfoHeader.biPlanes = 1;
|
||
InfoHeader.biBitCount = 24;
|
||
InfoHeader.biCompression = BI_RGB;
|
||
InfoHeader.biSizeImage = ImageBytes;
|
||
|
||
for (uint32 y = 0; y < QRWidth; y++)
|
||
{
|
||
QRCodeDestData = RGBDataPtr + ScaleY * y * QRWidthAdjustedX * 3;
|
||
for (uint32 x = 0; x < QRWidth; x++)
|
||
{
|
||
if (*QRCodeSourceDatas & 0x01)
|
||
{
|
||
for (uint32 rectY = 0; rectY < ScaleY; rectY++)
|
||
{
|
||
for (uint32 rectX = 0; rectX < ScaleX; rectX++)
|
||
{
|
||
*(QRCodeDestData + rectY * QRWidthAdjustedX * 3 + rectX * 3) = 0;//Blue
|
||
*(QRCodeDestData + rectY * QRWidthAdjustedX * 3 + rectX * 3 + 1) = 0;//Green
|
||
*(QRCodeDestData + rectY * QRWidthAdjustedX * 3 + rectX * 3 + 2) = 0;//Red
|
||
}
|
||
}
|
||
}
|
||
QRCodeDestData += ScaleX * 3;
|
||
QRCodeSourceDatas += 1;
|
||
}
|
||
}
|
||
|
||
uint8* ImageDataPtr = (uint8 *)malloc(ImageBytes);
|
||
memset(ImageDataPtr, 0xFF, ImageBytes); //分配内存,并且填充为白色
|
||
for (uint32 y = Margin; y < QRHeightAdjustedY + Margin; y++)
|
||
{
|
||
for (uint32 x = Margin; x < QRWidthAdjustedX + Margin; x++)
|
||
{
|
||
for (int32 PixelByte = 0; PixelByte < 3; PixelByte++)
|
||
{
|
||
uint32 ImageIndex = (y * WidthAdjusted + x) * 3 + PixelByte;
|
||
uint32 RGBDataIndex = ((y - Margin) * QRWidthAdjustedX + (x - Margin)) * 3 + PixelByte;
|
||
ImageDataPtr[ImageIndex] = RGBDataPtr[RGBDataIndex];
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!FFileManagerGeneric::Get().DirectoryExists(*Outfile))
|
||
{
|
||
FFileManagerGeneric::Get().MakeDirectory(*FPaths::GetPath(Outfile), true);
|
||
}
|
||
FILE* BitmapFile;
|
||
if (!(fopen_s(&BitmapFile, TCHAR_TO_UTF8(*Outfile), "wb")))
|
||
{
|
||
fwrite(&FileHeader, sizeof(BITMAPFILEHEADER), 1, BitmapFile);
|
||
fwrite(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, BitmapFile);
|
||
fwrite(ImageDataPtr, sizeof(uint8), ImageBytes, BitmapFile);
|
||
fclose(BitmapFile);
|
||
}
|
||
else
|
||
{
|
||
printf("create file failed!!");
|
||
}
|
||
QRcode_free(QRCodePtr);
|
||
free(RGBDataPtr);
|
||
if (Margin > 0)
|
||
{
|
||
free(ImageDataPtr);
|
||
}
|
||
}
|
||
}
|
||
|
||
UTexture2D* UQRCodeBlueprintFunctionLibrary::GenerateQRCodeTexture(const int32& Width, const int32& Height, const FString& Name, int32 Margin /* = 0 */)
|
||
{
|
||
std::string StdName(TCHAR_TO_UTF8(*Name));
|
||
const char* QRCodeStr = StdName.c_str();
|
||
QRcode* QRCodePtr = nullptr;
|
||
QRCodePtr = QRcode_encodeString(QRCodeStr, 1, QR_ECLEVEL_L, QR_MODE_8, 1);
|
||
UTexture2D* Texture = nullptr;
|
||
if (QRCodePtr)
|
||
{
|
||
uint32 QRWidth, QRWidthAdjustedX, QRHeightAdjustedY, QRDataBytes;
|
||
|
||
QRWidth = QRCodePtr->width;//矩阵的维数
|
||
uint32 ScaleX = (Width - 2 * Margin) / QRWidth;
|
||
uint32 ScaleY = (Height - 2 * Margin) / QRWidth;
|
||
QRWidthAdjustedX = QRWidth * ScaleX;//水平维度占的像素个数(ScaleX)
|
||
QRHeightAdjustedY = QRWidth * ScaleY;//垂直维度占的像素个数(ScaleY)
|
||
QRDataBytes = QRWidthAdjustedX * QRHeightAdjustedY * 3;//每一个像素3个字节(BGR)
|
||
|
||
uint8* RGBDataPtr = (uint8 *)malloc(QRDataBytes);
|
||
if (!RGBDataPtr)
|
||
{
|
||
printf("out of memory!!");
|
||
return Texture;
|
||
}
|
||
|
||
uint8* QRCodeSourceDatas = QRCodePtr->data;
|
||
uint8* QRCodeDestData;
|
||
memset(RGBDataPtr, 0xFF, QRDataBytes); //分配内存,并且填充为白色
|
||
for (uint32 y = 0; y < QRWidth; y++)
|
||
{
|
||
QRCodeDestData = RGBDataPtr + ScaleY * y * QRWidthAdjustedX * 3;
|
||
for (uint32 x = 0; x < QRWidth; x++)
|
||
{
|
||
if (*QRCodeSourceDatas & 0x01)
|
||
{
|
||
for (uint32 rectY = 0; rectY < ScaleY; rectY++)
|
||
{
|
||
for (uint32 rectX = 0; rectX < ScaleX; rectX++)
|
||
{
|
||
*(QRCodeDestData + rectY * QRWidthAdjustedX * 3 + rectX * 3) = 0;//Blue
|
||
*(QRCodeDestData + rectY * QRWidthAdjustedX * 3 + rectX * 3 + 1) = 0;//Green
|
||
*(QRCodeDestData + rectY * QRWidthAdjustedX * 3 + rectX * 3 + 2) = 0;//Red
|
||
}
|
||
}
|
||
}
|
||
QRCodeDestData += ScaleX * 3;
|
||
QRCodeSourceDatas += 1;
|
||
}
|
||
}
|
||
|
||
QRcode_free(QRCodePtr);
|
||
TArray<uint8> ImageBGRAData;
|
||
//for (uint32 i = 0; i < QRDataBytes; i++)
|
||
//{
|
||
// ImageBGRAData.Add(RGBDataPtr[i]);
|
||
// if ((i + 1) % 3 == 0)
|
||
// {
|
||
// ImageBGRAData.Add(0xFF);//填充Alpha通道为不透明
|
||
// }
|
||
//}
|
||
for (uint32 i = 0; i < (uint32)Width * (uint32)Height * 4; i++)
|
||
{
|
||
ImageBGRAData.Add(0xFF);
|
||
}
|
||
for (uint32 y = Margin; y < QRHeightAdjustedY + Margin; y++)
|
||
{
|
||
for (uint32 x = Margin; x < QRWidthAdjustedX + Margin; x++)
|
||
{
|
||
for (int32 PixelByte = 0; PixelByte < 3; PixelByte++)
|
||
{
|
||
uint32 ImageIndex = ( y * Width + x ) * 4 + PixelByte;
|
||
uint32 RGBDataIndex = ((y - Margin) * QRWidthAdjustedX + (x - Margin)) * 3 + PixelByte;
|
||
ImageBGRAData[ImageIndex] = RGBDataPtr[RGBDataIndex];
|
||
}
|
||
}
|
||
}
|
||
|
||
free(RGBDataPtr);
|
||
|
||
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
|
||
TSharedPtr<IImageWrapper> TargetImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::BMP);
|
||
if (TargetImageWrapper.IsValid())
|
||
{
|
||
Texture = UTexture2D::CreateTransient(Width, Height, PF_B8G8R8A8);
|
||
if (Texture != nullptr)
|
||
{
|
||
FTexture2DMipMap& Mip = Texture->PlatformData->Mips[0];
|
||
void* TextureData = Mip.BulkData.Lock(LOCK_READ_WRITE);
|
||
FMemory::Memcpy(TextureData, ImageBGRAData.GetData(), ImageBGRAData.Num());
|
||
Mip.BulkData.Unlock();
|
||
Texture->UpdateResource();
|
||
}
|
||
}
|
||
}
|
||
|
||
return Texture;
|
||
}
|
||
|
||
bool UQRCodeBlueprintFunctionLibrary::GenerateQRCodeImageByType(const int32& Width, const int32& Height, const FString& Name, const FString& Outfile, QR_IMAGE_FORMAT ImageFormat, int32 Margin /*= 0*/)
|
||
{
|
||
std::string StdName(TCHAR_TO_UTF8(*Name));
|
||
const char* QRCodeStr = StdName.c_str();
|
||
QRcode* QRCodePtr = nullptr;
|
||
QRCodePtr = QRcode_encodeString(QRCodeStr, 1, QR_ECLEVEL_L, QR_MODE_8, 1);
|
||
UTexture2D* Texture = nullptr;
|
||
if (QRCodePtr)
|
||
{
|
||
uint32 QRWidth, QRWidthAdjustedX, QRHeightAdjustedY, QRDataBytes;
|
||
|
||
QRWidth = QRCodePtr->width;//矩阵的维数
|
||
uint32 ScaleX = (Width - 2 * Margin) / QRWidth;
|
||
uint32 ScaleY = (Height - 2 * Margin) / QRWidth;
|
||
QRWidthAdjustedX = QRWidth * ScaleX;//水平维度占的像素个数(ScaleX)
|
||
QRHeightAdjustedY = QRWidth * ScaleY;//垂直维度占的像素个数(ScaleY)
|
||
QRDataBytes = QRWidthAdjustedX * QRHeightAdjustedY * 3;//每一个像素3个字节(BGR)
|
||
|
||
uint8* RGBDataPtr = (uint8 *)malloc(QRDataBytes);
|
||
if (!RGBDataPtr)
|
||
{
|
||
printf("out of memory!!");
|
||
return false;
|
||
}
|
||
|
||
uint8* QRCodeSourceDatas = QRCodePtr->data;
|
||
uint8* QRCodeDestData;
|
||
memset(RGBDataPtr, 0xFF, QRDataBytes); //分配内存,并且填充为白色
|
||
for (uint32 y = 0; y < QRWidth; y++)
|
||
{
|
||
QRCodeDestData = RGBDataPtr + ScaleY * y * QRWidthAdjustedX * 3;
|
||
for (uint32 x = 0; x < QRWidth; x++)
|
||
{
|
||
if (*QRCodeSourceDatas & 0x01)
|
||
{
|
||
for (uint32 rectY = 0; rectY < ScaleY; rectY++)
|
||
{
|
||
for (uint32 rectX = 0; rectX < ScaleX; rectX++)
|
||
{
|
||
*(QRCodeDestData + rectY * QRWidthAdjustedX * 3 + rectX * 3) = 0;//Blue
|
||
*(QRCodeDestData + rectY * QRWidthAdjustedX * 3 + rectX * 3 + 1) = 0;//Green
|
||
*(QRCodeDestData + rectY * QRWidthAdjustedX * 3 + rectX * 3 + 2) = 0;//Red
|
||
}
|
||
}
|
||
}
|
||
QRCodeDestData += ScaleX * 3;
|
||
QRCodeSourceDatas += 1;
|
||
}
|
||
}
|
||
|
||
QRcode_free(QRCodePtr);
|
||
TArray<uint8> ImageBGRAData;
|
||
|
||
for (uint32 i = 0; i < (uint32)Width * (uint32)Height * 4; i++)
|
||
{
|
||
ImageBGRAData.Add(0xFF);
|
||
}
|
||
for (uint32 y = Margin; y < QRHeightAdjustedY + Margin; y++)
|
||
{
|
||
for (uint32 x = Margin; x < QRWidthAdjustedX + Margin; x++)
|
||
{
|
||
for (int32 PixelByte = 0; PixelByte < 3; PixelByte++)
|
||
{
|
||
uint32 ImageIndex = (y * Width + x) * 4 + PixelByte;
|
||
uint32 RGBDataIndex = ((y - Margin) * QRWidthAdjustedX + (x - Margin)) * 3 + PixelByte;
|
||
ImageBGRAData[ImageIndex] = RGBDataPtr[RGBDataIndex];
|
||
}
|
||
}
|
||
}
|
||
|
||
free(RGBDataPtr);
|
||
|
||
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
|
||
TSharedPtr<IImageWrapper> TargetImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat(ImageFormat));
|
||
if (TargetImageWrapper.IsValid())
|
||
{
|
||
if (TargetImageWrapper->SetRaw(ImageBGRAData.GetData(), ImageBGRAData.Num(), Width, Height, ERGBFormat::BGRA, 8))
|
||
{
|
||
const TArray<uint8, FDefaultAllocator64> TagetImageData = TargetImageWrapper->GetCompressed();
|
||
if (!FFileManagerGeneric::Get().DirectoryExists(*Outfile))
|
||
{
|
||
FFileManagerGeneric::Get().MakeDirectory(*FPaths::GetPath(Outfile), true);
|
||
}
|
||
return FFileHelper::SaveArrayToFile(TagetImageData, *Outfile);
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|