用到的数据结构如下:
cpp文件如下:
测试代码如下:
以下是合并图片的测试效果:
合并的每一张小图片是从一张合照里剪切下来的,每张小图片的大小是不一样的,而且是未知的。从效果可以看得出,该算法尽可能地将图片挤在一起,但也还有待改进之处。
- //一个方框
- struct RectItem
- {
- unsigned int x; //方框在合并后的图片的x坐标
- unsigned int y; //方框在合并后的图片的y坐标
- unsigned int width; //方框的宽
- unsigned int height; //方框的高
- };
- //一张图片
- struct ImageItem
- {
- unsigned int id; //图片item的ID
- unsigned int x; //图片item在合并后的图片的x坐标
- unsigned int y; //图片item在合并后的图片的y坐标
- char filename[256]; //图片名字
- BITMAP bmp; //BITMAP结构,只用于到高宽
- HBITMAP hbmp; //图片handle,借用windows API合并图片
- };
- //图片列表
- class ImageList
- {
- public:
- void Insert(CString strPath);
- ImageList();
- ~ImageList();
- int GetMaxHeight();
- int GetMaxWidth();
- BOOL merge(CString outfile); //生成合并后的图片
- void InitFromStringArray(CString *pStr, int count);
- void LoadFromIni(CString strPath);
- void SaveToIni(CString strPath);
- int GetImageCount();
- ImageItem images[128]; //待合并的图片数组
- private:
- void MyBitBlt(int index); //计算出images[index]在合并后图片的坐标
- void DeleteRectItem(int index);
- void InsertRectItem( RectItem * item );
- void clean();
- RectItem rects[256]; //合并时产生的空隙(方框)数组
- int merge_image_width; //合并后图片的宽,实际上是等于要合并的图片中宽最大的图片的宽
- int merge_image_height; //合并后图片的高
- int m_imageCount; //待合并的图片数
- int m_rectCount; //空隙(方框)方框数
- };
- extern ImageList GlobalImageList;
- ImageList::ImageList()
- {
- memset( images, 0, sizeof(images) );
- memset( rects, 0, sizeof(rects) );
- m_imageCount = 0;
- m_rectCount = 0;
- merge_image_width = 0;
- merge_image_height = 0;
- }
- ImageList::~ImageList()
- {
- clean();
- }
- int ImageList::GetImageCount()
- {
- return m_imageCount;
- }
- void ImageList::Insert(CString strPath)
- {
- ImageItem item;
- item.hbmp = (HBITMAP)LoadImage(AfxGetInstanceHandle(),
- (LPCTSTR)strPath,
- IMAGE_BITMAP,
- 0,
- 0,
- LR_LOADFROMFILE);
- if ( item.hbmp == NULL)
- {
- CString str;
- str.Format("无法打开指定的图片: %s", strPath);
- AfxMessageBox(str);
- }else
- {
- GetObject(item.hbmp, sizeof(BITMAP), &item.bmp);
- }
- //memset(images[m_imageCount].filename, 0, sizeof(images[m_imageCount].filename);
- strcpy(item.filename, strPath.GetBuffer(strPath.GetLength()));
- //对images进行排序
- for (int i = 0; i < m_imageCount; i++)
- {
- if (item.bmp.bmWidth > images[i].bmp.bmWidth )
- break;
- if (item.bmp.bmWidth == images[i].bmp.bmWidth && item.bmp.bmHeight > images[i].bmp.bmHeight )
- break;
- }
- for (int j = m_imageCount; j > i; j--)
- {
- memcpy(&images[j], &images[j-1], sizeof(ImageItem) );
- }
- memcpy(&images[i], &item, sizeof(ImageItem) );
- m_imageCount++;
- //设置ID
- for (i = 0; i < m_imageCount; i++)
- {
- images[i].id = i + 1;
- }
- }
- BOOL ImageList::merge(CString outfile)
- {
- merge_image_width = GetMaxWidth();
- for (int i = 0; i < m_imageCount; i++)
- {
- MyBitBlt(i);
- }
- unsigned int width = merge_image_width;
- unsigned int height = merge_image_height;
- HDC imgDC = CreateCompatibleDC( NULL );
- VOID * pbits32;
- HBITMAP holdBmp,hbm32;
- BITMAPFILEHEADER hdr;
- BITMAPINFOHEADER bmi;
- unsigned char* buffer;
- HANDLE hFile;
- int LineDataBytes=width*4;
- int Zero=(4-((width*3)%4))%4;
- int TotalDataBytes=(width*3+Zero)*height;
- BOOL result=FALSE;
- if(width<=0 || height<=0)
- return FALSE;
- hdr.bfType = ((WORD) ('M' << 8) | 'B'); //"BM"
- hdr.bfReserved1 = 0;
- hdr.bfReserved2 = 0;
- hdr.bfOffBits = (DWORD) (sizeof(hdr)+sizeof(bmi));
- hdr.bfSize = (width*3+Zero)*height + hdr.bfOffBits;
- bmi.biSize=sizeof(BITMAPINFOHEADER);
- bmi.biWidth=width;
- bmi.biHeight=height;
- bmi.biPlanes=1;
- bmi.biBitCount=24;
- bmi.biCompression=BI_RGB;
- bmi.biSizeImage=0;
- bmi.biXPelsPerMeter=0;
- bmi.biYPelsPerMeter=0;
- bmi.biClrUsed=0;
- bmi.biClrImportant=0;
- hFile=CreateFile(outfile,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
- if(INVALID_HANDLE_VALUE!=hFile)
- {
- DWORD bw;
- WriteFile(hFile,(LPSTR)&hdr, sizeof(hdr),&bw,NULL);
- WriteFile(hFile,(LPSTR)&bmi, sizeof(bmi),&bw,NULL);
- bmi.biBitCount=32;
- if (hbm32 = CreateDIBSection(imgDC, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, &pbits32, NULL, 0) )
- {
- BYTE *p32 = (BYTE *)pbits32;
- LONG *p;
- unsigned int i,j,h=0;
- buffer=(unsigned char*)malloc(TotalDataBytes);
- holdBmp = (HBITMAP)SelectObject(imgDC, hbm32);
- //TODO:将这些数据都变成局部变量
- for (i = 0; i < m_imageCount; i++)
- {
- HDC hdc = CreateCompatibleDC( NULL );
- HBITMAP hOldBmp = (HBITMAP)SelectObject(hdc, images[i].hbmp);
- BitBlt(imgDC, images[i].x, images[i].y, images[i].bmp.bmWidth, images[i].bmp.bmHeight, hdc, 0, 0, SRCCOPY);
- //DeleteObject(SelectObject(hdc, hOldBmp));
- DeleteDC(hdc);
- }
- for (i = 0;i<height; i++)
- {
- for (j = 0;j<width; j++)
- {
- p = (LONG *)p32 + j;
- buffer[h] = GetRValue(*p);
- buffer[h+1] = GetGValue(*p);
- buffer[h+2] = GetBValue(*p);
- h+=3;
- }
- if(Zero)h+=Zero;
- p32 += LineDataBytes;
- }
- WriteFile(hFile,(LPSTR)buffer, TotalDataBytes,&bw,NULL);
- DeleteObject(SelectObject(imgDC, holdBmp));
- free(buffer);
- result=TRUE;
- }
- CloseHandle(hFile);
- }
- DeleteDC(imgDC);
- return result;
- }
- void ImageList::clean()
- {
- for (int i = 0; i < m_imageCount; i++ )
- {
- DeleteObject(images[i].hbmp);
- images[i].hbmp = NULL;
- }
- memset( images, 0, sizeof(images) );
- memset( rects, 0, sizeof(rects) );
- m_imageCount = 0;
- m_rectCount = 0;
- merge_image_width = 0;
- merge_image_height = 0;
- }
- int ImageList::GetMaxHeight()
- {
- int height = 0;
- for (int i = 0; i < m_imageCount; i++)
- {
- if (height < images[i].bmp.bmHeight)
- {
- height = images[i].bmp.bmHeight;
- }
- }
- return height;
- }
- int ImageList::GetMaxWidth()
- {
- int width = 0;
- for (int i = 0; i < m_imageCount; i++)
- {
- if (width < images[i].bmp.bmWidth)
- {
- width = images[i].bmp.bmWidth;
- }
- }
- return width;
- }
- void ImageList::SaveToIni(CString strPath)
- {
- CString sessionName = "Bitmap";
- char key[64];
- char value[1024];
- memset(value, 0, sizeof(value));
- sprintf(value, "%i", m_imageCount);
- WritePrivateProfileString(sessionName, "count", value, strPath);
- for ( int i = 0; i < m_imageCount; i++)
- {
- memset(key, 0, sizeof(key));
- sprintf(key, "bmp%d", i);
- memset(value, 0, sizeof(value));
- WritePrivateProfileString(sessionName, key, images[i].filename, strPath);
- }
- }
- void ImageList::LoadFromIni(CString strPath)
- {
- CString sessionName = "Bitmap";
- char key[64];
- char value[1024];
- CString filePath[100];
- int fileCount = 0;
- //读取bmp文件的个数
- memset(value, 0, sizeof(value));
- GetPrivateProfileString( sessionName, "count", "", value, sizeof(value), strPath );
- sscanf(value, "%i", &fileCount);
- //将每个路径依次存到数据里
- for ( int i = 0; i < fileCount ; i++)
- {
- memset(key, 0, sizeof(key));
- sprintf(key, "bmp%d", i);
- memset(value, 0, sizeof(value));
- GetPrivateProfileString( sessionName, key, "", value, sizeof(value), strPath );
- filePath[i].Format(value);
- }
- //根据CString数组初始化image list
- InitFromStringArray(filePath, fileCount);
- }
- void ImageList::InitFromStringArray(CString *pStr, int count)
- {
- clean();
- for ( int i = 0; i < count; i++, pStr++ )
- {
- Insert(*pStr);
- }
- }
- void ImageList::MyBitBlt(int image_index)
- {
- int i;
- searchproper:
- for ( i = 0; i < m_rectCount; i++ )
- {
- //rect和image一样大小,设置image的坐标,并将删除rects中的一项
- if ( images[image_index].bmp.bmWidth == rects[i].width &&
- images[image_index].bmp.bmHeight == rects[i].height )
- {
- images[image_index].x = rects[i].x;
- images[image_index].y = rects[i].y;
- DeleteRectItem(i);
- return;
- }
- //rect和image的宽一样大小,高rect更大
- if ( images[image_index].bmp.bmWidth == rects[i].width &&
- images[image_index].bmp.bmHeight < rects[i].height )
- {
- images[image_index].x = rects[i].x;
- images[image_index].y = rects[i].y;
- //TODO: check right
- RectItem temp;
- temp.height = rects[i].height - images[image_index].bmp.bmHeight;
- temp.width = rects[i].width;
- temp.x = rects[i].x;
- temp.y = rects[i].y + images[image_index].bmp.bmHeight;
- DeleteRectItem(i);
- InsertRectItem(&temp);
- return;
- }
- //rect和image的高一样大小,宽rect更大
- if ( images[image_index].bmp.bmWidth < rects[i].width &&
- images[image_index].bmp.bmHeight == rects[i].height )
- {
- images[image_index].x = rects[i].x;
- images[image_index].y = rects[i].y;
- RectItem temp;
- temp.height = rects[i].height;
- temp.width = rects[i].width - images[image_index].bmp.bmWidth;
- temp.x = rects[i].x + images[image_index].bmp.bmWidth;
- temp.y = rects[i].y;
- DeleteRectItem(i);
- InsertRectItem(&temp);
- return;
- }
- //rect比image高和宽都大
- if ( images[image_index].bmp.bmWidth < rects[i].width &&
- images[image_index].bmp.bmHeight < rects[i].height )
- {
- images[image_index].x = rects[i].x;
- images[image_index].y = rects[i].y;
- RectItem temp1;
- temp1.height = images[image_index].bmp.bmHeight;
- temp1.width = rects[i].width - images[image_index].bmp.bmWidth;
- temp1.x = rects[i].x + images[image_index].bmp.bmWidth;
- temp1.y = rects[i].y;
- RectItem temp2;
- temp2.height = rects[i].height - images[image_index].bmp.bmHeight;
- temp2.width = rects[i].width;
- temp2.x = rects[i].x;
- temp2.y = rects[i].y + images[image_index].bmp.bmHeight;
- DeleteRectItem(i);
- InsertRectItem(&temp1);
- InsertRectItem(&temp2);
- return;
- }
- }
- //找不到合适的rect,就新建一项,并将其插入合适的地方
- RectItem new_item;
- new_item.height = images[image_index].bmp.bmHeight;
- new_item.width = merge_image_width;
- new_item.x = 0;
- new_item.y = merge_image_height;
- merge_image_height += new_item.height;
- InsertRectItem( &new_item );
- goto searchproper;
- }
- void ImageList::DeleteRectItem(int index)
- {
- for ( int i = index; i < m_rectCount; i++ )
- {
- memcpy( &rects[i], &rects[i+1], sizeof(RectItem) );
- }
- m_rectCount--;
- }
- void ImageList::InsertRectItem( RectItem * item )
- {
- int i, j;
- //找到该插入的位置,将其存到i
- for ( i = 0; i < m_rectCount; i++ )
- {
- if ( item->width < rects[i].width )
- {
- break;
- }
- if ( item->width == rects[i].width &&
- item->height < rects[i].height )
- {
- break;
- }
- }
- //插入
- for ( j = m_rectCount - 1; j >= i ; j-- )
- {
- memcpy( &rects[j+1], &rects[j], sizeof(RectItem) );
- }
- memcpy( &rects[i], item, sizeof(RectItem) );
- m_rectCount++;
- }
- CFileDialog fileDlg(TRUE,
- NULL,
- NULL,
- OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ALLOWMULTISELECT,
- "bmp文件(*.bmp)|*.bmp||",
- NULL);
- fileDlg.m_ofn.lpstrFile = new TCHAR[MAX_PATH* 100];//最多允许100个文件
- fileDlg.m_ofn.nMaxFile = 100*MAX_PATH;
- ZeroMemory(fileDlg.m_ofn.lpstrFile, sizeof(TCHAR) * fileDlg.m_ofn.nMaxFile);
- if( fileDlg.DoModal() != IDOK ) return;
- //TODO: init bmp ini data
- CString filePath[100];
- //获取图片路径
- POSITION pos = fileDlg.GetStartPosition();
- int fileCount;
- for (fileCount = 0; pos!=0; fileCount++)
- {
- CString file = fileDlg.GetNextPathName(pos);
- if( file.Find(".bmp") == -1)
- {
- AfxMessageBox("非bmp文件,程序退出。");
- exit(1);
- }
- filePath[fileCount] = file;
- }
- GlobalImageList.InitFromStringArray(filePath, fileCount);
- GlobalImageList.merge("merge.bmp");
合并的每一张小图片是从一张合照里剪切下来的,每张小图片的大小是不一样的,而且是未知的。从效果可以看得出,该算法尽可能地将图片挤在一起,但也还有待改进之处。