Android?鼠標(biāo)光標(biāo)的圖形合成原理實例探究
引言
一直很好奇鼠標(biāo)光標(biāo)是如何實現(xiàn)的,它反映很快、延遲很小,沒有受到 Android 顯示系統(tǒng)的影響。正好最近做相關(guān)的工作,跟著源碼好好研究一下。
本文參考 Android 9.0 源碼。
從 Input 說起
我們并不是要講 Input,只想看看鼠標(biāo)光標(biāo)的繪制過程。但是,Android 將鼠標(biāo)光標(biāo)的實現(xiàn)放到了 Input 中,這看起來也是合理的。在 Input 中,光標(biāo)由類Sprite
實現(xiàn)。源碼中對 Sprite
的解釋為:顯示在其他圖層之上的圖形對象??磥?nbsp;Sprite
并非專為光標(biāo)設(shè)計,但在源碼中的位置表明,它在 Android 中也只為鼠標(biāo)或觸摸之類的輸入設(shè)備的光標(biāo)服務(wù)。Sprite
的定義中也只提供了簡單的圖形操作。
frameworks/base/libs/input/SpriteController.h /* * A sprite is a simple graphical object that is displayed on-screen above other layers. * The basic sprite class is an interface. * The implementation is provided by the sprite controller. */ class Sprite : public RefBase { protected: Sprite() { } virtual ~Sprite() { } public: enum { // The base layer for pointer sprites. BASE_LAYER_POINTER = 0, // reserve space for 1 pointer // The base layer for spot sprites. BASE_LAYER_SPOT = 1, // reserve space for MAX_POINTER_ID spots }; /* Sets the bitmap that is drawn by the sprite. * The sprite retains a copy of the bitmap for subsequent rendering. */ virtual void setIcon(const SpriteIcon& icon) = 0; inline void clearIcon() { setIcon(SpriteIcon()); } /* Sets whether the sprite is visible. */ virtual void setVisible(bool visible) = 0; /* Sets the sprite position on screen, relative to the sprite's hot spot. */ virtual void setPosition(float x, float y) = 0; /* Sets the layer of the sprite, relative to the system sprite overlay layer. * Layer 0 is the overlay layer, > 0 appear above this layer. */ virtual void setLayer(int32_t layer) = 0; /* Sets the sprite alpha blend ratio between 0.0 and 1.0. */ virtual void setAlpha(float alpha) = 0; /* Sets the sprite transformation matrix. */ virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0; };
控制光標(biāo)的類叫做 SpriteController
,PointerController 會使用這個類來顯示光標(biāo)。這里我們只關(guān)心光標(biāo)圖形的合成,真正顯示和更新光標(biāo)的方法是 SpriteController::doUpdateSprites()
。
frameworks/base/libs/input/SpriteController.cpp void SpriteController::doUpdateSprites() { // 從invalidatedSprites 中收集需要更新的 Sprite Vector<SpriteUpdate> updates; size_t numSprites; { // acquire lock AutoMutex _l(mLock); numSprites = mLocked.invalidatedSprites.size(); for (size_t i = 0; i < numSprites; i++) { const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites.itemAt(i); updates.push(SpriteUpdate(sprite, sprite->getStateLocked())); sprite->resetDirtyLocked(); } mLocked.invalidatedSprites.clear(); } // release lock // surfaces 未創(chuàng)建或丟失時,重新創(chuàng)建 surface bool surfaceChanged = false; for (size_t i = 0; i < numSprites; i++) { SpriteUpdate& update = updates.editItemAt(i); if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) { update.state.surfaceWidth = update.state.icon.bitmap.width(); update.state.surfaceHeight = update.state.icon.bitmap.height(); update.state.surfaceDrawn = false; update.state.surfaceVisible = false; // 創(chuàng)建 Surface,我們這次的關(guān)注點 update.state.surfaceControl = obtainSurface( update.state.surfaceWidth, update.state.surfaceHeight); if (update.state.surfaceControl != NULL) { update.surfaceChanged = surfaceChanged = true; } } } // 如果需要,重新調(diào)整 sprites 大小 SurfaceComposerClient::Transaction t; bool needApplyTransaction = false; for (size_t i = 0; i < numSprites; i++) { ...... if (update.state.surfaceWidth < desiredWidth || update.state.surfaceHeight < desiredHeight) { needApplyTransaction = true; t.setSize(update.state.surfaceControl, desiredWidth, desiredHeight); ...... } } } if (needApplyTransaction) { t.apply(); } // 如果需要,重畫 sprites for (size_t i = 0; i < numSprites; i++) { SpriteUpdate& update = updates.editItemAt(i); if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) { update.state.surfaceDrawn = false; update.surfaceChanged = surfaceChanged = true; } if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn && update.state.wantSurfaceVisible()) { sp<Surface> surface = update.state.surfaceControl->getSurface(); ANativeWindow_Buffer outBuffer; ...... // 使用 SKIA 畫圖 SkBitmap surfaceBitmap; ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height), outBuffer.bits, bpr); SkCanvas surfaceCanvas(surfaceBitmap); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); if (outBuffer.width > update.state.icon.bitmap.width()) { paint.setColor(0); // transparent fill color surfaceCanvas.drawRect(SkRect::MakeLTRB(update.state.icon.bitmap.width(), 0, outBuffer.width, update.state.icon.bitmap.height()), paint); } if (outBuffer.height > update.state.icon.bitmap.height()) { paint.setColor(0); // transparent fill color surfaceCanvas.drawRect(SkRect::MakeLTRB(0, update.state.icon.bitmap.height(), outBuffer.width, outBuffer.height), paint); } ...... } // 根據(jù) dirty 值來設(shè)置 Surface needApplyTransaction = false; for (size_t i = 0; i < numSprites; i++) { SpriteUpdate& update = updates.editItemAt(i); bool wantSurfaceVisibleAndDrawn = update.state.wantSurfaceVisible() && update.state.surfaceDrawn; bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible; bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible; if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) { ...... } if (needApplyTransaction) { status_t status = t.apply(); if (status) { ALOGE("Error applying Surface transaction"); } } ...... }
一次的光標(biāo)的更新就會涉及到如此多的代碼邏輯,可見UI真是不容易。其他的邏輯線不管,這次我們只關(guān)心光標(biāo)的圖層。上述代碼通過 obtainSurface()
來創(chuàng)建 Surface。
frameworks/base/libs/input/SpriteController.cpp sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) { ensureSurfaceComposerClient(); sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface( String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden | ISurfaceComposerClient::eCursorWindow); if (surfaceControl == NULL || !surfaceControl->isValid()) { ALOGE("Error creating sprite surface."); return NULL; } return surfaceControl; }
這里我們需要重點關(guān)注的是 createSurface()
方法中的參數(shù) flags
。Sprite 中這個 flags
設(shè)置了eHidden
和eCursorWindow
,它們表明創(chuàng)建的 Surface 是隱藏的,并標(biāo)識為 Cursor 使用。
來到 Surface
Input 中為光標(biāo)創(chuàng)建了一個 Surface,并且標(biāo)識這是一個 Cursor 使用的 Surface。之后,Surface 中會根據(jù)情形對光標(biāo)圖層做特殊處理,這里的關(guān)鍵字就是 Cursor
。
我們還是以光標(biāo)圖層為主線進行跟蹤,先繼續(xù)看下createSurface()
。經(jīng)過一系列的 Binder 調(diào)用和 Message傳遞,最終通過 SurfaceFlinger 的createLayer()
完成圖層創(chuàng)建。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, int32_t windowType, int32_t ownerUid, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer) { ...... switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { // 普通圖層 case ISurfaceComposerClient::eFXSurfaceNormal: result = createBufferLayer(client, uniqueName, w, h, flags, format, handle, gbp, &layer); break; // 純色圖層 case ISurfaceComposerClient::eFXSurfaceColor: result = createColorLayer(client, uniqueName, w, h, flags, handle, &layer); break; default: result = BAD_VALUE; break; } ...... // Client中通過Layer管理Surface,將創(chuàng)建的Layer加入到LayerStack中 result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer); if (result != NO_ERROR) { return result; } mInterceptor->saveSurfaceCreation(layer); setTransactionFlags(eTransactionNeeded); return result; }
createLayer()
中,光標(biāo)算是普通圖層,所以僅需調(diào)用createBufferLayer()
來創(chuàng)建。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) { ...... // 創(chuàng)建一個BufferLayer sp<BufferLayer> layer = new BufferLayer(this, client, name, w, h, flags); // 設(shè)置Buffer屬性 status_t err = layer->setBuffers(w, h, format, flags); if (err == NO_ERROR) { *handle = layer->getHandle(); // 獲取Layer的句柄 *gbp = layer->getProducer(); // 獲取GraphicBufferProducer對象 *outLayer = layer; } ALOGE_IF(err, "createBufferLayer() failed (%s)", strerror(-err)); return err; }
其中layer->setBuffers()
設(shè)置了該BufferLayer的屬性??梢钥吹剑?dāng)申請的是一個 Cursor 圖層時,mPotentialCursor
被設(shè)置為true
,表明該 BufferLayer 作為 Cursor 使用。
frameworks/native/services/surfaceflinger/BufferLayer.cpp status_t BufferLayer::setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { ...... mFormat = format; mPotentialCursor = (flags & ISurfaceComposerClient::eCursorWindow) ? true : false; mProtectedByApp = (flags & ISurfaceComposerClient::eProtectedByApp) ? true : false; mCurrentOpacity = getOpacityForFormat(format); mConsumer->setDefaultBufferSize(w, h); mConsumer->setDefaultBufferFormat(format); mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); return NO_ERROR; }
SurfaceFlinger 中的 Cursor 操作
上面講到 Cursor Layer 最核心的屬性mPotentialCursor
,createSurface()
只是設(shè)置了這個屬性,真正的使用在 SurfaceFlinger 渲染過程中。接著我發(fā)現(xiàn),想把這個東西看明白,先需要把 Android 圖形合成弄清楚,這可是的龐大的工程。借張圖,有興趣的自己研究。
但是,時間有限,怎么辦?我的解決辦法就是搜索關(guān)鍵字。搜索關(guān)鍵字Cursor
后,可以得到一些相關(guān)的操作。SurfaceFlinger 接收到 Vsync 信號后,會調(diào)用handleMessageRefresh()
來刷新顯示。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp void SurfaceFlinger::handleMessageRefresh() { ...... preComposition(refreshStartTime); //合成預(yù)處理 rebuildLayerStacks(); //重新構(gòu)建LayerStacks setUpHWComposer(); //更新HWComposer的圖層和屬性 doDebugFlashRegions(); //圖形繪制的debug模式 doTracing("handleRefresh"); logLayerStats(); doComposition(); //合成所有圖層 postComposition(refreshStartTime); //合成后處理 ...... }
我們還是只關(guān)心 Cursor 的操作,它位于 HWComposer 控制的圖層中。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp void SurfaceFlinger::setUpHWComposer() { ...... // 遍歷所有的DisplayDevice,為繪制做準(zhǔn)備 for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { ...... mDisplays[dpy]->beginFrame(mustRecompose); if (mustRecompose) { mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty; } } // 設(shè)置HWC Layer if (CC_UNLIKELY(mGeometryInvalid)) { mGeometryInvalid = false; for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) { ...... for (size_t i = 0; i < currentLayers.size(); i++) { const auto& layer = currentLayers[i]; // 嘗試創(chuàng)建HWC Layer,如果失敗則強制OpenGL渲染 if (!layer->hasHwcLayer(hwcId)) { if (!layer->createHwcLayer(getBE().mHwc.get(), hwcId)) { layer->forceClientComposition(hwcId); continue; } } // 設(shè)置HWC Layer的顯示區(qū)域、合成模式、Alpha、Order等 layer->setGeometry(displayDevice, i); // HWC被禁止或繪制debug模式時,強制OpenGL渲染 if (mDebugDisableHWC || mDebugRegion) { layer->forceClientComposition(hwcId); } ...... } // 準(zhǔn)備HWC需要渲染的數(shù)據(jù) for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { auto& displayDevice = mDisplays[displayId]; const auto hwcId = displayDevice->getHwcDisplayId(); ...... //調(diào)用 setPerFrameData方法 layer->setPerFrameData(displayDevice); ...... } ...... for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { ...... // 嘗試進行顯示 status_t result = displayDevice->prepareFrame(*getBE().mHwc); ...... } }
其中setPerFrameData()
完成 HWComposer 的相關(guān)設(shè)置,為顯示做準(zhǔn)備。
frameworks/native/services/surfaceflinger/BufferLayer.cpp void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) { ...... // 設(shè)置可見區(qū)域 auto error = hwcLayer->setVisibleRegion(visible); ...... // 設(shè)置刷新區(qū)域 error = hwcLayer->setSurfaceDamage(surfaceDamageRegion); ...... // Sideband layers設(shè)置 if (getBE().compositionInfo.hwc.sidebandStream.get()) { setCompositionType(hwcId, HWC2::Composition::Sideband); error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle()); ...... return; } if (mPotentialCursor) { // Cursor layers設(shè)置 setCompositionType(hwcId, HWC2::Composition::Cursor); } else { // Device layers設(shè)置 setCompositionType(hwcId, HWC2::Composition::Device); } // 設(shè)置色彩空間 error = hwcLayer->setDataspace(mCurrentDataSpace); if (error != HWC2::Error::None) { ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace, to_string(error).c_str(), static_cast<int32_t>(error)); } // 獲取HDR數(shù)據(jù)并設(shè)置到HWC中 const HdrMetadata& metadata = mConsumer->getCurrentHdrMetadata(); error = hwcLayer->setPerFrameMetadata(displayDevice->getSupportedPerFrameMetadata(), metadata); ...... // 獲取渲染的數(shù)據(jù)buffer和Fence,設(shè)置到HWC中 sp<GraphicBuffer> hwcBuffer; hwcInfo.bufferCache.getHwcBuffer(getBE().compositionInfo.mBufferSlot, getBE().compositionInfo.mBuffer, &hwcSlot, &hwcBuffer); auto acquireFence = mConsumer->getCurrentFence(); error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence); ...... }
我們終于找到了希望看到的mPotentialCursor
,通過這個標(biāo)識告訴 HWC2 這是一個 CursorLayer。除此之外,對于 CursorLayer 的操作與 DeviceLayer 并沒有區(qū)別。所以,SurfaceFlinger 更多的是希望 HWComposer 根據(jù) Layer 的類型進行不同處理。目前 HWC2 支持的 Layer 類型有,
- HWC2_COMPOSITION_CLIENT:不通過 HWC 硬件來合成圖層。GPU 將這類圖層合成到一個圖像 Buffer 中,然后傳遞給 HWC。
- HWC2_COMPOSITION_DEVICE:使用 HWC 硬件來合成圖層。
- HWC2_COMPOSITION_SOLID_COLOR:用來處理 ColorLayer 數(shù)據(jù),如果 HWC 不支持,則改為使用 CLIENT 方式合成。
- HWC2_COMPOSITION_CURSOR:用來處理 CursorLayer 數(shù)據(jù),位置通過
setCursorPosition
異步設(shè)置。如果 HWC 不支持,則改為使用 CLIENT 或 DEVICE 方式合成。 - HWC2_COMPOSITION_SIDEBAND:對于這種 Layer,需要由外部機制提供內(nèi)容更新,例如電視信號的視頻數(shù)據(jù)。如果 HWC 不支持,則改為使用 CLIENT 或 DEVICE 方式合成,但可能無法正確顯示。
Cursor Layer還有一個重要的操作,setCursorPosition()
,這個方法用來設(shè)置 Cursor 的位置,具體的實現(xiàn)依然在 HWComposer 中。當(dāng)用戶進程更新 Surface 圖形時,SurfaceFlinger 會發(fā)送INVALIDATE
消息給相應(yīng)的 Layer。消息處理函數(shù)調(diào)用handleTransaction()
和handlePageFlip()
來更新Layer對象。handleTransaction()
用來處理 Layer 和顯示設(shè)備的變化,它繼續(xù)調(diào)用handleTransactionLocked()
。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { ...... // 處理Layer的變化 if (transactionFlags & eTraversalNeeded) { ...... } // 處理顯示設(shè)備的變化 if (transactionFlags & eDisplayTransactionNeeded) { processDisplayChangesLocked(); processDisplayHotplugEventsLocked(); } // 設(shè)置transform hint if (transactionFlags & (eDisplayLayerStackChanged|eDisplayTransactionNeeded)) { ...... } //處理Layer的增減 if (mLayersAdded) { ...... } if (mLayersRemoved) { ...... } commitTransaction(); // 更新光標(biāo)位置 updateCursorAsync(); }
我們找到了 Cursor 更新的地方,SurfaceFlinger 更新圖形時會同步更新光標(biāo)位置。之后,在 Vsync 到來時,完成圖像的更新顯示。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp void SurfaceFlinger::updateCursorAsync() { for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) { ...... // 調(diào)用Layer的對應(yīng)方法 for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { layer->updateCursorPosition(displayDevice); } } }
frameworks/native/services/surfaceflinger/Layer.cpp void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) { // HWC Layer不存在或者不是Cursor Layer,不做處理 auto hwcId = displayDevice->getHwcDisplayId(); if (getBE().mHwcLayers.count(hwcId) == 0 || getCompositionType(hwcId) != HWC2::Composition::Cursor) { return; } ...... // 獲取圖層的位置 Rect bounds = reduce(win, s.activeTransparentRegion); Rect frame(getTransform().transform(bounds)); frame.intersect(displayDevice->getViewport(), &frame); if (!s.finalCrop.isEmpty()) { frame.intersect(s.finalCrop, &frame); } auto& displayTransform(displayDevice->getTransform()); auto position = displayTransform.transform(frame); // 調(diào)用HWC的方法來設(shè)置圖層位置 auto error = getBE().mHwcLayers[hwcId].layer->setCursorPosition(position.left, position.top); }
到達 HWComposer
上面分析了許多代碼,但真正與 Cursor 相關(guān)的并不多。CursorLayer 的真正實現(xiàn)還是在 HWComposer 中。但是 HWComposer 的實現(xiàn)是與平臺相關(guān)的,不同的平臺對 CursorLayer 的實現(xiàn)可能不同。效率的方式是使用一個獨立的硬件 OSD 來顯示 CursorLayer,然后通過硬件合成的方式將 CursorLayer 疊加到 UI 顯示層。使用這種方式,光標(biāo)的移動效率也很高,只要改變硬件 OSD 顯示的位置即可。如果沒有獨立的硬件 OSD 來使用,就只能在標(biāo)準(zhǔn)顯示層上進行軟件疊加,或者使用 GPU 來疊加。
參考:
Android顯示系統(tǒng)SurfaceFlinger詳解
由于跟平臺相關(guān)的實現(xiàn)具有私密性,這里不再繼續(xù)分析,更多關(guān)于Android鼠標(biāo)光標(biāo)圖形合成的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android UI使用HorizontalListView實現(xiàn)水平滑動
這篇文章主要為大家詳細介紹了Android UI使用HorizontalListView實現(xiàn)水平滑動效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01Android camera實時預(yù)覽 實時處理,人臉識別示例
本篇文章主要介紹了Android camera實時預(yù)覽 實時處理,面部認證示例,具有一定的參考價值,有興趣的可以了解一下。2017-01-01Android水波紋載入控件CircleWaterWaveView使用詳解
這篇文章主要為大家詳細介紹了Android水波紋載入控件CircleWaterWaveView使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-01-01Android編程之ICS式下拉菜單PopupWindow實現(xiàn)方法詳解(附源碼下載)
這篇文章主要介紹了Android編程之ICS式下拉菜單PopupWindow實現(xiàn)方法,結(jié)合實例詳細分析了ICS式下拉菜單的實現(xiàn)原理與相關(guān)技巧,并附帶源碼供讀者下載,需要的朋友可以參考下2015-12-12Flutter使用JsBridge方式處理Webview與H5通信的方法
這篇文章主要介紹了Flutter使用JsBridge方式處理Webview與H5通信的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04