richedit研究04 – 高效時鐘

來源:互聯網
上載者:User

上一次,我們可以擷取到圖片動畫幀之間的時間間隔,如果想讓動畫轉起來,就必須有時鐘。插入的圖片動畫數量可能會比較多,因此要想不影響效能,時鐘必須很輕量級而且要很高效。

 

Windows平台上實現時鐘的方式五花八門,你可以使用視窗相關的SetTimer來設定一個時鐘,也可以自己開闢線程來做等待觸發類比時鐘,而Chromium封裝的要更加C++對象化一些:依託Windows視窗訊息,抽象出延遲任務的概念。這種手法幾年前我也曾經考慮過,只是對其中下次最短觸發時間計算以及更新的演算法和設計都有力不從心,最終得出的是誤差很大的精簡版:選擇固定的最小時間片為最小觸發單位,對很小的時間間隔誤差很明顯。

 

Windows有Timer Queues用來實現高效的非同步時鐘,比較奇怪的是這組API用的貌似並不多。我們知道每個進程都有一個預設的線程池,可以在其中執行一些Work Items,時鐘隊列和等待操作也都會用到這個線程池。timer-queue中的timers建立和銷毀都很輕量高效,因此我選擇了它。

 

每個OLE圖片對象在設定圖片之後,如果發現是多幀的,就需要啟動動畫,建立時鐘:

 

ATLVERIFY(CreateTimerQueueTimer(&timer_, NULL,
                              WaitOrTimerCallback,
                              callback_parameter_.get(),
                              image_->GetFrameDelay(current_frame_),
                              0, WT_EXECUTEDEFAULT));

 

這裡timer_是傳回值,返回建立的時鐘對象,可以在OLE對象銷毀或者回呼函數中進行刪除,而刪除操作會等待回調執行完畢才返回。傳遞TimerQueue為NULL表示使用系統的隊列。Period為0表示只觸發一次,觸發時間為image_->GetFrameDelay(current_frame_)。由於回呼函數WaitOrTimerCallback是線上程池的線程中執行,所以更新操作需要同步到動畫圖片的建立線程中。callback_parameter_包含有上一節提及的ThreadState對象以及動畫OLE對象指標,ThreadState建立的時候會同時建立一個隱藏視窗用於工作者線程向UI線程同步操作:

 

VOID CALLBACK IMRichPicture::WaitOrTimerCallback(PVOID lpParameter,
                                            BOOLEAN TimerOrWaitFired) {
  ATLASSERT(TimerOrWaitFired == TRUE);
 
  IMRichPicture::CallbackParameter* parameter =
      reinterpret_cast<IMRichPicture::CallbackParameter*>(lpParameter);
  ATLASSERT(parameter);
  parameter->thread_state->UpdatePictureFrame(parameter->picture);
}

 

下面是UpdatePictureFrame的實現:

 

void IMThreadState::UpdatePictureFrame(IMRichPicture* picture) const {
  PostMessage(message_window_, kMessageUpdatePictureFrame,
              reinterpret_cast<WPARAM>(picture->richedit()),
              reinterpret_cast<LPARAM>(picture));
}

 

這樣繞一大圈子,是為了利用Timer Queues的同時保證圖片的更新操作是在UI線程中執行,因為圖片被插入也是發生在UI線程,即動畫控制項建立於UI線程,為了避免加鎖帶來的麻煩以及死結的可能性,不應該輕易去加鎖,盡量利用作業系統提供的基礎設施來實現。這裡需要注意的是隱藏視窗接收到kMessageUpdatePictureFrame訊息時,richedit視窗可能已不存在或者動畫控制項已經銷毀,因此使用指標前,需要判斷對象是否還存在:

 

case kMessageUpdatePictureFrame: {
      IMRichEditImpl* richedit = reinterpret_cast<IMRichEditImpl*>(wparam);
      IMRichPicture* picture = reinterpret_cast<IMRichPicture*>(lparam);
      if (IMThreadState::current()->HasRichEdit(richedit))
        richedit->OnUpdatePictureFrame(picture);
      return 0;
}

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.