IImage的圖片顯示

來源:互聯網
上載者:User
//========================================================================
//TITLE:
//    詳聊IImage的圖片顯示
//AUTHOR:
//    norains
//DATE:
//    Sunday 26-August-2007
//Environment:
//        EVC4.0 + Windows CE 5.0 Standard SDK
//========================================================================
    IImage是WinCE5.0之後才加入的COM組件,用處正如其名,更為方便顯示圖片.WinCE 5.0之後,我們就可以喜新厭舊,拋棄imgdecmp,轉投IImage,呵呵~雖然個人覺得這兩個解碼,論速度不相伯仲,都是一樣的----慢,特別是繪製大圖片的時候還有限制,但畢竟有總好過無,所以我們就來看看今天的主角吧!
  
    用IImage來顯示圖片真是異乎尋常的簡單,無非就是調用幾個函數而已:

   IImage * m_pImage;
     IImagingFactory * m_pImagingFactory;
    
     HRESULT hr;

  //COM初始化
    if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED)))
   {  
        goto END;
   }

  //建立COM執行個體
    if(FAILED(hr = CoCreateInstance(CLSID_ImagingFactory,NULL,CLSCTX_INPROC_SERVER,IID_IImagingFactory,(void**) &m_pImagingFactory)))
     {
        goto END;
     }

  //從檔案中建立圖片
    if(FAILED(hr = m_pImagingFactory->CreateImageFromFile(TEXT("測試.bmp"), &m_pImage)))
     {
        goto END;
     }
    
    //繪製圖片
    if(FAILED(hr = m_pImage->Draw(hdc,&rcWnd,NULL)))
     {
      goto END;
     }
    
    
