http://download.csdn.net/download/lxl123/3708381
#if !defined(AFX_PICTEXTCTRL_H__F504DD42_3730_4D17_BE21_79EF8522A887__INCLUDED_)
#define AFX_PICTEXTCTRL_H__F504DD42_3730_4D17_BE21_79EF8522A887__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// PicTextCtrl.h : header file
//
#include <imaging.h>
#include <afxtempl.h>
#define WM_UPDETE_PICTEXT (WM_USER + 100)
typedef struct _tagPicData{
int oft;//在文本中的偏移
int x,y;//用于显示时的坐标
int width,hight;
int start,end;//开始结束行号
int hasText;//单行图像后面可以显示的文字个数
HBITMAP pic;
}PicData_T;
typedef struct _tagTextLine{
int start,end;
// int hasText;
}TextLine;
/////////////////////////////////////////////////////////////////////////////
// CPicTextCtrl window
class CPicTextCtrl : public CWnd
{
// Construction
public:
CPicTextCtrl();
// Attributes
public:
enum{
PIC_AT_START,
PIC_AT_END,
PIC_AT_MID
};
protected:
#ifdef USE_IIMAGE
IImage *image;
IImagingFactory *imgFac;
IBitmapImage *imgBmp;
IImageDecoder *imgDec;
IImageSink *imgSik;
#endif
HDC m_bkDC;
HBITMAP m_bkbmp,m_bkold;
private:
CArray<TextLine,TextLine&> m_txtArray;
CArray<PicData_T,PicData_T&> m_picArray;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CPicTextCtrl)
//}}AFX_VIRTUAL
// Implementation
public:
BOOL AddPic(CString fname,DWORD offset,int picOft);
void SetStartShowLine(int line);
void SetMargins(int left,int right);
int GetViewLineCount();
void SetTextFont(int i);
BOOL Create(DWORD dwStyle,const RECT& rect,CWnd* pParentWnd,UINT nID,CCreateContext* pContext = NULL);
BOOL AddPic(CString fname,int oft);
BOOL AddPic(CFile& file,DWORD offset,DWORD len,int picOft);
void SetText(CString str);
void Reset();
virtual ~CPicTextCtrl();
// Generated message map functions
protected:
HBITMAP myGetBitmapFromFile(CString fname,int *w,int *h);
HBITMAP myGetBitmapFromFile(CFile &pFile,DWORD offset,DWORD len,int *w,int *h);
#ifdef USE_IIMAGE
HBITMAP GetHBitmapFromFile(CFile& pFile,DWORD offset,DWORD len,int *w,int *h);
HBITMAP GetHBitmapFromFile(CString fname,int *w,int *h);
void ResetImage();
#endif
//{{AFX_MSG(CPicTextCtrl)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint();
afx_msg void OnUpdatePicText(WPARAM wParam,LPARAM lParam);
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnWindowPosChanged(WINDOWPOS FAR* lpwndpos);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
void GetPictruePixels(LPBITMAPINFO pbi,BYTE *src,BYTE *dst);
int DrawTextWidth(CString str,int width);
int m_lMarg,m_rMarg;
void SortPicture();
BOOL HasPicture(int start,int end,int *ret);
int m_pCount;
int GetShowPicArea(int pNum, int inViewLine, int inTotalLine, int *left, int *top, int *w, int *h);
int GetShowPictrue(int curLine);
void SetScrollbar();
int FindNextSplit(TCHAR *str);
void CalcPicLine(int *i,int *j,int *oft,int pos,int cnt,int lcnt,int mod);
CFont m_fnt;
int GetPicLineCount(int pos);
BOOL HandlePictrue(int *i,int *j,int *oft);
int HasPicture(int pos,int *first);
CScrollBar *m_scrbar;
void CalcTextOft();
CString m_str;
int m_viewLine;
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_PICTEXTCTRL_H__F504DD42_3730_4D17_BE21_79EF8522A887__INCLUDED_)
// PicTextCtrl.cpp : implementation file
//
#include "stdafx.h"
//#include "initguid.h"
#include "PicTextCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define PICTEXT_SCRBAR 0x1234
#define isEngWordChar(ch) ((iswascii(ch) && iswalpha(ch)))
/////////////////////////////////////////////////////////////////////////////
// CPicTextCtrl
CPicTextCtrl::CPicTextCtrl()
{
m_scrbar = NULL;
m_pCount = 0;
m_lMarg = m_rMarg = 2;
LOGFONT fnt;
memset(&fnt,0,sizeof(LOGFONT));
fnt.lfHeight = 24;
fnt.lfWidth = 0;
fnt.lfWeight = 400;
fnt.lfItalic = FALSE;
#if ISGDX
lstrcpy(fnt.lfFaceName,_T("Arial"));
#else
fnt.lfQuality = CLEARTYPE_QUALITY;
fnt.lfCharSet = GB2312_CHARSET;
lstrcpy(fnt.lfFaceName,_T("华文宋体"));
#endif
m_fnt.CreateFontIndirect(&fnt);
#ifdef USE_IIMAGE
imgFac=NULL;
imgBmp=NULL;
imgDec=NULL;
imgSik=NULL;
image=NULL;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
#endif
}
CPicTextCtrl::~CPicTextCtrl()
{
#ifdef USE_IIMAGE
ResetImage();
if(imgFac)
imgFac->Release();
#endif
Reset();
SelectObject(m_bkDC,m_bkold);
DeleteObject(m_bkbmp);
DeleteDC(m_bkDC);
DeleteObject(m_bkold);
if(m_scrbar)
delete m_scrbar;
#ifdef USE_IIMAGE
CoUninitialize();
#endif
}
BEGIN_MESSAGE_MAP(CPicTextCtrl, CWnd)
//{{AFX_MSG_MAP(CPicTextCtrl)
ON_WM_CREATE()
ON_WM_PAINT()
ON_MESSAGE(WM_UPDETE_PICTEXT,OnUpdatePicText)
ON_WM_VSCROLL()
ON_WM_ERASEBKGND()
ON_WM_WINDOWPOSCHANGED()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CPicTextCtrl message handlers
int CPicTextCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
#ifdef USE_IIMAGE
if(FAILED(CoCreateInstance(CLSID_ImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_IImagingFactory,(void**) &imgFac)))
return -1;
#endif
RECT rtscr;
rtscr.left = lpCreateStruct->cx - GetSystemMetrics(SM_CXVSCROLL);
rtscr.top = 0;
rtscr.right = lpCreateStruct->cx;
rtscr.bottom = lpCreateStruct->cy;
m_scrbar = new CScrollBar;
if(!m_scrbar->Create(WS_CHILD|WS_VISIBLE|WS_DISABLED|SBS_VERT,rtscr,this,PICTEXT_SCRBAR))
return -1;
HDC pdc = ::GetDC(m_hWnd);
m_bkDC = ::CreateCompatibleDC(pdc);
m_bkbmp = ::CreateCompatibleBitmap(pdc,lpCreateStruct->cx,lpCreateStruct->cy);
m_bkold = (HBITMAP)::SelectObject(m_bkDC,m_bkbmp);
::ReleaseDC(m_hWnd,pdc);
// TRACE(_T("m_bkddc = %p,m_bkbmp = %p\n"),m_bkDC.m_hDC,m_bkbmp.m_hObject);
return 0;
}
void CPicTextCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
if(m_str.IsEmpty()) return;
// TRACE(_T("call pictext onpaint\n"));
// TODO: Add your message handler code here
LOGFONT fnt;
TextLine tline;
CString txtLine;
RECT rtClient,rtLine;
GetClientRect(&rtClient);
int x,y,w,h;
int i = 0,j,k,total;//,viewLine = GetViewLineCount();
HDC memDC = ::CreateCompatibleDC(dc.m_hDC);
HBITMAP bmp = ::CreateCompatibleBitmap(dc.m_hDC,rtClient.right,rtClient.bottom);
HBITMAP old = (HBITMAP)::SelectObject(memDC,bmp);
m_fnt.GetLogFont(&fnt);
::SelectObject(memDC,m_fnt.m_hObject);
// rtClient.right -= (GetSystemMetrics(SM_CXVSCROLL)-2);//往右边多画一些
::FillRect(memDC,&rtClient,::GetSysColorBrush(COLOR_WINDOW));
::Rectangle(memDC,0,0,rtClient.right,rtClient.bottom);
rtClient.right -= (m_rMarg + GetSystemMetrics(SM_CXVSCROLL));
rtLine.left = rtLine.top = m_lMarg;
rtLine.bottom = rtClient.bottom,rtLine.right = rtClient.right;
if(m_scrbar->IsWindowEnabled())
{
i = m_scrbar->GetScrollPos();
}
total = i + m_viewLine;//viewLine;
while(i < total)
{
if(i >= m_txtArray.GetSize())
break;
if((j = GetShowPictrue(i)) >= 0)//得到图像编号
{
k = GetShowPicArea(j,i-m_scrbar->GetScrollPos(),i,&x,&y,&w,&h);
if(h > rtLine.bottom - rtLine.top)
h = rtLine.bottom - rtLine.top;
PicData_T pd = m_picArray.GetAt(j);
HBITMAP old = (HBITMAP)::SelectObject(m_bkDC,pd.pic);
::BitBlt(memDC,pd.x,pd.y,w,h,m_bkDC,x,y,SRCCOPY);
::SelectObject(m_bkDC,old);
if(pd.start != pd.end)//图片有多行
{
i += k;
rtLine.top += k * fnt.lfHeight;
continue;
}
else
{
if(pd.hasText != 0)//显示单行图像行的文字
{
tline = m_txtArray.GetAt(i);
::SetBkColor(memDC,RGB(255,255,255));
::SetTextColor(memDC,RGB(0,0,0));
::SetBkMode(memDC,TRANSPARENT);
if(pd.x != m_lMarg)//图像前面有文字
{
txtLine = m_str.Mid(tline.start,tline.end -tline.start -pd.hasText);
int slen = txtLine.GetLength();
if(txtLine.Right(1) == '\n')
slen--;
::DrawText(memDC,txtLine.GetBuffer(0),slen,&rtLine,DT_SINGLELINE);
txtLine.ReleaseBuffer();
}
txtLine = m_str.Mid(tline.end - pd.hasText,pd.hasText);
int slen = txtLine.GetLength();
if(txtLine.Right(1) == '\n')
slen--;
rtLine.left = pd.x + pd.width;
::DrawText(memDC,txtLine.GetBuffer(0),slen,&rtLine,DT_SINGLELINE);
txtLine.ReleaseBuffer();
rtLine.left = m_lMarg;
rtLine.top += fnt.lfHeight;
i++;
continue;
}
}
}
tline = m_txtArray.GetAt(i);
if(tline.end != 0)//显示一行文字
{
txtLine = m_str.Mid(tline.start,tline.end - tline.start);
int slen = txtLine.GetLength();
if(txtLine.Right(1) == '\n')//换行符不显示
slen--;
::SetBkColor(memDC,RGB(255,255,255));
::SetBkMode(memDC,TRANSPARENT);
::SetTextColor(memDC,RGB(0,0,0));
::DrawText(memDC,txtLine.GetBuffer(0),slen,&rtLine,DT_SINGLELINE);
txtLine.ReleaseBuffer();
}
rtLine.top += fnt.lfHeight;
i++;
}
rtClient.right += m_rMarg;
::BitBlt(dc.m_hDC,0,0,rtClient.right,rtClient.bottom,memDC,0,0,SRCCOPY);
::SelectObject(memDC,old);
::DeleteObject(bmp);
::DeleteDC(memDC);
m_scrbar->SendMessage(WM_PAINT);
// Do not call CWnd::OnPaint() for painting messages
}
BOOL CPicTextCtrl::AddPic(CFile& file,DWORD offset,DWORD len,int picOft)
{
if(picOft > m_str.GetLength() - 1)
return FALSE;
int w,h;
#ifdef USE_IIMAGE
HBITMAP hb = GetHBitmapFromFile(file,offset,len,&w,&h);
#else
HBITMAP hb = myGetBitmapFromFile(file,offset,len,&w,&h);
#endif
if(!hb)
return FALSE;
PicData_T pd;
pd.width = w, pd.hight = h;
pd.hasText = 0;
pd.oft = picOft;
pd.pic = hb;
m_picArray.Add(pd);
SortPicture();
return TRUE;
}
#ifdef USE_IIMAGE
HBITMAP CPicTextCtrl::GetHBitmapFromFile(CFile &pFile,DWORD offset,DWORD len,int *w,int *h)
{
HBITMAP hb = NULL;
HRESULT hr;
ResetImage();
BYTE *pBuf = new BYTE[len];
if(!pBuf)
{
goto out;
}
pFile.Seek(offset,CFile::begin);
if(pFile.Read(pBuf,len) != len)
{
goto out;
}
hr = imgFac->CreateImageFromBuffer(pBuf,len,BufferDisposalFlagNone,&image);
if(FAILED(hr))
{
delete[] pBuf;
}
ImageInfo info;
SIZE sz;
image->GetImageInfo(&info);
imgFac->CreateBitmapFromImage(image,info.Width,info.Height,PixelFormatDontCare,InterpolationHintDefault,&imgBmp);
imgBmp->GetSize(&sz);
*w = sz.cx, *h = sz.cy;
BITMAP bt;
RECT rt1;
GetClientRect(&rt1);
rt1.right -= GetSystemMetrics(SM_CXVSCROLL);
image->Draw(m_bkDC,&rt1,NULL);
::GetObject(m_bkbmp,sizeof(BITMAP),&bt);
BitmapData bd;
RECT rt;
rt.left=0,rt.top=0;
rt.right=sz.cx,rt.bottom=sz.cy;
hr=imgBmp->LockBits(&rt,ImageLockModeRead,PixelFormatDontCare,&bd);
if(SUCCEEDED(hr))
{
hb=::CreateBitmap(sz.cx,sz.cy,1,bt.bmBitsPixel,bd.Scan0);
//::SelectObject(m_bkDC.m_hDC,hb1);
imgBmp->UnlockBits(&bd);
}
out:
if(pBuf)
delete[] pBuf;
return hb;
}
HBITMAP CPicTextCtrl::GetHBitmapFromFile(CString fname,int *w,int *h)
{
HBITMAP hb = NULL;
HRESULT hr;
ResetImage();
hr =imgFac->CreateImageFromFile(fname,&image);
if (FAILED(hr))
{
if(imgFac)
imgFac->Release();
imgFac = NULL;
return NULL;
}
ImageInfo info;
SIZE sz;
image->GetImageInfo(&info);
imgFac->CreateBitmapFromImage(image,info.Width,info.Height,PixelFormatDontCare,InterpolationHintDefault,&imgBmp);
imgBmp->GetSize(&sz);
*w = sz.cx, *h = sz.cy;
BITMAPINFO pBmi = { { sizeof(BITMAPINFOHEADER),info.Width,info.Height, 1, 32, NULL/*BI_ALPHABITFIELDS*/} };
void * pBits = NULL;
hb = ::CreateDIBSection(NULL, & pBmi,DIB_RGB_COLORS, &pBits, NULL, NULL);
HDC hdc = ::GetDC(m_hWnd);
HDC bmpDC = ::CreateCompatibleDC(hdc);
RECT rect = {0, 0, info.Width, info.Height};
HGDIOBJ oldBmp = SelectObject(bmpDC, hb);
image->Draw(bmpDC, &rect, 0);
SelectObject(bmpDC, oldBmp);
::DeleteDC(bmpDC);
::ReleaseDC(m_hWnd,hdc);
return hb;
}
void CPicTextCtrl::ResetImage()
{
if (image)
image->Release();
image = NULL;
if(imgBmp)
imgBmp->Release();
imgBmp = NULL;
if(imgSik)
imgSik->Release();
imgSik = NULL;
if(imgDec)
imgDec->Release();
imgDec = NULL;
}
#endif
void CPicTextCtrl::SetText(CString str)
{
if(!m_str.IsEmpty())
m_str.Empty();
// m_str.FreeExtra();
if(str[str.GetLength()-1] != '\0')
{
m_str = str + '\0';//因为图片可能在最后,所以在文字后加结束标记
// TRACE(_T("string right != 0,str lenght =%d,m_str lenght =%d\n"),str.GetLength(),m_str.GetLength());
}
else
m_str = str;
}
BOOL CPicTextCtrl::AddPic(CString fname, int oft)
{
if(oft > m_str.GetLength())//- 1)
return FALSE;
int w,h;
#ifdef USE_IIMAGE
HBITMAP hb = GetHBitmapFromFile(fname,&w,&h);
#else
HBITMAP hb = myGetBitmapFromFile(fname,&w,&h);
#endif
if(!hb)
return FALSE;
PicData_T pd;
pd.width = w, pd.hight = h;
pd.hasText = 0;
pd.oft = oft;
pd.pic = hb;
m_picArray.Add(pd);
TRACE(_T("add pic bitmap = %p,oft = %d\n"),pd.pic,oft);
SortPicture();
return TRUE;
}
void CPicTextCtrl::CalcTextOft()
{
TCHAR *buf,*tmp;
RECT rtClient;
BOOL haspic,txtend = FALSE;
TextLine tline;
int i=0,j,k,line=0,total = m_str.GetLength() - 1;//因为前面加了'\0'
if(!total)
return;
BOOL bNextLine = FALSE,bEnter = FALSE;
HDC dc = ::GetDC(m_hWnd);
::SelectObject(dc,m_fnt.m_hObject);
GetClientRect(&rtClient);
// GetCharWidth32(dc,_T('W'),_T('W'),&j);//预留一个字的空间
rtClient.right -= (GetSystemMetrics(SM_CXVSCROLL) + m_rMarg);//+j);
tmp = buf = m_str.GetBuffer(0);
while(i <= total)
{
tline.start = i;
m_txtArray.Add(tline);
j = m_lMarg;
if(i == 0 && HasPicture(i,&k))//最开始有图片特殊处理
{
HandlePictrue(&line,&j,&i);
tline.start = i;
}
while(j < rtClient.right)
{
bNextLine = bEnter = FALSE;
if(i > total)
{
txtend = TRUE;
break;
}
if(!*buf)//最后一个字符为0,但此处可能有图像
{
tline.end = i;
m_txtArray.SetAt(line,tline);//最后一行不包含图像,在此结束
j = m_lMarg;
i++;
if(!haspic)//最后没有图片增加一个新行,因为最后要删除多余的一行
tline.end = 0;
m_txtArray.Add(tline);
line++;
}
if(*buf == '\n')//换行0A
{
tline.end = i+1;//?
m_txtArray.SetAt(line++,tline);
tline.start = i+1;//?
m_txtArray.Add(tline);
i++;
buf++;
j = m_lMarg;
bEnter = TRUE;
goto HandlePic;
}
if(!isEngWordChar(*buf))//不是英语单词
{
if(isEngWordChar(*(buf+1)))//下个是英语单词
{
SIZE sz;
k = FindNextSplit(buf + 1);
GetTextExtentPoint32(dc,buf,k,&sz);
if(j + sz.cx > rtClient.right)//剩余的空间不够下个单词要换行
bNextLine = TRUE;
}
GetCharWidth32(dc,*buf,*buf,&k);
j += k;
if(j > rtClient.right)//不够显示转到下一行
break;
else//显示非英语单词,然后再处理图片
{
i++;
buf++;
goto HandlePic;
}
}
else
{
GetCharWidth32(dc,*buf,*buf,&k);
j += k;
i++;
buf++;
}
HandlePic:
haspic = HandlePictrue(&line,&j,&i);
if(haspic)//有可能是图像的下一行文字
{
tline.start = i;//得到起始位置
buf = tmp + i;//?
}
if(bNextLine)//剩下的空间不够单词显示,因此换行
break;
}
if(!txtend)
{
tline.end = i;
m_txtArray.SetAt(line++,tline);
}
else
{
m_txtArray.RemoveAt(line);
}
// TRACE(_T("line index %d string = %s\n"),line,m_str.Mid(tline.start,tline.end-tline.start));
}
::ReleaseDC(m_hWnd,dc);
m_str.ReleaseBuffer();
#if 0
for(i = 0; i < line ;i++)
{
tline = m_txtArray.GetAt(i);
TRACE(_T("line index %d,start = %d,end = %d\n"),i,tline.start,tline.end);
}
#endif
SetScrollbar();
}
int CPicTextCtrl::HasPicture(int pos,int *first)
{
BOOL bfirst = TRUE;
int j,i,ret = 0,count = m_picArray.GetSize();
for(i = 0;i < count;i++)
{
j = m_picArray.GetAt(i).oft;
if(j > pos)
break;
else if(j == pos)
{
if(bfirst)
{
*first = i;//得到第一张的位置
bfirst = FALSE;
}
ret++;
}
}
return ret;
}
//lineNo行号,width行的开始宽度,oft字符偏移
BOOL CPicTextCtrl::HandlePictrue(int *lineNo, int *width, int *oft)
{
int i,hasPic;
int pNum,picLine;
if(m_pCount >= m_picArray.GetSize()) return FALSE;//图像已处理完成,直接返回
hasPic = HasPicture(*oft,&pNum);
if(hasPic)//当前位置有图像
{
if(pNum < m_pCount) return FALSE;//如果得到的偏号小于已处理图片数,说明此位置的图片已处理,主要用在图片在最开始的时候
for(i=0;i<hasPic;i++)//如果同一位置有多张图片
{
picLine = GetPicLineCount(pNum+i);//分别得到每张图片所占行数
if(*oft == 0)//图像在文本最开始
{
CalcPicLine(lineNo,width,oft,pNum+i,i,picLine,PIC_AT_START);
}
else if(*oft==m_str.GetLength() - 1)//图像在最后
{
CalcPicLine(lineNo,width,oft,pNum+i,i,picLine,PIC_AT_END);
}
else//图像在中间
{
CalcPicLine(lineNo,width,oft,pNum+i,i,picLine,PIC_AT_MID);
}
m_pCount++;
}
return TRUE;
}
return FALSE;
}
int CPicTextCtrl::GetPicLineCount(int pos)//得到图像占多少行
{
int ret;
WORD h;
LOGFONT fnt;
m_fnt.GetLogFont(&fnt);
h=m_picArray.GetAt(pos).hight;
ret = h / fnt.lfHeight;
if(h % fnt.lfHeight > fnt.lfHeight / 3)//大于1/3行高的增加一行
ret++;
if(ret == 0)
ret = 1;
return ret;
}
void CPicTextCtrl::CalcPicLine(int *lineNo,int *width,int *oft,int pNum,int num,int picLine,int pos)
{
RECT rtClient;
int cnt;
TextLine tline = m_txtArray.GetAt(*lineNo);
PicData_T pd = m_picArray.GetAt(pNum);
int pcont = HasPicture(*oft,&cnt);//此位置图像数量
GetClientRect(&rtClient);
rtClient.right -= (GetSystemMetrics(SM_CXVSCROLL) + m_rMarg);
if(picLine == 1)//图像只有一行
{
if(pcont == 1 )//此位置只有一张图片
{
if(*width + pd.width < rtClient.right)//当前行能够显示图片
{
pd.start = *lineNo;//图像在当前行显示
pd.end = *lineNo;
if(*width == m_lMarg && pos == PIC_AT_END)//图像在文本结尾,且图像前面无文字
pd.x = m_lMarg + (rtClient.right - m_lMarg - pd.width)/2;//图像在此行的中间显示
else
pd.x = *width;
if(pos == PIC_AT_END)//如果此位置有多张图片,则后面不显示文字
cnt=0;
else
{
int rigth = m_str.GetLength() - *oft;
// if(pos != PIC_AT_START)
// rigth -= 1;//因为当前字符已计算过,所以要-1
CString str = m_str.Right(rigth);
cnt = DrawTextWidth(str,rtClient.right - *width - pd.width);//计算图像后面可以显示的字符个数
}
if(cnt > 0)//有文字
{
int offset;
if(HasPicture(*oft + 1,*oft+cnt,&offset))//在图像后面可以显示的文本里也有图像,则只显示到下个图像之前的部分文字
{
// if(offset > 0)//图像后面可以显示的文字个数
{
*oft += offset;
pd.hasText = offset;
tline.end = *oft;
}
}
else//当前图像显示行无下张图像,则在图像后面显示刚才计算得到的字符个数
{
pd.hasText = cnt;
*oft += cnt;
tline.end = *oft;
}
*width = m_lMarg;//下行开始显示位置
m_txtArray.SetAt(*lineNo,tline);
*lineNo += 1;//增加一个新行
tline.start = *oft;
m_txtArray.Add(tline);
}
else//单行图像后面没有文字
{
if(pos == PIC_AT_END)
{
if(*width == m_lMarg)
tline.end = 0;
else//图像在末尾,前面有文字
tline.end = *oft;
m_txtArray.SetAt(*lineNo,tline);
tline.start = tline.end = 0;
m_txtArray.Add(tline);
*lineNo += 1;
}
else
{
tline.end = *oft;
m_txtArray.SetAt(*lineNo,tline);
tline.start = *oft;
*lineNo += 1;
m_txtArray.Add(tline);
}
*width = m_lMarg;
}
m_picArray.SetAt(pNum,pd);
}
else//当前行不够显示图片
{
tline.end = *oft;
m_txtArray.SetAt((*lineNo)++,tline);
pd.start = pd.end = *lineNo;
if(pd.width >= rtClient.right - m_lMarg)//图像宽度大于控件初始X坐标为0
pd.x = m_lMarg;
else//否则居中显示
pd.x = m_lMarg + (rtClient.right - m_lMarg - pd.width)/2;
m_picArray.SetAt(pNum,pd);
tline.start = tline.end = 0;
m_txtArray.Add(tline);
if(pos != PIC_AT_END)
{
tline.start = *oft;
m_txtArray.Add(tline);
*lineNo += 1;
}
*width = m_lMarg;
}
}
else//此处有多张图片,并且第一张是单行,因此图像后面不显示文字
{
if(num == 0)//第一张单行图像
{
BOOL newline = FALSE;
if(*width > m_lMarg)//单行图像前面有文字
{
if(*width + pd.width > rtClient.right)//此行宽度不够,在下行显示
{
pd.x = pd.x = m_lMarg + (rtClient.right - m_lMarg - pd.width)/2;
newline = TRUE;
}
else
pd.x = *width;
}
else//单行图像前面无文字
pd.x = m_lMarg;
tline.end = *oft;
m_txtArray.SetAt(*lineNo,tline);
if(newline)
{
tline.start = tline.end = 0;
m_txtArray.Add(tline);
*lineNo += 1;
}
pd.start = pd.end = *lineNo;
m_picArray.SetAt(pNum,pd);
tline.start = *oft;
m_txtArray.Add(tline);//增加新行
*lineNo += 1;
}
else
{
tline.end = 0;
m_txtArray.SetAt((*lineNo),tline);//上一行结束
if(pd.width >= rtClient.right - m_lMarg)//图像宽度大于控件初始X坐标为0
pd.x = m_lMarg;
else//否则居中显示
pd.x = m_lMarg + (rtClient.right - m_lMarg - pd.width)/2;
pd.start = *lineNo;
pd.end = *lineNo;
m_picArray.SetAt(pNum,pd);
tline.start = *oft;
m_txtArray.Add(tline);//增加下一行
*lineNo += 1;
}
*width = m_lMarg;
}
}
else//图像有多行则图像所在行不显示文字
{
if(num == 0)//多行图片的第一张
{
if(pos == PIC_AT_START || *width == m_lMarg)//图像在文本开始或者当前是一个新行,则图像从当前行开始显示
pd.start = *lineNo;
else
{
pd.start = *lineNo + 1;//图像在文字下一行
tline.end = *oft;
m_txtArray.SetAt(*lineNo,tline);//设置图像前面的文字行
tline.start = tline.end = 0;
m_txtArray.Add(tline);
*lineNo += 1;
}
tline.start = tline.end = 0;
for(int i=0;i < picLine;i++)
{
m_txtArray.Add(tline);//图像所在行文字为空
}
*lineNo = *lineNo + picLine;// + 1;//增加图像所占行数,并且添加一个新行
pd.end = *lineNo - 1;
if(pos != PIC_AT_END)
{
tline.start = *oft;
m_txtArray.SetAt(*lineNo,tline);
}
if(pd.width >= rtClient.right - m_lMarg)//图像宽度大于控件初始X坐标为0
pd.x = m_lMarg;
else//否则居中显示
pd.x = m_lMarg + (rtClient.right - m_lMarg - pd.width)/2;
*width = m_lMarg;
m_picArray.SetAt(pNum,pd);
}
else//多张图片的后几张
{
pd.start = *lineNo;
for(int i=0;i < picLine;i++)
{
m_txtArray.Add(tline);//图像所在行文字为空
}
*lineNo = *lineNo + picLine;
if(pos != PIC_AT_END)
{
tline.start = *oft;
m_txtArray.SetAt(*lineNo,tline);
}
pd.end = *lineNo - 1;
if(pd.width >= rtClient.right - m_lMarg)//图像宽度大于控件初始X坐标为0
pd.x = m_lMarg;
else//否则居中显示
pd.x = m_lMarg + (rtClient.right - m_lMarg - pd.width)/2;
*width = m_lMarg;
m_picArray.SetAt(pNum,pd);
}
}
}
int CPicTextCtrl::FindNextSplit(TCHAR *str)
{
int i = 0;
TCHAR *buf = str;
while(*buf)
{
if(!isEngWordChar(*buf))
break;
buf++;
i++;
}
return i;
}
BOOL CPicTextCtrl::Create(DWORD dwStyle,const RECT& rect,CWnd* pParentWnd,UINT nID,CCreateContext* pContext)
{
LPCTSTR pClass = ::AfxRegisterWndClass(CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS);
return CWnd::Create(pClass,_T("PicTextCtrl"),dwStyle,rect,pParentWnd,nID,pContext);
}
void CPicTextCtrl::OnUpdatePicText(WPARAM wParam,LPARAM lParam)
{
CRect rt;
GetClientRect(&rt);
rt.left = rt.right - GetSystemMetrics(SM_CXVSCROLL);
m_pCount = 0;
int count = m_txtArray.GetSize();
while (count--)
m_txtArray.RemoveAt(0);
CalcTextOft();
m_scrbar->MoveWindow(&rt,TRUE);
m_viewLine = GetViewLineCount();
Invalidate();
}
void CPicTextCtrl::Reset()
{
int i,count;
SelectObject(m_bkDC,m_bkbmp);
count = m_picArray.GetSize();
for(i=0;i<count;i++)
{
DeleteObject(m_picArray.GetAt(0).pic);
m_picArray.RemoveAt(0);
}
count = m_txtArray.GetSize();
for(i=0;i<count;i++)
m_txtArray.RemoveAt(0);
m_str.Empty();
}
void CPicTextCtrl::SetTextFont(int i)
{
LOGFONT fnt;
m_fnt.GetLogFont(&fnt);
switch(i)
{
case 0:
fnt.lfHeight = 16;
break;
case 1:
fnt.lfHeight = 20;
break;
case 2:
fnt.lfHeight = 24;
break;
}
m_fnt.DeleteObject();
m_fnt.CreateFontIndirect(&fnt);
m_viewLine = GetViewLineCount();
Invalidate();
}
int CPicTextCtrl::GetViewLineCount()
{
RECT rtClient;
LOGFONT fnt;
m_fnt.GetLogFont(&fnt);
GetClientRect(&rtClient);
return rtClient.bottom / fnt.lfHeight;
}
void CPicTextCtrl::SetScrollbar()
{
int total = m_txtArray.GetSize();
int vLine = GetViewLineCount();
if(total > vLine)
{
SCROLLINFO si;
memset(&si,0,sizeof(SCROLLINFO));
si.cbSize = sizeof(SCROLLINFO);
si.nMax = total - 1;
si.nPage = vLine;
si.fMask = SIF_ALL;
m_scrbar->SetScrollInfo(&si);
m_scrbar->EnableWindow(TRUE);
}
else
{
m_scrbar->SetScrollPos(0);
m_scrbar->EnableWindow(FALSE);
}
}
int CPicTextCtrl::GetShowPictrue(int curLine)
{
PicData_T pd;
for(int i = 0; i < m_picArray.GetSize(); i++)
{
pd = m_picArray.GetAt(i);
if(pd.start <= curLine && pd.end >= curLine)
{
return i;
}
}
return -1;
}
//返回图像显示的行数
int CPicTextCtrl::GetShowPicArea(int pNum, int inViewLine, int inTotalLine, int *left, int *top, int *w, int *h)
{
LOGFONT fnt;
int ret;
int vline = GetViewLineCount();
int picLine = GetPicLineCount(pNum);
PicData_T pd = m_picArray.GetAt(pNum);
m_fnt.GetLogFont(&fnt);
*left = 0;
*w = pd.width;
pd.y = inViewLine * fnt.lfHeight;
if(pd.hight < fnt.lfHeight)
pd.y += (fnt.lfHeight - pd.hight) / 2;
else
pd.y += 1;
if(inTotalLine > pd.start)//显示图像下部分
{
if(pd.end - inTotalLine <= vline - inViewLine)//显示图像完整的下部分
{
*h = (pd.end - inTotalLine + 1) * fnt.lfHeight;
*top = pd.hight - *h;
ret = pd.end - inTotalLine + 1;
}
else//显示图像中间部分
{
*top = (inTotalLine - pd.start) * fnt.lfHeight;
*h = (vline - inViewLine) * fnt.lfHeight;
ret = vline - inViewLine;
}
}
else if(inTotalLine == pd.start)//显示图像上部分
{
*top = 0;
if(picLine <= vline - inViewLine)//可以完整显示
{
*h = pd.hight;
ret = picLine;
}
else//显示上部分,不完整
{
*h = (vline - inViewLine) * fnt.lfHeight;
ret = (vline - inViewLine);
}
}
m_picArray.SetAt(pNum,pd);
return ret;
}
void CPicTextCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
// TODO: Add your message handler code here and/or call default
if(! m_scrbar->IsWindowEnabled()) return;
if(pScrollBar == m_scrbar)
{
SCROLLINFO si;
m_scrbar->GetScrollInfo(&si);
switch(nSBCode)
{
case SB_LINEDOWN:
si.nPos++;
break;
case SB_LINEUP:
si.nPos--;
break;
case SB_PAGEDOWN:
si.nPos += si.nPage;
break;
case SB_PAGEUP:
si.nPos -= si.nPage;
break;
case SB_THUMBPOSITION:
si.nPos = si.nTrackPos;
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
case SB_ENDSCROLL:
break;
}
if(si.nPos < 0)
si.nPos = 0;
if(si.nPos > (int)(si.nMax - si.nPage + 1))
si.nPos = si.nMax - si.nPage + 1;
if(si.nPos == pScrollBar->GetScrollPos())
return;
pScrollBar->SetScrollPos(si.nPos);
Invalidate();
}
CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
}
BOOL CPicTextCtrl::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
// TRACE(_T("call onErase background\n"));
return CWnd::OnEraseBkgnd(pDC);
}
void CPicTextCtrl::OnWindowPosChanged(WINDOWPOS FAR* lpwndpos)
{
CWnd::OnWindowPosChanged(lpwndpos);
// TODO: Add your message handler code here
if(!IsWindowEnabled() || !IsWindowVisible())
return;
RECT rt;
rt.top = 0;
rt.right = lpwndpos->cx;
rt.bottom = lpwndpos->cy;
rt.left = rt.right - GetSystemMetrics(SM_CXVSCROLL);
m_pCount = 0;
int count = m_txtArray.GetSize();
while(count--)
m_txtArray.RemoveAt(0);
// TRACE(_T("before recalc txtary = %d\n"),m_txtArray.GetSize());
CalcTextOft();
m_scrbar->MoveWindow(&rt);
m_viewLine = GetViewLineCount();
// TRACE(_T("call on windowpos changed,txtary = %d,m_str = %s\n"),m_txtArray.GetSize(),m_str.Left(20));
}
BOOL CPicTextCtrl::HasPicture(int start, int end, int *ret)//判断在文字的某个范围内是否有图片,如果有的话返回第一张图像位置
{
int i,j;
for(i=start;i <= end;i++)
{
if(HasPicture(i,&j) > 0)
{
*ret = i - start;
return TRUE;
}
}
return FALSE;
}
void CPicTextCtrl::SortPicture()//将图片按在文本中的偏移排序
{
PicData_T pd;
for(int i=0;i<m_picArray.GetSize()-1;i++)
{
pd = m_picArray.GetAt(i);
for(int j=i+1;j<m_picArray.GetSize();j++)
{
if(m_picArray.GetAt(j).oft < pd.oft)
{
m_picArray.SetAt(i,m_picArray.GetAt(j));
m_picArray.SetAt(j,pd);
}
}
}
}
void CPicTextCtrl::SetMargins(int left, int right)
{
if(left >= 0)
m_lMarg = left;
if(right >= 0)
m_rMarg = right;
Invalidate();
}
int CPicTextCtrl::DrawTextWidth(CString str,int width)
{
int i = 0,j = 0,k,len = str.GetLength();
TCHAR *buf = str.GetBuffer(0);
HDC hdc = ::GetDC(m_hWnd);
SelectObject(hdc,m_fnt.m_hObject);
GetCharWidth32(hdc,_T('W'),_T('W'),&k);
width -= k;
while(i < len)
{
if(!isEngWordChar(*buf))
{
if(*buf == '\n')
{
i++;
break;
}
if(isEngWordChar(*(buf+1)))
{
SIZE sz;
k = FindNextSplit(buf + 1);
GetTextExtentPoint32(hdc,buf,k,&sz);
if(j + sz.cx > width)
break;
else
{
j += sz.cx;
i += k;
buf += k;
continue;
}
}
}
if(!GetCharWidth32(hdc,*buf,*buf,&k))
break;
j += k;
if(j > width)
break;
i++;
buf++;
}
::ReleaseDC(m_hWnd,hdc);
str.ReleaseBuffer();
return i;
}
#define WIDTHBYTES(i) ((i + 31) / 32 * 4)
HBITMAP CPicTextCtrl::myGetBitmapFromFile(CFile &pFile,DWORD offset,DWORD len,int *w,int *h)
{
HBITMAP hb = NULL;
BITMAPFILEHEADER bf;
BITMAPINFO oldbi,newbi;
DWORD LineBytes;
DWORD NumColors;
DWORD ImgSize;
void *pBits;
BYTE *buf;
pFile.Seek(offset,CFile::begin);
pFile.Read(&bf,sizeof(bf));
if(bf.bfType != MAKEWORD('B','M'))
return hb;
pFile.Read(&oldbi.bmiHeader,sizeof(oldbi.bmiHeader));
*w = oldbi.bmiHeader.biWidth;
*h = oldbi.bmiHeader.biHeight;
LineBytes = (DWORD)WIDTHBYTES(oldbi.bmiHeader.biWidth * oldbi.bmiHeader.biBitCount);
ImgSize = LineBytes * oldbi.bmiHeader.biHeight;
if(oldbi.bmiHeader.biClrUsed)
{
switch(oldbi.bmiHeader.biBitCount)
{
case 1:
NumColors = 2;
break;
case 4:
NumColors = 16;
break;
case 8:
NumColors = 256;
break;
case 24:
NumColors = 0;
break;
default:
return hb;
}
}
else
NumColors = oldbi.bmiHeader.biClrUsed;
if(bf.bfOffBits != (DWORD)(NumColors * sizeof(RGBQUAD) + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)))
return hb;
ImgSize += NumColors * sizeof(RGBQUAD);
buf = (BYTE*)new BYTE[ImgSize];
if(buf == NULL)
return hb;
pFile.Read(buf,ImgSize);
HDC hdc = ::GetDC(m_hWnd);
if(oldbi.bmiHeader.biBitCount < 24)
{
memcpy(&newbi.bmiHeader,&oldbi.bmiHeader,sizeof(newbi.bmiHeader));
memset(&newbi.bmiColors,0,sizeof(newbi.bmiColors));
newbi.bmiHeader.biSizeImage = 0;
newbi.bmiHeader.biClrUsed = 0;
newbi.bmiHeader.biCompression = 0;
newbi.bmiHeader.biBitCount = 32;
hb = CreateDIBSection(hdc, &newbi,DIB_RGB_COLORS, &pBits, NULL, 0);
GetPictruePixels(&oldbi,buf,(BYTE*)pBits);
}
else
{
hb = CreateDIBSection(hdc, &oldbi,DIB_RGB_COLORS, &pBits, NULL, 0);
memcpy(pBits,buf,ImgSize);
}
::ReleaseDC(m_hWnd,hdc);
delete[] buf;
return hb;
}
HBITMAP CPicTextCtrl::myGetBitmapFromFile(CString fname,int *w,int *h)
{
HBITMAP hb = NULL;
CFile file;
if(!file.Open(fname,CFile::modeRead | CFile::typeBinary))
return hb;
HDC hdc;
HBITMAP oldbmp;
BITMAPFILEHEADER bf;
BITMAPINFO oldbi,newbi;
DWORD LineBytes;
DWORD NumColors;
DWORD ImgSize;
void *pBits;
BYTE *buf;
file.Read(&bf,sizeof(bf));
if(bf.bfType != MAKEWORD('B','M'))
goto fail;
file.Read(&oldbi.bmiHeader,sizeof(oldbi.bmiHeader));
*w = oldbi.bmiHeader.biWidth;
*h = oldbi.bmiHeader.biHeight;
LineBytes = (DWORD)WIDTHBYTES(oldbi.bmiHeader.biWidth * oldbi.bmiHeader.biBitCount);
ImgSize = LineBytes * oldbi.bmiHeader.biHeight;
switch(oldbi.bmiHeader.biBitCount)
{
case 1:
NumColors = 2;
break;
case 4:
NumColors = 16;
break;
case 8:
NumColors = 256;
break;
case 24:
default:
NumColors = 0;
}
if(bf.bfOffBits != (DWORD)(NumColors * sizeof(RGBQUAD) + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)))
goto fail;
ImgSize += NumColors * sizeof(RGBQUAD);
buf = (BYTE*)new BYTE[ImgSize];
if(buf == NULL)
goto fail;
file.Read(buf,ImgSize);
file.Close();
hdc = ::GetDC(m_hWnd);
if(oldbi.bmiHeader.biBitCount < 24)
{
memcpy(&newbi.bmiHeader,&oldbi.bmiHeader,sizeof(newbi.bmiHeader));
memset(&newbi.bmiColors,0,sizeof(newbi.bmiColors));
newbi.bmiHeader.biSizeImage = 0;
newbi.bmiHeader.biClrUsed = 0;
newbi.bmiHeader.biCompression = 0;
newbi.bmiHeader.biBitCount = 32;
hb = CreateDIBSection(hdc, &newbi,DIB_RGB_COLORS, &pBits, NULL, 0);
GetPictruePixels(&oldbi,buf,(BYTE*)pBits);
}
else
{
hb = CreateDIBSection(hdc, &oldbi,DIB_RGB_COLORS, &pBits, NULL, 0);
memcpy(pBits,buf,ImgSize);
}
::ReleaseDC(m_hWnd,hdc);
delete[] buf;
return hb;
fail:
file.Close();
return hb;
}
void CPicTextCtrl::GetPictruePixels(LPBITMAPINFO pbi,BYTE *src,BYTE *dst)//只处理1,4,8位图像
{
int i,j,k;
DWORD LineBytes = WIDTHBYTES(pbi->bmiHeader.biBitCount * pbi->bmiHeader.biWidth);
LPRGBQUAD pPal = (LPRGBQUAD)src;
switch(pbi->bmiHeader.biBitCount)
{
case 1:
src += sizeof(RGBQUAD) * 2;
for(i = 0;i < pbi->bmiHeader.biHeight;i++)
{
BYTE *sline = src + i * LineBytes;
BYTE *dline = dst + i * sizeof(RGBQUAD) * pbi->bmiHeader.biWidth;
j = 0;
while(j < pbi->bmiHeader.biWidth)
{
for(k = 0;k < 8;k++)
{
if(j++ > pbi->bmiHeader.biWidth)
break;
memcpy(dline,&pPal[(*sline >> (7 - k)) & 0x1],sizeof(RGBQUAD));
dline += sizeof(RGBQUAD);
}
sline++;
}
}
break;
case 4:
src += sizeof(RGBQUAD) * 16;
for(i = 0;i < pbi->bmiHeader.biHeight;i++)
{
BYTE *sline = src + i * LineBytes;
BYTE *dline = dst + i * sizeof(RGBQUAD) * pbi->bmiHeader.biWidth;
for(j = 0;j < pbi->bmiHeader.biWidth;j++)
{
memcpy(dline,&pPal[(*sline & 0xF0) >> 4],sizeof(RGBQUAD));
memcpy(dline + sizeof(RGBQUAD),&pPal[*sline & 0x0F],sizeof(RGBQUAD));
sline++;
dline += 2 * sizeof(RGBQUAD);
}
}
break;
case 8:
src += sizeof(RGBQUAD) * 256;
for(i = 0;i < pbi->bmiHeader.biHeight;i++)
{
BYTE *sline = src + i * LineBytes;
BYTE *dline = dst + i * sizeof(RGBQUAD) * pbi->bmiHeader.biWidth;
for(j = 0;j < pbi->bmiHeader.biWidth;j++)
{
for(k = 0;k < 3;k++)
{
memcpy(dline,&pPal[*sline++],sizeof(RGBQUAD));
dline += sizeof(RGBQUAD);
}
}
}
break;
default:
break;
}
}
void CPicTextCtrl::SetStartShowLine(int line)
{
if(m_scrbar && m_scrbar->IsWindowEnabled())
{
if(line < 0)
line = 0;
if(line > m_txtArray.GetSize() - GetViewLineCount())
line = m_txtArray.GetSize() - GetViewLineCount();
m_scrbar->SetScrollPos(line);
Invalidate();
}
}
BOOL CPicTextCtrl::AddPic(CString fname, DWORD offset, int picOft)
{
if(picOft > m_str.GetLength() - 1)
return FALSE;
CFile m_file;
if(!m_file.Open(fname,CFile::modeRead|CFile::typeBinary))
return FALSE;
int w,h;
#ifdef USE_IIMAGE
HBITMAP hb = GetHBitmapFromFile(m_file,offset,0,&w,&h);
#else
HBITMAP hb = myGetBitmapFromFile(m_file,offset,0,&w,&h);
#endif
m_file.Close();
if(!hb)
{
return FALSE;
}
PicData_T pd;
pd.width = w, pd.hight = h;
pd.hasText = 0;
pd.oft = picOft;
pd.pic = hb;
m_picArray.Add(pd);
SortPicture();
return TRUE;
}