当前C#剪切图片的案例挺多的,但是用C++的还比较少。但目前的项目需要剪切图片,我也知道用OpenCV比较简单,但是自己又不想在项目中配置一大堆OpenCV的东西,因为项目不怎么需要哪个。
本篇博文包含了 两个知识点:图片剪切,字节数组和IStream的转换,Stream和Image类的转换。
首先自己搜了网上C++的剪切图片的方法,然后发现GDI+ 中的DrawImage这个函数可以剪切图片。
Status DrawImage(IN Image* image,
IN const RectF& destRect,
IN REAL srcx,
IN REAL srcy,
IN REAL srcwidth,
IN REAL srcheight,
IN Unit srcUnit,
IN const ImageAttributes* imageAttributes = NULL,
IN DrawImageAbort callback = NULL,
IN VOID* callbackData = NULL)
1、在全局区创建一块内存句柄,用于目标流;
HGLOBAL hDesMem = GlobalAlloc(GMEM_MOVEABLE, ImageSize);
IStream *pDesStream = NULL;
CreateStreamOnHGlobal(hDesMem, TRUE, &pDesStream);
BYTE *pDesData = (BYTE *)GlobalLock(hDesMem);
2、.复制内存,到申请的全局空间中。
CopyMemory(pDesData, imgSrc, ImageSize);
GlobalUnlock(hDesMem);
3、最后要记得释放全局空间,释放流。
GlobalFree(hDesMem); // 释放全局空间
pDesStream->Release();
pOutStream->Release();
在我花了两个小时查看Image类之后,发现里面有一个Image的构造函数,可以通过IStream的方式来创建实例。
Image(
IN IStream* stream,
IN BOOL useEmbeddedColorManagement = FALSE
);
后来发现FromStream的函数也可以转为Image指针:
inline Image* Image::FromStream(
IN IStream* stream,
IN BOOL useEmbeddedColorManagement
)
include
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
// 功能:切割图像,或缩放图像
// 参数:imgSrc,源图片字节数组,
// ImageSize:原图片的大小
// imgDst:目标图片的缓冲区地址
// xPos:目标图片左上角x坐标
// yPos:目标图片左上角y坐标
// Width:目标图片宽度
// Height:目标图片高度
// 返回值:失败返回0,否则返回目标图片的大小
DWORD CutPicture(BYTE *imgSrc, int ImageSize, BYTE* imgDst, int xPos, int yPos, int Width, int Height)
{
int nRet = -1;
if (Width<=0 || Height <=0 || imgSrc==nullptr || ImageSize<=0)
{
return 0;
}
GdiplusStartupInput gdiplusstartupinput;
ULONG_PTR gdiplustoken;
DWORD nDstSize = 0;
GdiplusStartup(&gdiplustoken, &gdiplusstartupinput, NULL);
{
CLSID clsid;
nRet = GetCodecClsid(L"image/jpeg", &clsid);
if (nRet == -1)
{
GdiplusShutdown(gdiplustoken);
return 0;
}
//再创建一块内存句柄,用于目标流
HGLOBAL hDesMem = GlobalAlloc(GMEM_MOVEABLE, ImageSize);
IStream *pDesStream = NULL;
CreateStreamOnHGlobal(hDesMem, TRUE, &pDesStream);
BYTE *pDesData = (BYTE *)GlobalLock(hDesMem);
//3.复制内存,到申请的全局空间中。
CopyMemory(pDesData, imgSrc, ImageSize);
GlobalUnlock(hDesMem);
// 4. 重建Image
Image *bmSrc = Image::FromStream(pDesStream);
// bmSrc->Save(L"E:\\test3.jpg", &clsid, NULL);
int w = 0, h = 0;
w = bmSrc->GetWidth();
h = bmSrc->GetHeight();
if (w < h) //图片是竖着的 交换Width和Height
{
int nTemp = Width;
Width = Height;
Height = nTemp;
}
Bitmap *bmPhoto = new Bitmap(Width, Height); // elvsi待解决
bmPhoto->SetResolution(bmSrc->GetHorizontalResolution(), bmSrc->GetVerticalResolution());
//bmPhoto->SetResolution(Width, Height);
Graphics grPhoto(bmPhoto);
grPhoto.Clear((ARGB)Color::White);
grPhoto.SetInterpolationMode(InterpolationModeHighQualityBicubic);
Rect dest(0, 0, w, h);
grPhoto.DrawImage((Image*)bmSrc, dest, xPos, yPos, Width, Height, UnitPixel);
// bmPhoto->Save(L"E:\\test4.jpg", &clsid, NULL);
//2.创建流
IStream *pOutStream = NULL;
ULARGE_INTEGER pSeek;
LARGE_INTEGER dlibMove = { 0 };
CreateStreamOnHGlobal(NULL, TRUE, &pOutStream);
//以JPEG图片格式储存数据到流中
bmPhoto->Save(pOutStream, &clsid, NULL);
pOutStream->Seek(dlibMove, STREAM_SEEK_SET, &pSeek);
pOutStream->Read(imgDst, ImageSize, &nDstSize);
GlobalFree(hDesMem); // 释放全局空间
delete bmSrc;
delete bmPhoto;
pDesStream->Release();
pOutStream->Release();
}
GdiplusShutdown(gdiplustoken);
return nDstSize;
}
总结
第一次发博文,如果有说的不对的地方,还望各位大牛包容和指正。