END:
  //釋放資源
    if(m_pImage != NULL)
     {
         m_pImage->Release();
         m_pImage = NULL;
     }

    if(m_pImagingFactory != NULL)
     {
         m_pImagingFactory->Release();
         m_pImagingFactory = NULL;
     }

     CoUninitialize();

    OK,就是這麼簡單,只要OS中只要包含了相應的解碼器組件,以上代碼顯示大部分的圖片應該是沒問題的.好吧,既然如此,那麼這篇豆腐塊也就可以結束了~:-)
   
    然而可惜的是,微軟向來不會將事情做得很完滿,要不然這篇豆腐塊到這裡確實是盡善盡美了.
   
    這段代碼之中,本應風平浪靜,但偏偏有一個函數表現怪異,且很容易調用失敗,它乃IImage::Draw()!
   
    1)顯示源檔案特定地區   
   
    顯示整副圖片沒啥問題,只要將srcRect參數賦值NULL即可.但如果是顯示特定的地區呢?比如說,有一副800*600的圖片,我只想顯示一半,那該咋辦?估計很多人(恩,也包括我),不會仔細看文檔,而會是憑藉經驗直接這麼寫:     RECT rcSrc = {0,0,400,600};
     m_pImage->Draw(hdc,&rcWnd,&rcSrc);

    噢,我的上帝,運行這段代碼吧!你會看到什麼?如果幸運的話,你會一個一個小方塊!這時候,估計很多人會問候微軟的父母,噢,等等,微軟的東西是有不少bug,但這麼簡單的東西,還不至於讓其如此糊塗的.
  
    仔細看看文檔關於該參數的說明:An optional pointer to a RECT that specifies, in 0.01mm units, the portion of the image to be drawn in dstRect.
  
    看到最重要的一句了麼:in 0.01mm units !! 以0.01mm為單位,非像素點! 雖然微軟的出發點是好的,讓其顯示的精度更為準確,但這個確實給我們帶來不少的麻煩.但既然微軟介面都如此定義了,與其在這裡幹跺腳,抱怨微軟,還不如我們做點實際的,畢竟我們還是要完成任務,領取每個月的薪水養家糊口.:-(
  
    天無絕人之路,OK,讓我們看看單位的轉換吧:

     m_pImage->GetImageInfo(&ImageInfo);
    
    double dDotPermmX = ImageInfo.Xdpi / 25.4;
        double dDotPermmY = ImageInfo.Ydpi / 25.4;
        
        //pSrcRect指向以像素點為單位的地區
         RECT rcSrc = {(LONG)(pSrcRect->left / dDotPermmX / 0.01),
             (LONG)(pSrcRect->top / dDotPermmY / 0.01),
             (LONG)(pSrcRect->right / dDotPermmX / 0.01),
             (LONG)(pSrcRect->bottom / dDotPermmY / 0.01)};   
            
         m_pImage->Draw(hdc,&rcDraw,&rcSrc);

        雖然這樣能夠基本正常顯示指定的地區,但還是有一定的誤差,不過接下來討論的另一個問題,恰好可以解決.
       
       
        2)IImage::Draw()耗時久
       
    這是一個沒有辦法的問題,因為IImage::Draw()需要經曆一些的解碼才能繪製到目標DC,特別是在顯示一些特別大的JPEG(如果能夠顯示的話:-))時更為明顯.如果圖片僅僅是顯示一次,也許問題尚且不那麼嚴重,但如果是需要多次調用,比方說拖動圖片,那麼這種速度絕對無法忍受的.當然咯,這個問題在將來是肯定不會出現的,並且這個將來也不會很久,不用等到人老珠黃或是為伊消得人憔悴,只要嵌入式裝置的解碼速度和目前主流的PC相差無幾.這一天不會遠了,只是對於解決今天的問題卻是遠水救不了近火,還是乖乖地用技巧解決吧!
   
    方法其實很簡單,IImage::Draw()耗時久嘛,那我們只要第一次繪製時將映像儲存到記憶體DC中,然後再把記憶體DC的資料繪製到目標DC即可.這樣一來,僅僅是第一次繪圖時感到不愉快(如果你脾氣暴躁,也許會感到煩躁),但之後的每次都是暢快之旅.以一次換來一勞永逸,還有比這更便宜的事嘛?//擷取圖片屬性
     m_pImage->GetImageInfo(&ImageInfo);
   
    //建立一個記憶體DC,用來儲存圖片資料
     hBitmap = CreateCompatibleBitmap(hdc,ImageInfo.Width,ImageInfo.Height);
     hdcMem = CreateCompatibleDC(hdc);
     hOldSel = SelectObject(hdcMem,hBitmap);

     ...

    //將圖片資料存放區到記憶體DC中
     rcMemDC = {0,0,ImageInfo.Width,ImageInfo.Height};    
     m_pImage->Draw(hdcMem,&rcMemDC,NULL);
    
     ...
    
    //將圖片繪製到目標DC
     StretchBlt(hdc,
                             pDstRect->left,
                             pDstRect->top,
                             pDstRect->right - pDstRect->left,
                             pDstRect->bottom - pDstRect->top,
                             hdcMem,
                             pSrcRect->left,
                             pSrcRect->top,
                             pSrcRect->right - pSrcRect->left,
                             pSrcRect->bottom - pSrcRect->top,
                             SRCCOPY);

    只要第一次將圖片資料儲存到hdcMem中,以後繪製圖片時只要繪製hdcMem資料即可,這比還需要經曆一次解碼過程的IImage::Draw()快多了!
   
    也許有細心的朋友可能會注意到StretchBlt()的形參.恩,在此對這些朋友表示我由衷的敬意.沒錯,是的,將圖片資料儲存到hdcMem後,帶來的額外一個好處就是,繪製圖片某個地區時再也不用轉換單位啦!
   
    因為是將整張圖片資料存放區到hdcMem,所以參數只要設定為NULL;儲存到hedMem後,採用StretchBlt()函數繪製只需要以像素為單位.呵呵,是的,沒錯,無形中我們就避免了單位轉換可能帶來的損失.所謂的一箭雙鵰乎?
   
   
    3)繪製大圖片失敗
   
    在調用IImage::Draw()繪製大圖片時,很有可能導致的後果是:繪製失敗.而提示的錯誤,更多是:E_OUTOFMEMORY,記憶體不足.這是一個很無奈的現實.雖然我的裝置記憶體已經很明顯大於圖片所需的,但由於受限制於WinCE5.0 32M虛擬記憶體,即使是採用旁門左道分配更多的虛擬記憶體,其結果無非也是:失敗!沒辦法,恩,至少我是沒找到解決的方法.
   
    當然咯,萬事沒絕對,如果對圖片品質要求不高,倒還是有法子讓其顯示的.很簡單,只要擷取縮圖執行個體,然後再繪製即可.

     m_pImage->GetThumbnail(WIDTH,HEIGHT,&m_pThum);
     m_pThum->Draw(hdc,&rcDC,NULL);

這次IImage::Draw()應該不會失敗了.當然,如果還是那麼倒黴,那唯一的方法就是將WIDTH和HEIGHT減小,直到能正常顯示為止.不過,隨著數值的減小,圖片的品質也會隨之降低.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.