c++图文混排控件

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;
}



一直以来都比较羡慕那些,界面美观,功能强大的即时通讯软件,自己慢慢摸索了好长时间,用MFC做了一个聊天程序主窗口演示小程序。 该程序主要自定义了CRichEditCtrl控件,使用XML技术与GDI处理各种图片及QQ表情组件,串行化数据等,使得程序拥有了一般聊天程序应用的特色(同时仿FeiQ程序的自动释放程序运行需要的资源到安装目录): 支持字体设置、图文混排、表情及各种格式图片的插入、发送与保存,聊天记录的保存等等... 1、聊天输入、输出窗口:使用CRichEditCtrl控件,扩展后方便了字体设置、图文混排、表情及各种格式图片的插入,及窗口上右键菜单的功能。 2、聊天表情:这一部分是我精心制作的,使用GDI处理各种图片使得程序支持各种格式图片的预览、插入、保存。使用XML技术,方便快捷管理表情数据。自动释放表情图片资源(程序所在目录)及QQ表情组件(放到\System32\ImageOle.dll),程序自动注册组件,让程序支持GIF格式表情的插入。程序仿FeiQ自动生成表情页面缓存图片,快捷增加大量表情。本程序生成缓存图时,使用多线程技术,增加、修改完后立即更新页面缓存图,几千张图片,马上生成完成。 3、表情管理:支持表情的添加、删除、修改,移动,导入、导出表情库等,修改完后自动删除需要更新的页面缓存图,及程序程序无关的文件等。 4、聊天记录:使用串行化数据技术,将聊天记录生成到Log.dat文件中,可分页浏览记录。 本程序本人精心制件,最近一段时间在弄一个即时通讯软件,程序马上完工,完工后立即上传,由于弄程序花了不少精力,出于私心暂时不公布了。。。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值