C++ 使用GDI+剪切图片

简介

当前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)

 

字节数组和IStream的转换

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();

IStream和Image类的转换

在我花了两个小时查看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;
}

 

总结

第一次发博文,如果有说的不对的地方,还望各位大牛包容和指正。

你可能感兴趣的:(C++)