MFC 捕捉桌面存成jpg案例代码
下面是关于截屏并保存成jpg文件的代码。由主函数OnCapScreenJpg()、DDBToDIB()、JpegFromDib()、DibToSamps()以及QuadFromWord()函数组成。这些函数的功能包括截取屏幕、将截取的屏幕转成设备无关bmp、再进一步压缩成jpeg格式。这些代码是从网上得到的,得到的代码没有注释,调试、阅读比较费劲。因此我在调试过程中,加上了大量的注释,对不能运行的语句进行了修正,现发到网上,方便读者。这些代码我已在基于对话框的MFC项目(VS2022)中调试通过。
//捕捉屏幕存成jpg主函数代码实现
void CScrToJpeg2Dlg::OnCapScreenJpg(CString m_strTempBmp){ //参数是保存jpg文件路径及文件名
//1、声明CDC类对象、CBitmap类对象、BITMAP变量等CDC dc, tdc; //声明两个CDC对象CBitmap bm; //声明bmp对象(该对象被CDC对象tdc选入)BITMAP btm; //声明bmp结构体变量BITMAPINFOHEADER bih; //声明位图信息头结构体变量BITMAPFILEHEADER bfh; //声明位图文件头结构体变量DWORD size; //无符号长整型(截图总像素数)LPSTR lpData; //指向存储截图数据内存区的指针,LPSTR本质就是char*int Width = GetSystemMetrics(SM_CXSCREEN); //获取屏幕宽度(像素)int Height = GetSystemMetrics(SM_CYSCREEN); //获取屏幕高度
//2、关联显示器生成CDC类对象dc,以兼容dc方式生成位图类对象bmdc.CreateDC("DISPLAY", NULL, NULL, NULL); //DISPLAY指显示器(还可指向打印机等)bm.CreateCompatibleBitmap(&dc, Width, Height); //兼容dc生成位图对象bm
//3、继续以兼容dc的方式生成CDC对象tdctdc.CreateCompatibleDC(&dc);
//4、CDC对象tdc调用拷贝屏幕函数并最终将截屏数据存入BITMAP类型的btmCBitmap* pOld = tdc.SelectObject(&bm); //将CBitmap对象bm选入tdc中tdc.BitBlt(0, 0, Width, Height, &dc, 0, 0, SRCCOPY); //截屏到tdc中(实际进入bm)tdc.SelectObject(pOld); //tdc恢复缺省位图(与bm分离)bm.GetBitmap(&btm); //将截图转存到btm中(bm是类对象,btm是结构体变量)
//5、计算截图像素数据并按此数据申请全局缓存size = btm.bmWidthBytes * btm.bmHeight; //计算截图总像素数lpData = (LPSTR)GlobalAlloc(GPTR, size); //申请全局内存指针(类似剪贴板)//GPTR含义:表示分配固定的内存,返回值是一个指针,同时将所申请内存初始化为0//size申请的字节数
//6、对位图信息头结构体对象bih填写信息(数据来自于btm)bih.biBitCount = btm.bmBitsPixel; //颜色位数bih.biClrImportant = 0; //指定重要颜色数。0代表都重要bih.biClrUsed = 0; //指定实际颜色数,如果为零,则用到的颜色数为2biBitCountbih.biCompression = 0; //压缩方式,0不压缩bih.biHeight = btm.bmHeight; //图象的高度,单位是象素bih.biPlanes = 1; //只能填写1bih.biSize = sizeof(BITMAPINFOHEADER); //该结构体大小bih.biSizeImage = size; //图像大小(像素部分)bih.biWidth = btm.bmWidth; //图像宽度bih.biXPelsPerMeter = 0; //水平分辨率(0表示图像没有关联具体的物理尺寸信息)bih.biYPelsPerMeter = 0; //垂直分辨率
//7、调用GetDIBits将bm中的数据存入lpData,存入按照位图信息头结构体变量bih中规定格式GetDIBits(dc, bm, 0, bih.biHeight, lpData, (BITMAPINFO*)&bih, DIB_RGB_COLORS);//GetDIBits功能:检取指定位图的信息,并将其以指定格式复制到一个缓冲区中//参1 设备环境句柄 参2 位图类对象 参3 起始扫描线位置从0开始 参4 扫描线数//参5 指向用来检索位图数据的缓冲区指针 参6 指向位图信息头结构的指针//参7 DIB_RGB_COLORS表示颜色表由红、绿、蓝(RGB)三个直接值构成;还可以是颜色格式 //DIB_PAL_COLORS表示颜色表由指向当前逻辑调色板的16位索引值数组构成
//9、定义位图文件头结构bfh.bfReserved1 = 0; //预留1 必须为0bfh.bfReserved2 = 0; //预留2 必须为0bfh.bfType = ((WORD)('M' << 8) | 'B'); //文件类型:填写BM或者十六进制的0x4d42;bfh.bfSize = 54 + size; //位图文件大小(单位:字节)bfh.bfOffBits = 54; //位图数据起始位置(让开文件头部分)//10、如果设备支持调色板则创建逻辑调色板 CPalette pal; //CPalette类创建和管理调色板(含建立LOGPALETTE结构并创建逻辑调色板)if (dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE) {//GetDeviceCaps(RASTERCAPS)功能:获取dc的格栅能力//RC_PALETTE:标志位,表示支持调色板//括号中如果为真,则创建和选择调色板到设备上下文中UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY) * 256);//PALETTEENTRY结构体指定逻辑调色板中条目的颜色和用法//LOGPALETTE结构体定义了逻辑调色板LOGPALETTE* pLP = (LOGPALETTE*) new BYTE[nSize];//声明一个逻辑调色板指针(new方式)pLP->palVersion = 0x300; //逻辑调色板版本号pLP->palNumEntries = GetSystemPaletteEntries(dc, 0, 255, pLP->palPalEntry);//palNumEntries逻辑调色板中的条目数pal.CreatePalette(pLP); //完成创建调色板delete[] pLP; //清理pLP}
//11、位图转换为DIB(设备无关位图)(Convert the bitmap to a DIB)HANDLE hDIB = DDBToDIB(bm, BI_RGB, &pal);//DDBToDIB这个函数是自定义的(源码见后)//DDBToDIB函数将设备依赖位图转换为设备无关位图//参1 位图类对象 参2 压缩模式BI_RGB的值是0表示不压缩 参3 调色板指针
//12、设备无关图DIB再转成jpegCString strError; //声明错误码字符串JpegFromDib(hDIB, 100, m_strTempBmp, &strError);//这个函数也是自定义的(源码见后)//参1 设备无关图句柄 参2 保存的文件名 参3 报错字符串GlobalFree(hDIB); //该函数是释放指定的全局内存块GlobalFree(lpData); //释放共享内存(new方式声明的内存要自己清理)MessageBox("Screenshot saved as file successfully!");
}
//主程序到此结束//设备相关位图转成设备无关位图子函数代码实现
HANDLE CScrToJpeg2Dlg::DDBToDIB(CBitmap& bitmap, //参1 CBitmap类对象 实际传入的就是bmDWORD dwCompression, //参2 无符号整形 压缩模式 实际输入的是0(无压缩)CPalette* pPal) //参3 调色板指针 实际传入的是对pal的取址
{
//1、定义变量BITMAP btm; //声明一个BITMAP结构体变量BITMAPINFOHEADER bi; //声明位图信息头结构体变量LPBITMAPINFOHEADER lpbi; //指向位图信息头结构体的指针DWORD dwLen; //无符号长整形(信息头和颜色表的字节长度)HANDLE hDIB = NULL; //设备无关图句柄HANDLE handle; //句柄类型HDC hDC; //设备上下文句柄(=CDC*中的m_hDc)HPALETTE hPal; //调色板指针
//2、断言判断(如果获取bitmap的安全句柄失败,则结束程序)ASSERT(bitmap.GetSafeHandle());//GetSafeHandle()用于获取GDI对象的句柄。//3、如参数中未传入合适的关于压缩类型的参数if (dwCompression == BI_BITFIELDS) return NULL; //结束程序//BI_BITFIELDS这个标识用在16位和32位的BMP上,有三个DWORD作为图片数据的MASK//4、如参数中没有传入调色板则使用缺省的调色板hPal = (HPALETTE)pPal->GetSafeHandle(); //获取传入的调色板句柄if (hPal == NULL) //获取调色板句柄不成功hPal = (HPALETTE)GetStockObject(DEFAULT_PALETTE); //载入缺省调色板//5、获取作为参数传入的位图信息并写入btm中bitmap.GetObject(sizeof(btm), (LPSTR)&btm);//参1 写入位图信息的字节数//参2 指向写入缓冲区的指针//6、初始化位图信息头bi(在本函数中定义的)bi.biSize = sizeof(BITMAPINFOHEADER); //指定这个结构的长度,一般为40bi.biWidth = btm.bmWidth; //指定图象的宽度,单位是象素bi.biHeight = btm.bmHeight; //指定图像的高度bi.biPlanes = 1; //只能是1bi.biBitCount = btm.bmPlanes * btm.bmBitsPixel;//biBitCount表示颜色时要用到的位数//biBitCount常用的值为1(黑白图)、4(16色图)、8(256色)、24(真彩色图)、32位色 //bmPlanes指定调色板数目,是一种“面”的数目,通常为1,//bmBitsPixel一个点在每个调色板上接近的颜色位数bi.biCompression = dwCompression;//指定位图是否压缩,有效的值为BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDSbi.biSizeImage = 0; //指定实际的位图数据占用的字节数(先写成0,后边再填入)bi.biXPelsPerMeter = 0; //指定目标设备的水平分辨率,单位是每米的象素个数bi.biYPelsPerMeter = 0; //指定目标设备的垂直分辨率bi.biClrUsed = 0; //指定图象实际用到的颜色数,该值为零表示用到的颜色数为2biBitCountbi.biClrImportant = 0; //指定本图象中重要的颜色数,如果该值为零,则所有颜色都是重要的//7、计算信息头和颜色表字节数int nColors = (1 << bi.biBitCount); //将1做左移24或32位会大于256if (nColors > 256) nColors = 0; //判定是否真色彩dwLen = bi.biSize + nColors * sizeof(RGBQUAD);//bi.biSize 位图信息头结构字节数//RGBQUAD是一个结构体,由四个字节组成,分别表示蓝、绿、红、预留
//8、需要一个设备上下文,以从其得到DIBhDC = ::GetDC(NULL); //获得当前设备上下文句柄hPal = SelectPalette(hDC, hPal, FALSE);//参1 当前设备上下文句柄//参2 要选择的逻辑调色板的句柄//参3 指定逻辑调色板是否强制为背景调色板。/*说明:FALSE表示当应用程序位于前台时,RealizePalette会将逻辑调色板复制到设备调色板中。如果hdc是内存设备上下文,则忽略此参数。*///返回值 如果函数成功,则返回值是设备上下文上一个逻辑调色板的句柄
//9、从当前逻辑调色板中映射调色板(入口点)到系统调色板中RealizePalette(hDC); //已在其中选择逻辑调色板的设备上下文的句柄//10、位图信息头及颜色表申请内存(暂时,后边再扩大)hDIB = GlobalAlloc(GMEM_FIXED, dwLen); //申请全局内存(位图信息头及颜色表)//参1 GMEM_FIXED(返回的是句柄也是地址),GMEM_MOVEABLE(返回的是句柄)//用后要用GlobalFree函数来释放内存块if (!hDIB) { //申请失败的处理SelectPalette(hDC, hPal, FALSE);::ReleaseDC(NULL, hDC);return NULL;}lpbi = (LPBITMAPINFOHEADER)hDIB; //将hDIB强转为位图信息头结构指针*lpbi = bi; //将前面定义的bi赋值给*lpbi(此时bi中只有位图信息头内容)//11、调用GetDIBits函数,并将lpBits参数设置为NULL,这样程序将自动计算biSizeImage字段GetDIBits( //该函数获取指定兼容位图的位信息,将其按DIB指定格式复制到一个缓冲区中hDC, //参1 当前设备上下文句柄(HBITMAP)bitmap.GetSafeHandle(), //参2 传入位图句柄0L, //参3 扫描开始位置(DWORD)bi.biHeight,//参4 扫描线数(LPBYTE)NULL, //参5 指向用来检索位图信息缓冲区的指针//为NULL时函数把位图维数与格式传递给lpbi指向的BITMAPINFO结构(LPBITMAPINFO)lpbi, //参6 BITMAPINFO结构的指针,此结构确定传入位图的数据格式(DWORD)DIB_RGB_COLORS); //参7 指定BITMAPINFO结构的bmiColors成员的格式。bi = *lpbi; //把获得内容的lpbi中的内容重新赋值给bi
//12、如果驱动程序没有填写biSizeImage字段,则计算图像的每一行边界都按DWORD(32位)if (bi.biSizeImage == 0) { //初始化定义的是0,说明还没有赋值bi.biSizeImage = (((bi.biWidth * bi.biBitCount) + 31) & ~31) * bi.biHeight;//biWidth位图宽度 //biBitCount表示颜色用的位数 二者相乘表示一行像素用的位数//“+31)&~31”与每行字节数是4个字节(32位)的倍数相关//biHeight图像行数
//13、如果使用压缩方案,结果实际上可能会更大;增加大小以考虑这一点。if (dwCompression != BI_RGB) bi.biSizeImage = (bi.biSizeImage * 3) / 2;}//14、重新分配缓冲区,使其能够容纳所有位dwLen += bi.biSizeImage; //位图信息头、颜色表、位图内容都加到dwLen中if (handle = GlobalReAlloc(hDIB, dwLen, GMEM_MOVEABLE)) { //GMEM_MOVEABLE返回句柄hDIB = handle;} //此时hDIB指向整个位图(包括位图头信息、颜色表及内容)else {GlobalFree(hDIB); //不成功时释放hDIBSelectPalette(hDC, hPal, FALSE); //恢复缺省调色板 ::ReleaseDC(NULL, hDC);return NULL;}//15、获得位图位句柄并最终获得设备无关位图(Get the bitmap bits,FINALLY get the DIB)lpbi = (LPBITMAPINFOHEADER)hDIB;BOOL bGotBits = GetDIBits(hDC, //参1 当前设备上下文句柄(HBITMAP)bitmap.GetSafeHandle(),//参2 传入位图句柄0L, //参3 扫描开始位置(DWORD)bi.biHeight, //参4 扫描线数(LPBYTE)lpbi //参5 指向用来写入位图数据缓冲区的指针+ (bi.biSize + nColors * sizeof(RGBQUAD)), //仍是参5 指针偏移量(让开信息头部分)(LPBITMAPINFO)lpbi, //参6 指向一个BITMAPINFO结构的指针(DWORD)DIB_RGB_COLORS); //参7 指定BITMAPINFO结构的bmiColors成员的格式if (!bGotBits) { //获得设备无关图不成功的处理GlobalFree(hDIB); //释放hDIBSelectPalette(hDC, hPal, FALSE); //恢复缺省调色板::ReleaseDC(NULL, hDC); //释放设备上下文句柄return NULL; //返回空指针}
//16、获得设备无关位图成功的处理SelectPalette(hDC, hPal, FALSE); //恢复缺省调色板::ReleaseDC(NULL, hDC);//释放设备上下文句柄return hDIB; //返回设备无关位图句柄
}
//DDBToDIB子函数至此结束//设备无关位图转jpeg函数代码实现
BOOL CScrToJpeg2Dlg::JpegFromDib( //该函数在主程序中被调用的HANDLE hDib, //参1 由上一个无关位图函数获得的句柄hDIBint nQuality, //参2 JPEG质量(0-100)CString csJpeg, //参3 路径及文件名CString* pcsMsg) //参4 返回的错误信息
{
//1、基本信息检查if (nQuality < 0 || nQuality >100 || hDib == NULL || pcsMsg == NULL || csJpeg == ""){if (pcsMsg != NULL)*pcsMsg = "Invalid input data";return FALSE; //结束程序}*pcsMsg = "";//2、使用libjpeg库定义压缩对象及错误处理器struct jpeg_compress_struct cinfo; //定义jpeg压缩对象struct jpeg_error_mgr jerr; //定义jpeg错误处理器//3、定义文件指针(jpeg文件指针)及相关变量FILE* pOutFile; //目标文件Target file int nSampsPerRow; //图像每行的采样点数(样本数)JSAMPARRAY jsmpArray; //存储jpeg文件中像素(RGB值)的缓冲区/*一个JSAMPLE类型数组代表一条扫描线,JSAMPARRAY表示2维的JSAMPLE数组。nSampsPerRow与像素之间的关系:像素宽度 = SampsPerRow / Bands波段数;波段也称为通道(Channel),波段数决定了图像性质,如单波段(如灰度图)、三波段(如RGB彩色图)等。样本数在在数值上要大于像素数。*/
//4、将错误处理器关联压缩对象,并生成压缩对象(初始化压缩对象)cinfo.err = jpeg_std_error(&jerr); //指定错误处理器jpeg_create_compress(&cinfo);//初始化jpeg压缩对象//5、打开用于存放jpeg的文件获得文件指针,并对可能打开文件失败进行处理if ((fopen_s(&pOutFile, csJpeg, "wb")) != 0) //打开文件必须二进制模式{*pcsMsg = "Cannot open"; //给错误信息字符串赋值*pcsMsg += csJpeg; //把声明的路径及文件名加载Cannot open之后jpeg_destroy_compress(&cinfo); //释放压缩工作过程中所申请的资源return FALSE; //结束函数}
//6、文件指针与压缩对象关联jpeg_stdio_dest(&cinfo, pOutFile); //指定压缩图像存放目标文件//7、给cinfo结构体对象添加参数LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)hDib; //hDib是作为参数传入的位图句柄cinfo.image_width = lpbi->biWidth; //图像的宽度(像素)cinfo.image_height = lpbi->biHeight; //图像的高度(像素)info.input_components = 3; //每个像素的颜色分量cinfo.in_color_space = JCS_RGB; //JCS_RGB表示彩色图像、JCS_GRAYSCALE表示灰度图int nRowSize = lpbi->biWidth;//8、导入设置的压缩参数jpeg_set_defaults(&cinfo);/* 需要注意的是,jpeg_set_defaults函数一定要等设置好图像宽、高、色彩通道数计色彩空间四个参数后才能调用,因为这个函数要用到这四个值,调用jpeg_set_defaults函数后,jpeglib库采用默认的设置对图像进行压缩,如果需要改变设置,如压缩质量,调用这个函数后,可以调用其它设置函数,如jpeg_set_quality函数。其实图像压缩时有好多参数可以设置,但大部分我们都用不着设置,只需调用jpeg_set_defaults函数值为默认值即可。*///9、调整压缩参数jpeg_set_quality(&cinfo, //压缩对象nQuality, //压缩质量值(0-100)TRUE); //10、压缩开始jpeg_start_compress(&cinfo, TRUE);//11、输出缓冲区中每行的样本数(JSAMPLEs per row in output buffer)nSampsPerRow = cinfo.image_width * cinfo.input_components;//image_width像素宽度//input_components表示JPEG图像中每个像素由多少个独立的颜色构成;//如灰度图只有一个颜色分量,RGB图有三个颜色分量(与波段数概念相同)。//12、为图像处理分配内存空间,分配一个JSAMPARRAY类型的宽度为nSampsPerRow、高度为image_height//的二维数组空间,可以像二维数组一样访问:jsmpArray[x][y]。jsmpArray = (*cinfo.mem->alloc_sarray)( //内置的内存分配函数指针(用于分配二维样本数)(j_common_ptr)&cinfo, //cinfo是包含压缩参数和状态信息JPEG压缩结构体//这里cinfo被强转为libjpeg的通用结构体指针JPOOL_IMAGE, //指定内存区域类型(图像处理期间使用的内存类型)nSampsPerRow, //每行的样本数(通常是width*components,如RGB图像是width*3)cinfo.image_height);//图像的高度(行数,单位像素)//13、通过libjpeg的内存管理器分配一个二维数组,用于存储一行(或几行)JPEG图像数据。JSAMPARRAY pBuffer = (*cinfo.mem->alloc_sarray)(//SAMPARRAY 是libjpeg类型,表示"样本数组的数组",通常用于存储图像的行数据//pBuffer是一个指向JSAMPARRAY的指针,用于存储分配的内存//(*cinfo.mem->alloc_sarray)是一个函数指针,专门用于分配采样数组(RGB分量)内存(j_common_ptr)&cinfo,//将cinfo转换为通用的jpeg通用结构指针1, //表示分配1个JSAMPARRAY(即一个二维数组)nRowSize,//表示每行的大小(以像素为单位)1); //表示每个样本的大小(通常是1字节)//14、无关位图转成样本数据,成功时将扫描线数据写入文件if (DibToSamps( //(DibToSamps是自定义函数,详细源码见后)hDib, //在上一级函数中作为参数传入的位图nSampsPerRow,//每行样本数cinfo, //压缩对象jsmpArray,//存储二维图像数据的内存区指针pcsMsg)) //错误信息 {//将扫描线数组写入JPEG文件JDIMENSION lines_written = 0;while (cinfo.next_scanline < cinfo.image_height) {lines_written += jpeg_write_scanlines(&cinfo, &jsmpArray[cinfo.next_scanline],cinfo.image_height - cinfo.next_scanline);}}
//15、结束压缩时的处理jpeg_finish_compress(&cinfo); //Always finishfclose(pOutFile); //关闭文件指针jpeg_destroy_compress(&cinfo); //Free resourcesif (*pcsMsg != "") {return FALSE; //返回失败}else {return TRUE; //返回成功}
}
//----设备无关位图转jpeg函数JpegFromDib()结束----//无关位图像素转样本函数代码(在JpegFromDib中被调用)
BOOL CScrToJpeg2Dlg::DibToSamps(HANDLE hDib, //无关设备位图的句柄int nSampsPerRow, //输出缓冲区中每行的样本数struct jpeg_compress_struct cinfo, //关于压缩结构的结构体JSAMPARRAY jsmpPixels, //JPEG文件的像素RGB缓冲区CString* pcsMsg) //错误信息
{
//1、一般性检查(如果无关位图句柄、每行的样本数、错误码指针有问题时直接返回不成功if (hDib == NULL || nSampsPerRow <= 0 || pcsMsg == NULL) {if (pcsMsg != NULL) {*pcsMsg = "Invalid input data";return FALSE;}}
//2、声明变量并赋初值int r = 0, p = 0, q = 0, b = 0, n = 0, nUnused = 0, nBytesWide = 0, nUsed = 0;int nLastBits = 0, nLastNibs = 0, nCTEntries = 0, nRow = 0, nByte = 0, nPixel = 0;BYTE bytCTEnt = 0;LPBITMAPINFOHEADER pbBmHdr = (LPBITMAPINFOHEADER)hDib; //强转//3、解析无关位图文件头中的biBitCount,并根据像素的位深度确定颜色表的条目数switch (pbBmHdr->biBitCount) { //biBitCount表示单个像素位数,它决定了图像的颜色模式case 1: //每个像素用1个位存储,表示两种颜色nCTEntries = 2; //Monochrome,颜色表需要2个条目(黑和白) break;case 4: //4位图,每个像素用4位存储,可以表示 2⁴ = 16 种颜色nCTEntries = 16; //颜色表需16条目,存储具体的颜色值(调色板索引) break;case 8: //8位图(每个像素用8位表示,256色灰度/索引色图)nCTEntries = 256; //颜色表需要256个条目,适用于灰度图或索引彩色图break;case 16: //通常用 RGB565格式(5位红、6位绿、5位蓝)直接编码颜色值case 24: //标准 RGB888(每通道8位),直接存储颜色。case 32: //RGB+Alpha通道(如ARGB8888)。nCTEntries = 0; //真彩色图像不需要颜色表,像素值直接表示颜色(非调色板索引)。break;default:*pcsMsg = "Invalid bitmap bit count";return FALSE; //不支持的格式}/*补充:位图中颜色表(Color Table)仅对索引色图像(1/4/8位)有效,存储调色板颜色值。真彩色图像(16/24/32位)直接存储RGB值,无需颜色表。nCTEntries 的含义:表示颜色表的条目数,由像素位深度决定(如8位图需要256条目)。BMP文件结构用于确定如何读取像素数据(是否依赖颜色表)。颜色表格式:每个条目是一个 RGBQUAD 结构(4字节:B/G/R/保留),但实际颜色顺序可能因文件而异。*///4、指向颜色表和像素DWORD dwCTab = (DWORD)pbBmHdr + pbBmHdr->biSize;//pbBmHdr(即hDib)传入的无关位图的句柄//biSize位图信息头结构占字节数(偏移量)LPRGBQUAD pCTab = (LPRGBQUAD)(dwCTab); //强转成RGB颜色表类型指针LPSTR lpBits = (LPSTR)pbBmHdr + (WORD)pbBmHdr->biSize + (WORD)(nCTEntries * sizeof(RGBQUAD));//进一步强转成字符数组指针类型,再进一步偏移颜色表所占字节数,使指针指向颜色值区域//5、确定图像位的不同格式LPBYTE lpPixels = (LPBYTE)lpBits; //LPBYTE相当于无符号char*(单字节)RGBQUAD* pRgbQs = (RGBQUAD*)lpBits; //RGBQUAD结构用于定义调色板数组元素的类型WORD* wPixels = (WORD*)lpBits; //无符号双字节//6、根据位图的格式设置jsamps,请注意,行按从下到上的顺序处理,因为这是位图创建的方式switch (pbBmHdr->biBitCount) // 根据位图的位深度进行不同处理{case 1: //黑白图处理逻辑(每个像素占1位)nUsed = (pbBmHdr->biWidth + 7) / 8; //每行有效像素所占的字节数(向上取整)nUnused = (((nUsed + 3) / 4) * 4) - nUsed;//每行填充字节数(使每行字节数为4的倍数)nBytesWide = nUsed + nUnused; //每行字节总数(含填充)nLastBits = 8 - ((nUsed * 1) - pbBmHdr->biWidth); //最后字节中实际使用的位数for (r = 0; r < pbBmHdr->biHeight; r++) { //遍历每一行(倒序,实质是从下到上)for (p = 0, q = 0; p < nUsed; p++) { //遍历每行中的每个字节nRow = (pbBmHdr->biHeight - r - 1) * nBytesWide; //计算当前行在数据中的偏移nByte = nRow + p; //当前字节的绝对位置int nBUsed = (p < (nUsed >> 1)) ? 8 : nLastBits; //当前字节实际使用的位数for (b = 0; b < nBUsed; b++) { //处理当前字节的每一位bytCTEnt = lpPixels[nByte] << b; //左移b位获取当前位bytCTEnt = bytCTEnt >> 7; //右移7位始当前位成为最低位//使用颜色表(pCTab)获取RGB值并存储到jsmpPixels数组jsmpPixels[r][q + 0] = pCTab[bytCTEnt].rgbRed;jsmpPixels[r][q + 1] = pCTab[bytCTEnt].rgbGreen;jsmpPixels[r][q + 2] = pCTab[bytCTEnt].rgbBlue;q += 3;// 移动到下一个像素的RGB位置}}}break;case 4: //4位16色图处理逻辑(每个像素占4位)nUsed = (pbBmHdr->biWidth + 1) / 2;// 每行有效像素所占的字节数(2像素/字节)nUnused = (((nUsed + 3) / 4) * 4) - nUsed;// 每行填充字节数nBytesWide = nUsed + nUnused;// 每行总字节数nLastNibs = 2 - ((nUsed * 2) - pbBmHdr->biWidth);// 最后字节中实际使用的半字节数for (r = 0; r < pbBmHdr->biHeight; r++) {for (p = 0, q = 0; p < nUsed; p++) {nRow = (pbBmHdr->biHeight - r - 1) * nBytesWide;nByte = nRow + p;int nNibbles = (p < (nUsed - 1)) ? 2 : nLastNibs; //当前字节实际使用的半字节数jsmpPixels[r][q + 0] = pCTab[bytCTEnt].rgbRed;jsmpPixels[r][q + 1] = pCTab[bytCTEnt].rgbGreen;jsmpPixels[r][q + 2] = pCTab[bytCTEnt].rgbBlue;q += 3;}}break;case 8: //8位256色图处理逻辑(每个像素占1字节),每个字节都是一个像素颜色的指针nUnused = (((pbBmHdr->biWidth + 3) / 4) * 4) - pbBmHdr->biWidth;// 每行填充字节数for (r = 0; r < pbBmHdr->biHeight; r++) {for (p = 0, q = 0; p < pbBmHdr->biWidth; p++, q += 3) {nRow = (pbBmHdr->biHeight - r - 1) * (pbBmHdr->biWidth + nUnused);nPixel = nRow + p;// 直接使用像素值作为颜色表索引jsmpPixels[r][q + 0] = pCTab[lpPixels[nPixel]].rgbRed;jsmpPixels[r][q + 1] = pCTab[lpPixels[nPixel]].rgbGreen;jsmpPixels[r][q + 2] = pCTab[lpPixels[nPixel]].rgbBlue;}}break;case 16: //16位高彩色(每个像素占2字节)for (r = 0; r < pbBmHdr->biHeight; r++) {for (p = 0, q = 0; p < pbBmHdr->biWidth; p++, q += 3) {nRow = (pbBmHdr->biHeight - r - 1) * pbBmHdr->biWidth;nPixel = nRow + p;//将16位颜色值转换为RGBQUAD结构RGBQUAD quad = QuadFromWord(wPixels[nPixel]);//该函数为自定义,见后//存储RGB值jsmpPixels[r][q + 0] = quad.rgbRed;jsmpPixels[r][q + 1] = quad.rgbGreen;jsmpPixels[r][q + 2] = quad.rgbBlue;}}break;case 24: // 24位真彩色(每个像素占3字节)nBytesWide = (pbBmHdr->biWidth * 3);// 每行字节数(无填充)nUnused = (((nBytesWide + 3) / 4) * 4) - nBytesWide;// 每行填充字节数nBytesWide += nUnused;// 每行总字节数(含填充)for (r = 0; r < pbBmHdr->biHeight; r++) {for (p = 0, q = 0; p < (nBytesWide - nUnused); p += 3, q += 3) {nRow = (pbBmHdr->biHeight - r - 1) * nBytesWide;nPixel = nRow + p;// 直接读取BGR顺序的像素值(BMP存储顺序为BGR)jsmpPixels[r][q + 0] = lpPixels[nPixel + 2]; //RedjsmpPixels[r][q + 1] = lpPixels[nPixel + 1]; //GreenjsmpPixels[r][q + 2] = lpPixels[nPixel + 0]; //Blue}}break;case 32: //32位真色彩(每个像素占4字节)for (r = 0; r < pbBmHdr->biHeight; r++) {for (p = 0, q = 0; p < pbBmHdr->biWidth; p++, q += 3) {nRow = (pbBmHdr->biHeight - r - 1) * pbBmHdr->biWidth;nPixel = nRow + p;// 直接读取RGBQUAD结构中的RGB值(忽略alpha通道)jsmpPixels[r][q + 0] = pRgbQs[nPixel].rgbRed;jsmpPixels[r][q + 1] = pRgbQs[nPixel].rgbGreen;jsmpPixels[r][q + 2] = pRgbQs[nPixel].rgbBlue;}}break;} //switch结束return TRUE;
} //无关位图像素转样本函数DibToSamps()代码结束//16位像素转换为RGBQUAD值函数代码(在DibToSamps()中被调用)
RGBQUAD CScrToJpeg2Dlg::QuadFromWord(WORD b16) {BYTE bytVals[] = {0, 16, 24, 32, 40, 48, 56, 64,72, 80, 88, 96, 104,112,120,128,136,144,152,160,168,176,184,192,200,208,216,224,232,240,248,255};WORD wR = b16;WORD wG = b16;WORD wB = b16;wR <<= 1; wR >>= 11; //wR = wR << 1;wG <<= 6; wG >>= 11;wB <<= 11; wB >>= 11;RGBQUAD rgb;rgb.rgbReserved = 0;rgb.rgbBlue = bytVals[wB];rgb.rgbGreen = bytVals[wG];rgb.rgbRed = bytVals[wR];return rgb;
}
相关文章:
MFC 捕捉桌面存成jpg案例代码
下面是关于截屏并保存成jpg文件的代码。由主函数OnCapScreenJpg()、DDBToDIB()、JpegFromDib()、DibToSamps()以及QuadFromWord()函数组成。这些函数的功能包括截取屏幕、将截取的屏幕转成设备无关bmp、再进一步压缩成jpeg格式。这些代码是从网上得到的,得到的代码没…...
2.4.4-死锁的处理策略-检测和解除
知识总览 死锁的检测 用资源分配图这种数据结构来检测是否产生了死锁,资源分配图上有2种节点,进程节点用圆圈表示,一个圆圈代表一个进程,还有资源节点,一个矩形代表一类资源,用矩形中的圆圈表示当前类型的…...
豪越智能仓储:为消防应急物资管理“上锁”
在城市的繁华街角,一场突如其来的大火无情地肆虐着一栋商业大楼。火焰在楼内疯狂蔓延,滚滚浓烟迅速弥漫,人们的生命财产安全受到了严重威胁。消防警报声骤然响起,消防队员们迅速出动,争分夺秒赶赴火灾现场。然而&#…...
(06)数字化转型之质量管理:遵循PDCA规范的全流程避险指南
在全球化竞争和消费升级的双重驱动下,质量管理已从单纯的产品检验演变为企业核心竞争力的重要组成部分。一个完善的质量管理体系不仅能降低质量成本、提升客户满意度,更能成为品牌差异化的战略武器。本文将系统性地介绍现代企业质量管理的完整框架&#…...
图论算法精解(Java 实现):从基础到高频面试题
一、图的基础表示方法 1.1 邻接矩阵(Adjacency Matrix) 邻接矩阵是表示图的一种直观方式,它使用一个二维数组来存储节点之间的连接关系。对于一个有 n 个节点的图,邻接矩阵是一个 nn 的矩阵,其中 matrix [i][j] 表示…...
[Linux] Linux信号量深度解析与实践(代码示例)
Linux信号量深度解析与实践 文章目录 Linux信号量深度解析与实践一、什么是信号量1. 信号量的核心概念2. 信号量的分类3. 信号量的操作机制 二、怎么用信号量1. 信号量API的深度解析(1)无名信号量API(2)有名信号量API(…...
Switch最新 模拟器 Eden(伊甸)正式发布 替代Yuzu模拟器
Switch最新 模拟器 Eden(伊甸)正式发布 替代Yuzu模拟器 100 帧跑满《塞尔达传说:旷野之息》 这款模拟器基于 Yuzu 框架开发,但团队强调它并非…...
[cg] [ds]深度缓冲z与线性z推导
4. GLSL 代码实现 在着色器中,将深度缓冲值转换为线性深度: float LinearizeDepth(float depth, float near, float far) {// OpenGL 的 NDC 深度范围是 [-1, 1],需转换float z_ndc 2.0 * depth - 1.0;// 计算线性深度return (2.0 * near …...
clock的时钟频率check代码
在芯片验证中,经常遇到需要check时钟频率的场景,由于时钟数量有很多,手动写代码得到后年马月,所以我这边写了一个宏define,可以通过输入参数的形式验证需要check的时钟频率,大大提升了验证效率和准确率&…...
企业数字化转型是否已由信息化+自动化向智能化迈进?
DeepSeek引发的AI热潮迅速蔓延到了各个行业,目前接入DeepSeek的企业,涵盖了科技互联网、云服务、电信、金融、能源、汽车、手机等热门领域,甚至全国各地政府机构也纷纷引入。 在 DeepSeek 等国产 AI 技术的推动下,众多企业已经敏锐…...
PT5F2307触摸A/D型8-Bit MCU
1. 产品概述 ● PT5F2307是一款51内核的触控A/D型8位MCU,内置16K*8bit FLASH、内部256*8bit SRAM、外部512*8bit SRAM、触控检测、12位高精度ADC、RTC、PWM等功能,抗干扰能力强,适用于滑条遥控器、智能门锁、消费类电子产品等电子应用领域。 …...
嵌入式STM32学习——串口USART 2.0(printf重定义及串口发送)
printf重定义: C语言里面的printf函数默认输出设备是显示器,如果要实现printf函数输出正在串口或者LCD显示屏上,必须要重定义标准库函数里调用的与输出设备相关的函数,比如printf输出到串口,需要将fputc里面的输出指向…...
进程信号(上)【Linux操作系统】
文章目录 进程信号信号引入进程要如何识别信号?进程接收到信号的时候,不一定马上处理信号进程处理信号的情况 信号相关概念信号产生键盘产生通过指令向进程发送信号系统调用向进程发送信号软件条件异常错误 操作系统如何知道进程出现了异常错误ÿ…...
全方位详解微服务架构中的Service Mesh(服务网格)
一、引言 随着微服务架构的广泛应用,微服务之间的通信管理、流量控制、安全保障等问题变得日益复杂。服务网格(Service Mesh)作为一种新兴的技术,为解决这些问题提供了有效的方案。它将服务间通信的管理从微服务代码中分离出来&a…...
bi工具是什么意思?bi工具的主要功能有哪些?
目录 一、BI 工具是什么意思? 1. 基本概念 2. 发展历程 编辑二、BI 工具的主要功能 1. 数据连接与整合 2. 数据存储与管理 3. 数据分析与挖掘 4. 可视化呈现 5. 报表生成与分享 6. 实时监控与预警 三、BI 工具的应用场景 1. 销售与营销 2. 财务与会计…...
cocos creator使用jenkins打包微信小游戏,自动上传资源到cdn,windows版运行jenkins
cocos 版本2.4.11 在windows上jenkins的具体配置和部署,可参考上一篇文章cocos creator使用jenkins打包流程,打包webmobile_jenkins打包,发布,部署cocoscreator-CSDN博客 特别注意,windows上运行jenkins需要关闭windows自己的jenkins服务&a…...
PaddleOCR的Pytorch推理模块
概述 在项目中,遇到文字识别OCR的使用场景。 然而,目前效果最好的PaddleOCR只能用百度的PaddlePaddle框架运行。 常见项目中,往往使用更普遍的Pytorch框架,单独安装PaddlePaddle不仅会让项目过于臃肿,而且可能存在冲…...
操作系统期末复习(一)
一、选择 1.从用户的观点看,操作系统是() A.用户与计算机之间的接口 B.控制和管理计算机资源的软件 C.合理地组织计算机工作流程的软件 由若干层次的程序按一定的结构组成的有机体 答案:A 2.操作系统在计算机系统中位于&#x…...
今日行情明日机会——20250521
上证指数缩量收阳线,个股跌多涨少,整体处于日线上涨末端,注意风险。 深证指数,出现60分钟的顶分型,需要观察方向的选择。 2025年5月21日涨停股主要行业方向分析 并购重组 涨停家数:9家。 代表标的&am…...
传统Spring MVC + RESTful 与 Vue3 结合 JWT Token 验证的示例
以下是针对非Spring Boot项目(传统Spring MVC)的示例 一、项目结构 src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── example/ │ │ ├── config/ # 配置类目录 │ │ │ ├─…...
使用Redis的Bitmap实现了签到功能
思路分析 我们可以把 年和月 作为BitMap的key,然后保存到一个BitMap中,每次签到就到对应的位上把数字从0 变为1,只要是1,就代表是这一天签到了,反之咋没有签到。 关键问题 问题一: 什么叫做连续签到天数…...
Unity-编辑器扩展-其二
今天我们来基于之前提到的编辑器扩展的内容来做一些有实际用处的内容: 检查丢失的组件 首先是一个比较实际的内容:当我们在做项目时,经常会涉及到预设体在不同项目或者不同文件路径下的转移,这个时候很容易在某个具体的prefab对…...
项目中Warmup耗时高该如何操作处理
1)项目中Warmup耗时高该如何操作处理 2)如何在卸载资源后Untracked和Other的内存都回收 3)总Triangles的值是否包含了通过GPU Instancing画的三角形 4)有没有用Lua来修复虚幻引擎中对C代码进行插桩Hook的方案 这是第432篇UWA技术知…...
php://filter的trick
php://filter流最常见的用法就是文件包含读取文件,但是它不止可以用来读取文件,还可以和RCE,XXE,反序列化等进行组合利用 filter协议介绍 php://filter是php独有的一种协议,它是一种过滤器,可以作为一个中…...
STM32 I2C硬件读写
一、I2C外设简介 STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担支持多主机模型(固定多主机、可变多主机)支持7位/10位地址模式支持不同的通讯速度&a…...
Qt+线段拖曳示例代码
Qt线段拖曳示例代码,功能见下图。 代码如下: canvaswidget.h #ifndef CANVASWIDGET_H #define CANVASWIDGET_H#include <QWidget> #include <QPainter> #include <QMouseEvent> #include <QVector>class CanvasWidget : publi…...
计算机网络相关面试题
一、HTTP1.1和HTTP2的区别 HTTP/1(主要指 HTTP/1.1)和 HTTP/2 是 Web 协议发展中的两个重要版本,二者在性能、协议机制和功能特性上有显著差异。以下从多个维度对比分析,并结合具体案例说明: 一、连接与请求处理方式 1…...
docker中部署Universal Media Server (UMS)
Universal Media Server (UMS) 本身主要是作为桌面服务程序开发的(主要面向 Java GUI DLNA 播放),但确实可以通过 Docker 进行部署。虽然官方没有提供 Docker 镜像,但社区有一些可用的方式可以在 Docker 中运行它。 下面是一个可…...
WordPress Elementor零基础教程
一、WordPress Elementor 是什么?—— 可视化网站搭建 “积木工具箱” 基础定义 Elementor 是 WordPress 的一款可视化页面构建插件,就如同网站搭建领域的 “PPT 编辑器”。它能让你无需编写代码,仅通过拖放模块(像图片、文本、…...
鸿蒙UI开发——实现一个上拉抽屉效果
1、概 述 在项目开发中,我们可能会遇到临时交互的场景(即:弹出一个临时交互框,交互完毕后继续用户的主流程),效果如下: 在ArkUI中,此类弹出窗被称为“半模态页面”,ArkU…...
详细介绍Qwen3技术报告中提到的模型架构技术
详细介绍Qwen3技术报告中提到的一些主流模型架构技术,并为核心流程配上相关的LaTeX公式。 这些技术都是当前大型语言模型(LLM)领域为了提升模型性能、训练效率、推理速度或稳定性而采用的关键组件。 1. Grouped Query Attention (GQA) - 分组…...
docker面试题(3)
如何临时退出一个正在交互的容器的终端,而不终止它 按ctrlp,后按ctrlq ,如果按ctrlc会使容器内的应用进程终止,进而会使容器终止 很多应用容器都默认是后台运行的,怎么查看它们输出的日志信息 使用docker logs &#…...
2025年二级等保实施全攻略:传统架构与云等保方案深度解析
2025年,随着《网络安全法》的深化落实和等保2.0标准的全面推行,二级等保已成为中小企业及非核心业务系统的合规基线。如何在高效满足监管要求的同时,兼顾成本与安全效能?本文将结合最新政策与实战经验,从传统架构到云等…...
技术点对比
数据库 数据库程序在线访问与ORM访问的对比 数据库程序在线ORM访问优点性能好性能差可以处理复杂sql缺点 性能: 复杂sql支持: 开发成本: 架构风格 管道-过滤器风格与数据仓库风格对比 管道-过滤器风格数据仓储风格备注交互方式顺序结构…...
自监督学习与监督学习
🔍 一、监督学习 vs 自监督学习:核心区别 维度监督学习(Supervised Learning)自监督学习(Self-Supervised Learning)是否需要人工标注的标签✅ 需要,如分类标签、边界框等❌ 不需要,…...
Java操作数据库,JDBC
package myjdbc; import com.mysql.jdbc.Driver; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; /*** 练习JDBC,完成一些简单的操作。*/ public class jdbc01 {public static void main(Str…...
UML 活动图 (Activity Diagram) 使用案例
UML 活动图使用案例 UML 活动图 (Activity Diagram) 使用案例活动图的主要元素典型使用案例1. 用户登录流程2. 在线购物流程3. 订单处理系统4. 文件审批流程 活动图的优势何时使用活动图 UML 活动图 (Activity Diagram) 使用案例 活动图是UML中用于描述业务流程或系统工作流程…...
回溯法求解N皇后问题
目录 前言 一、回溯法是什么? 二、N皇后问题描述 分析解题思路 三、算法设计 1、递归法 2、非递归法 总结 前言 本文将从递归形式和非递归形式两种方法来介绍求解N皇后问题的回溯法,后续也会更新更多有关算法分析这方面的问题欢迎大家关注~🤩…...
网络流量分析工具ntopng的安装与基本使用
网络流量分析工具ntopng的安装与基本使用 一、ntopng基本介绍1.1 ntopng简介1.2 主要特点1.3 使用场景 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、安装ntopng工具3.1 官网地址3.2 配置软件源3.3 添加软件源3.4 安装ntopng 四、ntopng的基本配置4.1 修改配置文件4.…...
新导游入行规范与职业发展指导
随着旅游行业的蓬勃发展,导游作为旅游服务的重要环节,其职业素养和专业能力备受关注。对于新入行的导游而言,了解行业规范,明确职业发展方向,是开启职业生涯的重要一步。 一、严格遵守行业规范 持证上岗…...
数据结构与算法——堆
堆 树树的概念与结构树的相关术语树的表示树形结构实际运用场景 二叉树概念与结构特殊的二叉树满二叉树完全二叉树 二叉树存储结构顺序结构链式结构 实现顺序结构二叉树堆的概念与结构堆的实现向上调整算法(插入数据)向下调整算法 堆的应用堆排序(建堆)向…...
【写在创作纪念日】基于SpringBoot和PostGIS的各省东西南北四至极点区县可视化
目录 前言 一、空间检索简介 1、空间表结构 2、四至空间检索 二、前后端实现 1、后端实现 2、前端集成 三、成果展示 1、东部省份 2、西部省份 3、南部省份 4、北部省份 5、中部省份 四、总结 前言 在当今数字化时代,地理信息数据的分析与可视化对于众…...
AI驱动新增长:亚马逊Rufus广告点击率提升300%的奥秘
在生成式人工智能迅速融入商业应用的背景下,全球跨境电商巨头亚马逊(Amazon)正以前所未有的速度重构其广告生态。2024年第一季度,据亚马逊官方披露,通过部署内部开发的AI购物助手“Rufus”,其平台部分广告点…...
osgEarth中视角由跟随模式切换到漫游模式后没有鼠标拖拽功能问题分析及解决方法
遇到了一个棘手的问题,就是在由跟随模式切换到漫游模式的时候,鼠标无法实现拖拽功能。后来发现是前面给自己挖的坑。 因为要实现鼠标点选某个模型后,模型需要变红色显示,所以添加了一个事件处理程序。 // 创建 场景中模型的点选功能 事件处理程序 ModelSelectionHandler* …...
网页 HTML布局(详解)
本篇讲的是:构成网页的三要素中的HTML HTML的基本结构标签: html标签:网页的整体 head标签:网页的头部 body标签:网页的身体 title标签:网页的标题 一般我们新建一个HTML就会带有这些基本的标签:…...
为什么可以不重写m1方法
在 Java 中,当一个类继承另一个类并同时实现接口时,如果接口中的方法签名与父类中的方法签名完全相同(包括方法名、参数列表和返回类型),那么父类的方法会自动满足接口的实现要求,子类无需显式重写该方法。…...
深入解析应用程序分层及 BaseDao 的封装策略
目录 1. 应用程序分层 1.1. 应用程序分层简介 1.1.1. 三层结构 1.1.2. 分层的优点 1.1.3. 分层命名 1.2. 应用程序分层实现 1.3. 在分层项目中实现查询业务 2. 封装通用的BaseDao 2.1. 封装通用的DML操作 2.2. 封装通用的查询操作 3. 总结 前言 本文讲解JDBC中的应用…...
物理机做完bond后network服务重启失败
问题描述: 物理机通过systemctl status network.service查看网络服务情况,服务状态为failed,报错:Failed to start LSB: Bring up/down netw 问题分析: 1、network服务于NetworkManager服务冲突 2、未使用的网卡没…...
AGI大模型(30):LangChain链的基本使用
为开发更复杂的应用程序,需要使用Chain来链接LangChain中的各个组件和功能,包括模型之间的链接以及模型与其他组件之间的链接。 链在内部把一系列的功能进行封装,而链的外部则又可以组合串联。 链其实可以被视为LangChain中的一种基本功能单元。 API地址:https://python.…...
什么导致ERP系统中BOM表频繁出错?关键因素与解决路径
企业引入 ERP 系统后,常因 BOM(物料清单)维护不规范导致计划混乱、成本失控等问题。部分工厂依赖手工录入 BOM 数据,存在版本管理缺失、替代物料未标注等现象,使得 MRP 计划出错率高,生产效率与质量双降。解…...